From f188c290e57c5ef5abc2cc5e6a39a87cfe6c57ba Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Fri, 9 Aug 2019 04:25:22 -0400 Subject: [PATCH 001/379] Start on new menus --- src/d_main.c | 10 +- src/dehacked.c | 6 +- src/f_finale.h | 4 +- src/f_wipe.c | 4 +- src/g_game.c | 5 +- src/g_state.h | 2 +- src/hu_stuff.c | 12 + src/hu_stuff.h | 8 +- src/m_cheat.c | 14 - src/m_menu.c | 2596 +++++++++++------------------------------------- src/m_menu.h | 29 +- src/v_video.c | 119 ++- src/v_video.h | 10 +- 13 files changed, 762 insertions(+), 2057 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index a6bd48e0e..89c082531 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -307,7 +307,7 @@ static void D_Display(void) // set for all later wipedefindex = gamestate; // wipe_xxx_toblack if (gamestate == GS_TITLESCREEN && wipegamestate != GS_INTRO) - wipedefindex = wipe_timeattack_toblack; + wipedefindex = wipe_titlescreen_toblack; else if (gamestate == GS_INTERMISSION) { if (intertype == int_spec) // Special Stage @@ -325,7 +325,7 @@ static void D_Display(void) F_WipeStartScreen(); V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); F_WipeEndScreen(); - F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK, "FADEMAP0", false); + F_RunWipe(wipedefs[wipedefindex], gamestate != GS_MENU, "FADEMAP0", false); } if (gamestate != GS_LEVEL && rendermode != render_none) @@ -360,7 +360,7 @@ static void D_Display(void) HU_Drawer(); break; - case GS_TIMEATTACK: + case GS_MENU: break; case GS_INTRO: @@ -538,7 +538,7 @@ static void D_Display(void) vid.recalc = 0; // FIXME: draw either console or menu, not the two - if (gamestate != GS_TIMEATTACK) + if (gamestate != GS_MENU) CON_Drawer(); M_Drawer(); // menu is drawn even on top of everything @@ -556,7 +556,7 @@ static void D_Display(void) if (rendermode != render_none) { F_WipeEndScreen(); - F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK, "FADEMAP0", true); + F_RunWipe(wipedefs[wipedefindex], gamestate != GS_MENU, "FADEMAP0", true); } } diff --git a/src/dehacked.c b/src/dehacked.c index be45f3f0f..5aa8f1b20 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -3296,13 +3296,13 @@ static void readwipes(MYFILE *f) else if (fastcmp(pword, "FINAL")) wipeoffset = wipe_titlescreen_final; } - else if (fastncmp(word, "TIMEATTACK_", 11)) + else if (fastncmp(word, "MENU_", 11)) { pword = word + 11; if (fastcmp(pword, "TOBLACK")) - wipeoffset = wipe_timeattack_toblack; + wipeoffset = wipe_menu_toblack; else if (fastcmp(pword, "FINAL")) - wipeoffset = wipe_timeattack_final; + wipeoffset = wipe_menu_final; } else if (fastncmp(word, "CREDITS_", 8)) { diff --git a/src/f_finale.h b/src/f_finale.h index a6fccf888..61c0d7d48 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -85,7 +85,7 @@ enum wipe_voting_toblack, wipe_continuing_toblack, wipe_titlescreen_toblack, - wipe_timeattack_toblack, + wipe_menu_toblack, wipe_credits_toblack, wipe_evaluation_toblack, wipe_gameend_toblack, @@ -102,7 +102,7 @@ enum wipe_voting_final, wipe_continuing_final, wipe_titlescreen_final, - wipe_timeattack_final, + wipe_menu_final, wipe_credits_final, wipe_evaluation_final, wipe_gameend_final, diff --git a/src/f_wipe.c b/src/f_wipe.c index a1107e16f..1ea2ec2fa 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -55,7 +55,7 @@ UINT8 wipedefs[NUMWIPEDEFS] = { 0, // wipe_voting_toblack, 0, // wipe_continuing_toblack 0, // wipe_titlescreen_toblack - 0, // wipe_timeattack_toblack + 1, // wipe_menu_toblack 99, // wipe_credits_toblack 0, // wipe_evaluation_toblack 0, // wipe_gameend_toblack @@ -71,7 +71,7 @@ UINT8 wipedefs[NUMWIPEDEFS] = { 0, // wipe_voting_final 0, // wipe_continuing_final 0, // wipe_titlescreen_final - 0, // wipe_timeattack_final + 1, // wipe_menu_final 99, // wipe_credits_final 0, // wipe_evaluation_final 0, // wipe_gameend_final diff --git a/src/g_game.c b/src/g_game.c index e406e29d1..6a509e28b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2412,7 +2412,7 @@ void G_Ticker(boolean run) HU_Ticker(); break; - case GS_TIMEATTACK: + case GS_MENU: break; case GS_INTRO: @@ -3442,8 +3442,7 @@ INT16 G_SometimesGetDifferentGametype(void) // UINT8 G_GetGametypeColor(INT16 gt) { - if (modeattacking // == ATTACKING_RECORD - || gamestate == GS_TIMEATTACK) + if (modeattacking) // == ATTACKING_RECORD return orangemap[0]; if (gt == GT_MATCH) return redmap[0]; diff --git a/src/g_state.h b/src/g_state.h index f9f1babd3..e6aac9c38 100644 --- a/src/g_state.h +++ b/src/g_state.h @@ -27,7 +27,7 @@ typedef enum GS_CONTINUING, // continue screen GS_TITLESCREEN, // title screen - GS_TIMEATTACK, // time attack menu + GS_MENU, // SRB2Kart: menu-only (previously was GS_TIMEATTACK) GS_CREDITS, // credit sequence GS_EVALUATION, // Evaluation at the end of a game. GS_GAMEEND, // game end sequence diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 38324dbd9..cf3cc5872 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -66,6 +66,7 @@ //------------------------------------------- patch_t *hu_font[HU_FONTSIZE]; patch_t *kart_font[KART_FONTSIZE]; // SRB2kart +patch_t *gamemode_font[AZ_FONTSIZE]; patch_t *tny_font[HU_FONTSIZE]; patch_t *tallnum[10]; // 0-9 patch_t *nightsnum[10]; // 0-9 @@ -237,6 +238,17 @@ void HU_LoadGraphics(void) else kart_font[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); } + + j = AZ_FONTSTART; // All A to Z font sets + for (i = 0; i < AZ_FONTSIZE; i++, j++) + { + // Gamemode font + sprintf(buffer, "GAMEM%.3d", j); + if (W_CheckNumForName(buffer) == LUMPERROR) + gamemode_font[i] = NULL; + else + gamemode_font[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); + } // j = LT_FONTSTART; diff --git a/src/hu_stuff.h b/src/hu_stuff.h index be6798a82..2c5fcbb29 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -31,6 +31,11 @@ #define KART_FONTEND 'Z' #define KART_FONTSIZE (KART_FONTEND - KART_FONTSTART + 1) + +#define AZ_FONTSTART 'A' // the first font character +#define AZ_FONTEND 'Z' + +#define AZ_FONTSIZE (AZ_FONTEND - AZ_FONTSTART + 1) // // Level title font @@ -78,7 +83,8 @@ void HU_AddChatText(const char *text, boolean playsound); // set true when entering a chat message extern boolean chat_on; -extern patch_t *hu_font[HU_FONTSIZE], *kart_font[KART_FONTSIZE], *tny_font[HU_FONTSIZE]; // SRB2kart +extern patch_t *hu_font[HU_FONTSIZE], *tny_font[HU_FONTSIZE]; +extern patch_t *kart_font[KART_FONTSIZE], *gamemode_font[AZ_FONTSIZE]; // SRB2kart extern patch_t *tallnum[10]; extern patch_t *pingnum[10]; extern patch_t *pinggfx[5]; diff --git a/src/m_cheat.c b/src/m_cheat.c index e7e877ada..e12dfa590 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -58,20 +58,6 @@ typedef struct // ========================================================================== // Cheat responders -/*static UINT8 cheatf_ultimate(void) -{ - if (menuactive && (currentMenu != &MainDef && currentMenu != &SP_LoadDef)) - return 0; // Only on the main menu, or the save select! - - S_StartSound(0, sfx_itemup); - ultimate_selectable = (!ultimate_selectable); - - // If on the save select, move to what is now Ultimate Mode! - if (currentMenu == &SP_LoadDef) - M_ForceSaveSlotSelected(NOSAVESLOT); - return 1; -}*/ - static UINT8 cheatf_warp(void) { UINT8 i; diff --git a/src/m_menu.c b/src/m_menu.c index 166b115d6..342bbf4d5 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -152,6 +152,8 @@ 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 boolean menuwipe = false; // finish wipes between screens + static UINT8 setupcontrolplayer; static INT32 (*setupcontrols)[2]; // pointer to the gamecontrols of the player being edited @@ -185,19 +187,15 @@ 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); +menu_t PY_MainDef; + +// Extra + +static void M_Statistics(INT32 choice); static char *M_GetConditionString(condition_t cond); -menu_t SR_MainDef, SR_UnlockChecklistDef; + +menu_t EX_MainDef, EX_UnlockChecklistDef; +menu_t EX_LevelStatsDef; // Misc. Main Menu #if 0 // Bring this back when we have actual single-player @@ -215,29 +213,22 @@ 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_Credits(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 @@ -304,8 +295,8 @@ static patch_t *addonsp[NUM_EXT+5]; #define numaddonsshown 4 // Replay hut -menu_t MISC_ReplayHutDef; -menu_t MISC_ReplayOptionsDef; +menu_t EX_ReplayHutDef; +menu_t OP_ReplayOptionsDef; static void M_HandleReplayHutList(INT32 choice); static void M_DrawReplayHut(void); static void M_DrawReplayStartMenu(void); @@ -324,22 +315,17 @@ static void M_PlaybackQuit(INT32 choice); static UINT8 playback_enterheld = 0; // horrid hack to prevent holding the button from being extremely fucked // Drawing functions +static void M_DrawKartGamemodeMenu(void); 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_DrawSoundOptions(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); @@ -361,7 +347,6 @@ static void M_DrawSetupMultiPlayerMenu(void); #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); @@ -472,97 +457,36 @@ static consvar_t cv_dummystaff = {"dummystaff", "0", CV_HIDEN|CV_CALL, dummystaf // --------- 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}, + {IT_SUBMENU|IT_STRING, NULL, "Play", &PY_MainDef, 48}, + {IT_SUBMENU|IT_STRING, NULL, "Extra", &EX_MainDef, 80}, + {IT_CALL|IT_STRING, NULL, "Options", M_Options, 112}, + {IT_CALL|IT_STRING, NULL, "Quit", M_QuitSRB2, 160}, }; typedef enum { - secrets = 0, - singleplr, - multiplr, + play = 0, + extra, options, - addons, - quitdoom + quitkart } main_e; -static menuitem_t MISC_AddonsMenu[] = +static menuitem_t PlayMenu[] = { - {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleAddons, 0}, // dummy menuitem for the control func + {IT_CALL|IT_STRING, NULL, "Local Play", M_TimeAttack, 64}, + {IT_SUBMENU|IT_STRING, NULL, "Online", &MP_MainDef, 96}, + + {IT_SUBMENU|IT_STRING, NULL, "Back", &MainDef, 160}, }; -static menuitem_t MISC_ReplayHutMenu[] = -{ - {IT_KEYHANDLER|IT_NOTHING, NULL, "", M_HandleReplayHutList, 0}, // Dummy menuitem for the replay list - {IT_NOTHING, NULL, "", NULL, 0}, // Dummy for handling wrapping to the top of the menu.. -}; - -static menuitem_t MISC_ReplayStartMenu[] = -{ - {IT_CALL |IT_STRING, NULL, "Load Addons and Watch", M_HutStartReplay, 0}, - {IT_CALL |IT_STRING, NULL, "Watch Without Addons", M_HutStartReplay, 10}, - {IT_CALL |IT_STRING, NULL, "Watch Replay", M_HutStartReplay, 10}, - {IT_SUBMENU |IT_STRING, NULL, "Back", &MISC_ReplayHutDef, 30}, -}; - -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}, -}; - -static menuitem_t PlaybackMenu[] = -{ - {IT_CALL | IT_STRING, "M_PHIDE", "Hide Menu", M_SelectableClearMenus, 0}, - - {IT_CALL | IT_STRING, "M_PREW", "Rewind", M_PlaybackRewind, 20}, - {IT_CALL | IT_STRING, "M_PPAUSE", "Pause", M_PlaybackPause, 36}, - {IT_CALL | IT_STRING, "M_PFFWD", "Fast-Forward", M_PlaybackFastForward, 52}, - {IT_CALL | IT_STRING, "M_PSTEPB", "Backup Frame", M_PlaybackRewind, 20}, - {IT_CALL | IT_STRING, "M_PRESUM", "Resume", M_PlaybackPause, 36}, - {IT_CALL | IT_STRING, "M_PFADV", "Advance Frame", M_PlaybackAdvance, 52}, - - {IT_ARROWS | IT_STRING, "M_PVIEWS", "View Count", M_PlaybackSetViews, 72}, - {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint", M_PlaybackAdjustView, 88}, - {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint 2", M_PlaybackAdjustView, 104}, - {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint 3", M_PlaybackAdjustView, 120}, - {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint 4", M_PlaybackAdjustView, 136}, - - //{IT_CALL | IT_STRING, "M_POPTS", "More Options...", M_ReplayHut, 156}, - //{IT_CALL | IT_STRING, "M_PEXIT", "Stop Playback", M_PlaybackQuit, 172}, - {IT_CALL | IT_STRING, "M_PEXIT", "Stop Playback", M_PlaybackQuit, 156}, -}; -typedef enum -{ - playback_hide, - playback_rewind, - playback_pause, - playback_fastforward, - playback_backframe, - playback_resume, - playback_advanceframe, - playback_viewcount, - playback_view1, - playback_view2, - playback_view3, - playback_view4, - //playback_moreoptions, - playback_quit -} playback_e; - // --------------------------------- // 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}, + {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 @@ -628,11 +552,6 @@ typedef enum // --------------------- 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}, @@ -643,10 +562,6 @@ static menuitem_t SPauseMenu[] = typedef enum { - spause_pandora = 0, - spause_hints, - //spause_levelselect, - spause_continue, spause_retry, spause_options, @@ -654,6 +569,160 @@ typedef enum spause_quit } spause_e; +// Playback +static menuitem_t PlaybackMenu[] = +{ + {IT_CALL | IT_STRING, "M_PHIDE", "Hide Menu", M_SelectableClearMenus, 0}, + + {IT_CALL | IT_STRING, "M_PREW", "Rewind", M_PlaybackRewind, 20}, + {IT_CALL | IT_STRING, "M_PPAUSE", "Pause", M_PlaybackPause, 36}, + {IT_CALL | IT_STRING, "M_PFFWD", "Fast-Forward", M_PlaybackFastForward, 52}, + {IT_CALL | IT_STRING, "M_PSTEPB", "Backup Frame", M_PlaybackRewind, 20}, + {IT_CALL | IT_STRING, "M_PRESUM", "Resume", M_PlaybackPause, 36}, + {IT_CALL | IT_STRING, "M_PFADV", "Advance Frame", M_PlaybackAdvance, 52}, + + {IT_ARROWS | IT_STRING, "M_PVIEWS", "View Count", M_PlaybackSetViews, 72}, + {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint", M_PlaybackAdjustView, 88}, + {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint 2", M_PlaybackAdjustView, 104}, + {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint 3", M_PlaybackAdjustView, 120}, + {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint 4", M_PlaybackAdjustView, 136}, + + //{IT_CALL | IT_STRING, "M_POPTS", "More Options...", M_ReplayHut, 156}, + //{IT_CALL | IT_STRING, "M_PEXIT", "Stop Playback", M_PlaybackQuit, 172}, + {IT_CALL | IT_STRING, "M_PEXIT", "Stop Playback", M_PlaybackQuit, 156}, +}; + +typedef enum +{ + playback_hide, + playback_rewind, + playback_pause, + playback_fastforward, + playback_backframe, + playback_resume, + playback_advanceframe, + playback_viewcount, + playback_view1, + playback_view2, + playback_view3, + playback_view4, + //playback_moreoptions, + playback_quit +} playback_e; + +// PLAY + +// 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_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_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} +}; + +// EXTRAS + +static menuitem_t EX_MainMenu[] = +{ + {IT_STRING|IT_SUBMENU, NULL, "Unlocks", &EX_UnlockChecklistDef, 32}, + {IT_STRING|IT_CALL|IT_CALL_NOTMODIFIED, NULL, "Stats", M_Statistics, 64}, + {IT_STRING|IT_CALL, NULL, "Addons", M_Addons, 96}, + {IT_STRING|IT_CALL, NULL, "Replays", M_ReplayHut, 128}, + {IT_STRING|IT_SUBMENU, NULL, "Back", &MainDef, 160}, +}; + +static menuitem_t EX_UnlockChecklistMenu[] = +{ + {IT_SUBMENU|IT_STRING, NULL, "Back", &EX_MainDef, 200}, +}; + +static menuitem_t EX_LevelStatsMenu[] = +{ + {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelStats, '\0'}, // dummy menuitem for the control func +}; + +static menuitem_t EX_AddonsMenu[] = +{ + {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleAddons, 0}, // dummy menuitem for the control func +}; + +static menuitem_t EX_ReplayHutMenu[] = +{ + {IT_KEYHANDLER|IT_NOTHING, NULL, "", M_HandleReplayHutList, 0}, // Dummy menuitem for the replay list + {IT_NOTHING, NULL, "", NULL, 0}, // Dummy for handling wrapping to the top of the menu.. +}; + +static menuitem_t EX_ReplayStartMenu[] = +{ + {IT_CALL |IT_STRING, NULL, "Load Addons and Watch", M_HutStartReplay, 0}, + {IT_CALL |IT_STRING, NULL, "Watch Without Addons", M_HutStartReplay, 10}, + {IT_CALL |IT_STRING, NULL, "Watch Replay", M_HutStartReplay, 10}, + {IT_SUBMENU |IT_STRING, NULL, "Back", &EX_ReplayHutDef, 30}, +}; + +// OPTIONS + +static menuitem_t OP_ReplayOptionsMenu[] = +{ + {IT_CVAR|IT_STRING, NULL, "Record Replays", &cv_recordmultiplayerdemos, 0}, + {IT_CVAR|IT_STRING, NULL, "Sync Check Interval", &cv_netdemosyncquality, 10}, +}; + // ----------------- // Misc menu options // ----------------- @@ -703,244 +772,12 @@ static menuitem_t MISC_HelpMenu[] = {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, 100}, - {IT_CALL|IT_STRING|IT_CALL_NOTMODIFIED, NULL, "Statistics", M_Statistics, 108}, - {IT_CALL|IT_STRING, NULL, "Replay Hut", M_ReplayHut, 116}, - {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. @@ -1363,7 +1200,7 @@ static menuitem_t OP_DataOptionsMenu[] = { {IT_STRING | IT_CALL, NULL, "Screenshot Options...", M_ScreenshotOptions, 10}, {IT_STRING | IT_CALL, NULL, "Add-on Options...", M_AddonsOptions, 20}, - {IT_STRING | IT_SUBMENU, NULL, "Replay Options...", &MISC_ReplayOptionsDef, 30}, + {IT_STRING | IT_SUBMENU, NULL, "Replay Options...", &OP_ReplayOptionsDef, 30}, {IT_STRING | IT_SUBMENU, NULL, "Erase Data...", &OP_EraseDataDef, 50}, }; @@ -1602,55 +1439,8 @@ static menuitem_t OP_MonitorToggleMenu[] = // ========================================================================== // Main Menu and related -menu_t MainDef = CENTERMENUSTYLE(NULL, MainMenu, NULL, 72); - -menu_t MISC_AddonsDef = -{ - NULL, - sizeof (MISC_AddonsMenu)/sizeof (menuitem_t), - &OP_DataOptionsDef, - 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, - 0, - M_QuitReplayHut -}; - -menu_t MISC_ReplayOptionsDef = -{ - "M_REPOPT", - sizeof (MISC_ReplayOptionsMenu)/sizeof (menuitem_t), - &OP_DataOptionsDef, - MISC_ReplayOptionsMenu, - M_DrawGenericMenu, - 27, 40, - 0, - NULL -}; - -menu_t MISC_ReplayStartDef = -{ - NULL, - sizeof (MISC_ReplayStartMenu)/sizeof (menuitem_t), - &MISC_ReplayHutDef, - MISC_ReplayStartMenu, - M_DrawReplayStartMenu, - 30, 90, - 0, - NULL -}; +menu_t MainDef = KARTGAMEMODEMENU(NULL, MainMenu, NULL); +menu_t PY_MainDef = KARTGAMEMODEMENU(NULL, PlayMenu, &MainDef); menu_t PlaybackMenuDef = { NULL, @@ -1668,257 +1458,16 @@ menu_t MAPauseDef = PAUSEMENUSTYLE(MAPauseMenu, 40, 72); menu_t SPauseDef = PAUSEMENUSTYLE(SPauseMenu, 40, 72); menu_t MPauseDef = PAUSEMENUSTYLE(MPauseMenu, 40, 72); -// Misc Main Menu +// MISC menus + 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. -// +// PLAY MENU -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 = CENTERMENUSTYLE(NULL, SR_MainMenu, &MainDef, 72); - -//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", @@ -1975,6 +1524,120 @@ menu_t MP_PlayerSetupDef = M_QuitMultiPlayerMenu }; + +// TIME ATTACK + +static menu_t SP_TimeAttackDef = +{ + "M_ATTACK", + sizeof (SP_TimeAttackMenu)/sizeof (menuitem_t), + &PY_MainDef, + SP_TimeAttackMenu, + M_DrawTimeAttackMenu, + 34, 40, + 0, + M_QuitTimeAttackMenu +}; + +static menu_t SP_ReplayDef = +{ + NULL, + sizeof(SP_ReplayMenu)/sizeof(menuitem_t), + &EX_MainDef, + 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 +}; + +// EXTRAS MENU +menu_t EX_MainDef = KARTGAMEMODEMENU(NULL, EX_MainMenu, &MainDef); + +menu_t EX_UnlockChecklistDef = +{ + NULL, + 1, + &EX_MainDef, + EX_UnlockChecklistMenu, + M_DrawChecklist, + 280, 185, + 0, + NULL +}; + +menu_t EX_LevelStatsDef = +{ + NULL, + 1, + &EX_MainDef, + EX_LevelStatsMenu, + M_DrawLevelStats, + 280, 185, + 0, + NULL +}; + +menu_t EX_AddonsDef = +{ + NULL, + sizeof (EX_AddonsMenu)/sizeof (menuitem_t), + &EX_MainDef, + EX_AddonsMenu, + M_DrawAddons, + 50, 28, + 0, + NULL +}; + +menu_t EX_ReplayHutDef = +{ + NULL, + sizeof (EX_ReplayHutMenu)/sizeof (menuitem_t), + &EX_MainDef, + EX_ReplayHutMenu, + M_DrawReplayHut, + 30, 80, + 0, + M_QuitReplayHut +}; + +menu_t EX_ReplayStartDef = +{ + NULL, + sizeof (EX_ReplayStartMenu)/sizeof (menuitem_t), + &EX_ReplayHutDef, + EX_ReplayStartMenu, + M_DrawReplayStartMenu, + 30, 90, + 0, + NULL +}; + // Options menu_t OP_MainDef = { @@ -2036,7 +1699,7 @@ menu_t OP_SoundOptionsDef = sizeof (OP_SoundOptionsMenu)/sizeof (menuitem_t), &OP_MainDef, OP_SoundOptionsMenu, - M_DrawSkyRoom, + M_DrawSoundOptions, 30, 30, 0, NULL @@ -2110,6 +1773,91 @@ menu_t OP_ScreenshotOptionsDef = DEFAULTMENUSTYLE("M_SCSHOT", OP_ScreenshotOptio menu_t OP_AddonsOptionsDef = DEFAULTMENUSTYLE("M_ADDONS", OP_AddonsOptionsMenu, &OP_DataOptionsDef, 30, 30); menu_t OP_EraseDataDef = DEFAULTMENUSTYLE("M_DATA", OP_EraseDataMenu, &OP_DataOptionsDef, 30, 30); +menu_t OP_ReplayOptionsDef = +{ + "M_REPOPT", + sizeof (OP_ReplayOptionsMenu)/sizeof (menuitem_t), + &OP_DataOptionsDef, + OP_ReplayOptionsMenu, + M_DrawGenericMenu, + 27, 40, + 0, + NULL +}; + +// +// 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 + { + 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; +} + // ========================================================================== // CVAR ONCHANGE EVENTS GO HERE // ========================================================================== @@ -2738,7 +2486,7 @@ boolean M_Responder(event_t *ev) if (currentMenu->menuitems[itemOn].status == IT_MSGHANDLER) { - if (currentMenu->menuitems[itemOn].alphaKey != MM_EVENTHANDLER) + if (currentMenu->menuitems[itemOn].mvar1 != MM_EVENTHANDLER) { if (ch == ' ' || ch == 'n' || ch == 'y' || ch == KEY_ESCAPE || ch == KEY_ENTER) { @@ -2898,14 +2646,7 @@ boolean M_Responder(event_t *ev) 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); + M_SetupNextMenu(currentMenu->prevMenu); } else M_ClearMenus(true); @@ -2916,7 +2657,7 @@ boolean M_Responder(event_t *ev) if ((currentMenu->menuitems[itemOn].status) == IT_CONTROL) { // detach any keys associated with the game control - G_ClearControlKeys(setupcontrols, currentMenu->menuitems[itemOn].alphaKey); + G_ClearControlKeys(setupcontrols, currentMenu->menuitems[itemOn].mvar1); S_StartSound(NULL, sfx_shldls); return true; } @@ -2961,11 +2702,18 @@ void M_Drawer(void) if (currentMenu == &MessageDef) menuactive = true; + if (menuwipe) + F_WipeStartScreen(); + if (menuactive) { - // now that's more readable with a faded background (yeah like Quake...) - if (!WipeInAction && currentMenu != &PlaybackMenuDef) // Replay playback has its own background - V_DrawFadeScreen(0xFF00, 16); + if (gamestate == GS_MENU) // draw BG + { + V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("WEIRDRES", PU_CACHE), NULL); + V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUBG", PU_CACHE), NULL); + } + else if (!WipeInAction && currentMenu != &PlaybackMenuDef) // Replay playback has its own background + V_DrawFadeScreen(0xFF00, 16); // now that's more readable with a faded background (yeah like Quake...) if (currentMenu->drawroutine) { @@ -2994,6 +2742,13 @@ void M_Drawer(void) } } + if (menuwipe) + { + F_WipeEndScreen(); + F_RunWipe(wipedefs[wipe_menu_final], false, "FADEMAP0", true); + menuwipe = false; + } + // focus lost notification goes on top of everything, even the former everything if (window_notinfocus && cv_showfocuslost.value) { @@ -3017,6 +2772,17 @@ void M_StartControlPanel(void) return; } + if (gamestate == GS_TITLESCREEN) // Set up menu state + { + G_SetGamestate(GS_MENU); + + gameaction = ga_nothing; + paused = false; + CON_ToggleOff(); + + S_ChangeMusicInternal("menu", true); + } + menuactive = true; if (demo.playback) @@ -3025,11 +2791,8 @@ void M_StartControlPanel(void) } else if (!Playing()) { - // Secret menu! - //MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED); - currentMenu = &MainDef; - itemOn = singleplr; + itemOn = 0; } else if (modeattacking) { @@ -3038,17 +2801,12 @@ void M_StartControlPanel(void) } 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); + if (gamestate != GS_LEVEL) // intermission, so gray out stuff. 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; @@ -3064,19 +2822,6 @@ void M_StartControlPanel(void) 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; } @@ -3098,11 +2843,11 @@ void M_StartControlPanel(void) 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; + MPauseMenu[mpause_switchteam].mvar1 = 48; + MPauseMenu[mpause_switchspectate].mvar1 = 48; + MPauseMenu[mpause_options].mvar1 = 64; + MPauseMenu[mpause_title].mvar1 = 80; + MPauseMenu[mpause_quit].mvar1 = 88; Dummymenuplayer_OnChange(); if ((server || IsPlayerAdmin(consoleplayer))) @@ -3123,18 +2868,18 @@ void M_StartControlPanel(void) 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; + MPauseMenu[mpause_switchteam].mvar1 += ((splitscreen+1) * 8); + MPauseMenu[mpause_options].mvar1 += 8; + MPauseMenu[mpause_title].mvar1 += 8; + MPauseMenu[mpause_quit].mvar1 += 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; + MPauseMenu[mpause_switchspectate].mvar1 += ((splitscreen+1) * 8); + MPauseMenu[mpause_options].mvar1 += 8; + MPauseMenu[mpause_title].mvar1 += 8; + MPauseMenu[mpause_quit].mvar1 += 8; } } @@ -3142,16 +2887,16 @@ void M_StartControlPanel(void) { MPauseMenu[mpause_psetupsplit3].status = IT_STRING | IT_CALL; - MPauseMenu[mpause_options].alphaKey += 8; - MPauseMenu[mpause_title].alphaKey += 8; - MPauseMenu[mpause_quit].alphaKey += 8; + MPauseMenu[mpause_options].mvar1 += 8; + MPauseMenu[mpause_title].mvar1 += 8; + MPauseMenu[mpause_quit].mvar1 += 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; + MPauseMenu[mpause_options].mvar1 += 8; + MPauseMenu[mpause_title].mvar1 += 8; + MPauseMenu[mpause_quit].mvar1 += 8; } } } @@ -3203,6 +2948,10 @@ void M_ClearMenus(boolean callexitmenufunc) if (currentMenu == &MessageDef) // Oh sod off! currentMenu = &MainDef; // Not like it matters + + if (gamestate == GS_MENU) // Back to title screen + D_StartTitle(); + menuactive = false; } @@ -3213,12 +2962,22 @@ void M_SetupNextMenu(menu_t *menudef) { INT16 i; + if (gamestate == GS_MENU) + { + menuwipe = true; + F_WipeStartScreen(); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeEndScreen(); + F_RunWipe(wipedefs[wipe_menu_toblack], false, "FADEMAP0", false); + } + 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; @@ -3331,7 +3090,7 @@ void M_Init(void) 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; + PlayerMenu[i].mvar1 = 0; } #ifdef HWRENDER @@ -3360,7 +3119,7 @@ void M_InitCharacterTables(void) PlayerMenu[i].status = (i < 4 ? IT_CALL : IT_DISABLED); PlayerMenu[i].patch = PlayerMenu[i].text = NULL; PlayerMenu[i].itemaction = M_ChoosePlayer; - PlayerMenu[i].alphaKey = 0; + PlayerMenu[i].mvar1 = 0; } // Setup description table @@ -3665,6 +3424,27 @@ static void M_DrawMenuTitle(void) } } +static void M_DrawKartGamemodeMenu(void) +{ + INT32 i; + + V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL); + V_DrawCenteredThinString(BASEVIDWIDTH/2, 12, V_ALLOWLOWERCASE|V_6WIDTHSPACE, "Configure your controls, settings, and preferences."); + + V_DrawFixedPatch(172<numitems; i++) + { + switch (currentMenu->menuitems[i].status & IT_DISPLAY) + { + case IT_STRING: + V_DrawRightAlignedGamemodeString(170, currentMenu->menuitems[i].mvar1, 0, currentMenu->menuitems[i].text, + (i == itemOn) ? SKINCOLOR_PLAGUE : SKINCOLOR_PIGEON); + break; + } + } +} + static void M_DrawGenericMenu(void) { INT32 x, y, w, i, cursory = 0; @@ -3700,7 +3480,7 @@ static void M_DrawGenericMenu(void) /* FALLTHRU */ case IT_NOTHING: case IT_DYBIGSPACE: - y = currentMenu->y+currentMenu->menuitems[i].alphaKey;//+= LINEHEIGHT; + y = currentMenu->y+currentMenu->menuitems[i].mvar1;//+= LINEHEIGHT; break; case IT_BIGSLIDER: M_DrawThermo(x, y, (consvar_t *)currentMenu->menuitems[i].itemaction); @@ -3708,8 +3488,8 @@ static void M_DrawGenericMenu(void) break; case IT_STRING: case IT_WHITESTRING: - if (currentMenu->menuitems[i].alphaKey) - y = currentMenu->y+currentMenu->menuitems[i].alphaKey; + if (currentMenu->menuitems[i].mvar1) + y = currentMenu->y+currentMenu->menuitems[i].mvar1; if (i == itemOn) cursory = y; @@ -3791,23 +3571,23 @@ static void M_DrawGenericMenu(void) y += LINEHEIGHT; break; case IT_TRANSTEXT: - if (currentMenu->menuitems[i].alphaKey) - y = currentMenu->y+currentMenu->menuitems[i].alphaKey; + if (currentMenu->menuitems[i].mvar1) + y = currentMenu->y+currentMenu->menuitems[i].mvar1; /* 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; + if (currentMenu->menuitems[i].mvar1) + y = currentMenu->y+currentMenu->menuitems[i].mvar1; 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; + if (currentMenu->menuitems[i].mvar1) + y = currentMenu->y+currentMenu->menuitems[i].mvar1; V_DrawString(x-16, y, highlightflags, currentMenu->menuitems[i].text); y += SMALLLINEHEIGHT; @@ -3830,12 +3610,6 @@ static void M_DrawGenericMenu(void) } } -static void M_DrawGenericBackgroundMenu(void) -{ - V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE)); - M_DrawGenericMenu(); -} - static void M_DrawPauseMenu(void) { #if 0 @@ -3992,124 +3766,6 @@ static void M_DrawPauseMenu(void) 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 // @@ -4184,7 +3840,7 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) { // Random map! if (mapnum == -1) - return (gamestate != GS_TIMEATTACK && !modeattacking); + return (currentMenu != &SP_TimeAttackDef && !modeattacking); // Does the map exist? if (!mapheaderinfo[mapnum]) @@ -4374,7 +4030,7 @@ void M_StartMessage(const char *string, void *routine, MessageDef.prevMenu = currentMenu; MessageDef.menuitems[0].text = message; - MessageDef.menuitems[0].alphaKey = (UINT8)itemtype; + MessageDef.menuitems[0].mvar1 = (UINT8)itemtype; if (!routine && itemtype != MM_NOTHING) itemtype = MM_NOTHING; switch (itemtype) { @@ -4436,8 +4092,8 @@ static void M_DrawMessageMenu(void) mlines = currentMenu->lastOn>>8; max = (INT16)((UINT8)(currentMenu->lastOn & 0xFF)*8); - // hack: draw RA background in RA menus - if (gamestate == GS_TIMEATTACK) + // hack: draw background in menus + if (gamestate == GS_MENU) V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE)); M_DrawTextBox(currentMenu->x, y - 8, (max+7)>>3, mlines); @@ -4520,7 +4176,7 @@ static void M_DrawImageDef(void) V_DrawSmallScaledPatch(0,0,0,patch); } - if (currentMenu->menuitems[itemOn].alphaKey) + if (currentMenu->menuitems[itemOn].mvar1) { 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 @@ -4653,8 +4309,8 @@ static void M_Addons(INT32 choice) 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); + EX_AddonsDef.prevMenu = currentMenu; + M_SetupNextMenu(&EX_AddonsDef); } #define width 4 @@ -4724,7 +4380,7 @@ static char *M_AddonsHeaderPath(void) } #define UNEXIST S_StartSound(NULL, sfx_s26d);\ - M_SetupNextMenu(MISC_AddonsDef.prevMenu);\ + M_SetupNextMenu(EX_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);\ @@ -5116,13 +4772,11 @@ static void M_HandleAddons(INT32 choice) 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 @@ -5176,6 +4830,7 @@ void M_ReplayHut(INT32 choice) snprintf(menupath, 1024, "%s"PATHSEP"replay"PATHSEP"online"PATHSEP, srb2home); menupathindex[(menudepthleft = menudepth-1)] = strlen(menupath); } + if (!preparefilemenu(false, true)) { M_StartMessage("No replays found.\n\n(Press a key)\n", NULL, MM_NOTHING); @@ -5183,19 +4838,15 @@ void M_ReplayHut(INT32 choice) } else if (!demo.inreplayhut) dir_on[menudepthleft] = 0; + demo.inreplayhut = true; replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; PrepReplayList(); - - menuactive = true; - M_SetupNextMenu(&MISC_ReplayHutDef); - G_SetGamestate(GS_TIMEATTACK); + M_SetupNextMenu(&EX_ReplayHutDef); demo.rewinding = false; - - S_ChangeMusicInternal("replst", true); } static void M_HandleReplayHutList(INT32 choice) @@ -5275,7 +4926,7 @@ static void M_HandleReplayHutList(INT32 choice) default: // We can't just use M_SetupNextMenu because that'll run ReplayDef's quitroutine and boot us back to the title screen! currentMenu->lastOn = itemOn; - currentMenu = &MISC_ReplayStartDef; + currentMenu = &EX_ReplayStartDef; replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; @@ -5283,20 +4934,20 @@ static void M_HandleReplayHutList(INT32 choice) { case DFILE_ERROR_CANNOTLOAD: // Only show "Watch Replay Without Addons" - MISC_ReplayStartMenu[0].status = IT_DISABLED; - MISC_ReplayStartMenu[1].status = IT_CALL|IT_STRING; - //MISC_ReplayStartMenu[1].alphaKey = 0; - MISC_ReplayStartMenu[2].status = IT_DISABLED; + EX_ReplayStartMenu[0].status = IT_DISABLED; + EX_ReplayStartMenu[1].status = IT_CALL|IT_STRING; + //EX_ReplayStartMenu[1].mvar1 = 0; + EX_ReplayStartMenu[2].status = IT_DISABLED; itemOn = 1; break; case DFILE_ERROR_NOTLOADED: case DFILE_ERROR_INCOMPLETEOUTOFORDER: // Show "Load Addons and Watch Replay" and "Watch Replay Without Addons" - MISC_ReplayStartMenu[0].status = IT_CALL|IT_STRING; - MISC_ReplayStartMenu[1].status = IT_CALL|IT_STRING; - //MISC_ReplayStartMenu[1].alphaKey = 10; - MISC_ReplayStartMenu[2].status = IT_DISABLED; + EX_ReplayStartMenu[0].status = IT_CALL|IT_STRING; + EX_ReplayStartMenu[1].status = IT_CALL|IT_STRING; + //EX_ReplayStartMenu[1].mvar1 = 10; + EX_ReplayStartMenu[2].status = IT_DISABLED; itemOn = 0; break; @@ -5304,10 +4955,10 @@ static void M_HandleReplayHutList(INT32 choice) case DFILE_ERROR_OUTOFORDER: default: // Show "Watch Replay" - MISC_ReplayStartMenu[0].status = IT_DISABLED; - MISC_ReplayStartMenu[1].status = IT_DISABLED; - MISC_ReplayStartMenu[2].status = IT_CALL|IT_STRING; - //MISC_ReplayStartMenu[2].alphaKey = 0; + EX_ReplayStartMenu[0].status = IT_DISABLED; + EX_ReplayStartMenu[1].status = IT_DISABLED; + EX_ReplayStartMenu[2].status = IT_CALL|IT_STRING; + //EX_ReplayStartMenu[2].mvar1 = 0; itemOn = 2; break; } @@ -5444,8 +5095,6 @@ static void M_DrawReplayHut(void) static UINT16 replayhutmenuy = 0; - V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE)); - if (cv_vhseffect.value) V_DrawVhsEffect(false); @@ -5469,8 +5118,8 @@ static void M_DrawReplayHut(void) { INT32 maxy; // Scroll menu items if needed - cursory = y + currentMenu->menuitems[replaylistitem].alphaKey + dir_on[menudepthleft]*10; - maxy = y + currentMenu->menuitems[replaylistitem].alphaKey + sizedirmenu*10; + cursory = y + currentMenu->menuitems[replaylistitem].mvar1 + dir_on[menudepthleft]*10; + maxy = y + currentMenu->menuitems[replaylistitem].mvar1 + sizedirmenu*10; if (cursory > maxy - 20) cursory = maxy - 20; @@ -5488,7 +5137,7 @@ static void M_DrawReplayHut(void) // Draw static menu items for (i = 0; i < replaylistitem; i++) { - INT32 localy = y + currentMenu->menuitems[i].alphaKey; + INT32 localy = y + currentMenu->menuitems[i].mvar1; if (localy < 65) continue; @@ -5502,7 +5151,7 @@ static void M_DrawReplayHut(void) V_DrawString(x, localy, V_SNAPTOTOP|V_SNAPTOLEFT|highlightflags, currentMenu->menuitems[i].text); } - y += currentMenu->menuitems[replaylistitem].alphaKey; + y += currentMenu->menuitems[replaylistitem].mvar1; for (i = 0; i < (INT16)sizedirmenu; i++) { @@ -5560,7 +5209,7 @@ static void M_DrawReplayHut(void) } // Draw scrollbar - y = sizedirmenu*10 + currentMenu->menuitems[replaylistitem].alphaKey + 30; + y = sizedirmenu*10 + currentMenu->menuitems[replaylistitem].mvar1 + 30; if (y > SCALEDVIEWHEIGHT-80) { V_DrawFill(BASEVIDWIDTH-4, 75, 4, SCALEDVIEWHEIGHT-80, V_SNAPTOTOP|V_SNAPTORIGHT|159); @@ -5586,7 +5235,7 @@ static void M_DrawReplayStartMenu(void) const char *warning; UINT8 i; - M_DrawGenericBackgroundMenu(); + M_DrawGenericMenu(); #define STARTY 62-(replayScrollTitle>>1) // Draw rankings beyond first @@ -5689,16 +5338,17 @@ static void M_DrawReplayStartMenu(void) 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; demo.inreplayhut = false; + if (currentMenu->prevMenu) + M_SetupNextMenu(currentMenu->prevMenu); + else + M_ClearMenus(true); + return true; } @@ -5747,9 +5397,9 @@ static void M_DrawPlaybackMenu(void) for (i = playback_viewcount; i <= playback_view4; i++) PlaybackMenu[i].status = IT_DISABLED; - //PlaybackMenu[playback_moreoptions].alphaKey = 72; - //PlaybackMenu[playback_quit].alphaKey = 88; - PlaybackMenu[playback_quit].alphaKey = 72; + //PlaybackMenu[playback_moreoptions].mvar1 = 72; + //PlaybackMenu[playback_quit].mvar1 = 88; + PlaybackMenu[playback_quit].mvar1 = 72; //currentMenu->x = BASEVIDWIDTH/2 - 52; currentMenu->x = BASEVIDWIDTH/2 - 44; @@ -5763,9 +5413,9 @@ static void M_DrawPlaybackMenu(void) for (i = splitscreen+1; i < 4; i++) PlaybackMenu[playback_view1+i].status = IT_DISABLED; - //PlaybackMenu[playback_moreoptions].alphaKey = 156; - //PlaybackMenu[playback_quit].alphaKey = 172; - PlaybackMenu[playback_quit].alphaKey = 156; + //PlaybackMenu[playback_moreoptions].mvar1 = 156; + //PlaybackMenu[playback_quit].mvar1 = 172; + PlaybackMenu[playback_quit].mvar1 = 156; //currentMenu->x = BASEVIDWIDTH/2 - 94; currentMenu->x = BASEVIDWIDTH/2 - 88; @@ -5804,13 +5454,13 @@ static void M_DrawPlaybackMenu(void) icon = W_CachePatchName("PLAYRANK", PU_CACHE); // temp if ((i == playback_fastforward && cv_playbackspeed.value > 1) || (i == playback_rewind && demo.rewinding)) - V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].alphaKey, currentMenu->y, V_SNAPTOTOP, icon, R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_JAWZ, GTC_MENUCACHE)); + V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].mvar1, currentMenu->y, V_SNAPTOTOP, icon, R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_JAWZ, GTC_MENUCACHE)); else - V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].alphaKey, currentMenu->y, V_SNAPTOTOP, icon, (i == itemOn) ? activemap : inactivemap); + V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].mvar1, currentMenu->y, V_SNAPTOTOP, icon, (i == itemOn) ? activemap : inactivemap); if (i == itemOn) { - V_DrawCharacter(currentMenu->x + currentMenu->menuitems[i].alphaKey + 4, currentMenu->y + 14, + V_DrawCharacter(currentMenu->x + currentMenu->menuitems[i].mvar1 + 4, currentMenu->y + 14, '\x1A' | V_SNAPTOTOP|highlightflags, false); V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y + 18, V_SNAPTOTOP|V_ALLOWLOWERCASE, currentMenu->menuitems[i].text); @@ -5946,34 +5596,11 @@ static void M_PlaybackQuit(INT32 choice) if (demo.inreplayhut) M_ReplayHut(choice); else if (modeattacking) - { M_EndModeAttackRun(); - S_ChangeMusicInternal("racent", true); - } else D_StartTitle(); } -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]; @@ -6130,69 +5757,6 @@ static void M_SelectableClearMenus(INT32 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 // ======== @@ -6308,57 +5872,7 @@ static void M_DrawChecklist(void) } #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) +static void M_DrawSoundOptions(void) { INT32 i, y = 0; INT32 lengthstring = 0; @@ -6368,17 +5882,17 @@ static void M_DrawSkyRoom(void) if (currentMenu == &OP_SoundOptionsDef) { V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, - currentMenu->y+currentMenu->menuitems[0].alphaKey, + currentMenu->y+currentMenu->menuitems[0].mvar1, (sound_disabled ? warningflags : highlightflags), (sound_disabled ? "OFF" : "ON")); V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, - currentMenu->y+currentMenu->menuitems[2].alphaKey, + currentMenu->y+currentMenu->menuitems[2].mvar1, (digital_disabled ? warningflags : highlightflags), (digital_disabled ? "OFF" : "ON")); /*V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, - currentMenu->y+currentMenu->menuitems[5].alphaKey, + currentMenu->y+currentMenu->menuitems[5].mvar1, (midi_disabled ? warningflags : highlightflags), (midi_disabled ? "OFF" : "ON"));*/ @@ -6394,7 +5908,7 @@ static void M_DrawSkyRoom(void) { if (currentMenu->menuitems[i].itemaction == M_HandleSoundTest) { - y = currentMenu->menuitems[i].alphaKey; + y = currentMenu->menuitems[i].mvar1; break; } } @@ -6413,9 +5927,9 @@ static void M_DrawSkyRoom(void) if (lengthstring) { - V_DrawCharacter(BASEVIDWIDTH - currentMenu->x - 10 - lengthstring - (skullAnimCounter/5), currentMenu->y+currentMenu->menuitems[itemOn].alphaKey, + V_DrawCharacter(BASEVIDWIDTH - currentMenu->x - 10 - lengthstring - (skullAnimCounter/5), currentMenu->y+currentMenu->menuitems[itemOn].mvar1, '\x1C' | highlightflags, false); // left arrow - V_DrawCharacter(BASEVIDWIDTH - currentMenu->x + 2 + (skullAnimCounter/5), currentMenu->y+currentMenu->menuitems[itemOn].alphaKey, + V_DrawCharacter(BASEVIDWIDTH - currentMenu->x + 2 + (skullAnimCounter/5), currentMenu->y+currentMenu->menuitems[itemOn].mvar1, '\x1D' | highlightflags, false); // right arrow } } @@ -6453,6 +5967,7 @@ static void M_HandleSoundTest(INT32 choice) default: break; } + if (exitmenu) { if (currentMenu->prevMenu) @@ -6462,109 +5977,6 @@ static void M_HandleSoundTest(INT32 choice) } } -// 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; @@ -6573,23 +5985,6 @@ static void M_Credits(INT32 choice) 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 // ================== @@ -6607,651 +6002,6 @@ static void M_SinglePlayerMenu(INT32 choice) } #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 // =============== @@ -7289,7 +6039,7 @@ static void M_Statistics(INT32 choice) if (statsMax < 0) statsMax = 0; - M_SetupNextMenu(&SP_LevelStatsDef); + M_SetupNextMenu(&EX_LevelStatsDef); } static void M_DrawStatsMaps(int location) @@ -7488,9 +6238,7 @@ 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)); + S_ChangeMusicInternal("menu", true); // Eww, but needed for when user hits escape during demo playback M_DrawMenuTitle(); if (currentMenu == &SP_TimeAttackDef) @@ -7514,7 +6262,7 @@ void M_DrawTimeAttackMenu(void) if (dispstatus != IT_STRING && dispstatus != IT_WHITESTRING) continue; - y = currentMenu->y+currentMenu->menuitems[i].alphaKey; + y = currentMenu->y+currentMenu->menuitems[i].mvar1; if (i == itemOn) cursory = y; @@ -7645,7 +6393,7 @@ void M_DrawTimeAttackMenu(void) for (i = 0; i < 4; ++i) { - y = currentMenu->y+SP_TimeAttackMenu[i].alphaKey; + y = currentMenu->y+SP_TimeAttackMenu[i].mvar1; 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) @@ -7689,16 +6437,12 @@ static void M_TimeAttack(INT32 choice) 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) @@ -7708,161 +6452,6 @@ static boolean M_QuitTimeAttackMenu(void) 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) { @@ -8104,21 +6693,29 @@ static void M_ModeAttackEndGame(INT32 choice) if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING) Command_ExitGame_f(); + G_SetGamestate(GS_MENU); + + gameaction = ga_nothing; + paused = false; + CON_ToggleOff(); + + S_ChangeMusicInternal("menu", true); + M_StartControlPanel(); + switch(modeattacking) { - default: - case ATTACKING_RECORD: - currentMenu = &SP_TimeAttackDef; - break; - /*case ATTACKING_NIGHTS: - currentMenu = &SP_NightsAttackDef; - break;*/ + 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); @@ -8271,14 +6868,14 @@ static void M_DrawConnectMenu(void) // Room name if (ms_RoomId < 0) - V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ConnectMenu[mp_connect_room].alphaKey, + V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ConnectMenu[mp_connect_room].mvar1, highlightflags, (itemOn == mp_connect_room) ? "" : ""); else - V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].alphaKey, + V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].mvar1, highlightflags, room_list[menuRoomIndex].name); #undef mp_server_room } @@ -8815,17 +7412,17 @@ static void M_DrawMPMainMenu(void) #if MAXPLAYERS != 16 Update the maxplayers label... #endif - V_DrawRightAlignedString(BASEVIDWIDTH-x, y+MP_MainMenu[4].alphaKey, + V_DrawRightAlignedString(BASEVIDWIDTH-x, y+MP_MainMenu[4].mvar1, ((itemOn == 4) ? highlightflags : 0), "(2-16 players)"); #endif - V_DrawRightAlignedString(BASEVIDWIDTH-x, y+MP_MainMenu[5].alphaKey, + V_DrawRightAlignedString(BASEVIDWIDTH-x, y+MP_MainMenu[5].mvar1, ((itemOn == 5) ? highlightflags : 0), "(2-4 players)" ); #ifndef NONET - y += MP_MainMenu[8].alphaKey; + y += MP_MainMenu[8].mvar1; V_DrawFill(x+5, y+4+5, /*16*8 + 6,*/ BASEVIDWIDTH - 2*(x+5), 8+6, 159); @@ -9591,7 +8188,7 @@ static void M_EraseDataResponse(INT32 ch) // Delete the data if (erasecontext == 2) { - // SRB2Kart: This actually needs to be done FIRST, so that you don't immediately regain playtime/matches secrets + // SRB2Kart: This actually needs to be done FIRST, so that you don't immediately regain playtime/matches extras totalplaytime = 0; matchesplayed = 0; F_StartIntro(); @@ -9612,7 +8209,7 @@ static void M_EraseData(INT32 choice) if (choice == 0) eschoice = M_GetText("Record Attack data"); else if (choice == 1) - eschoice = M_GetText("Secrets data"); + eschoice = M_GetText("Extras data"); else eschoice = M_GetText("ALL game data"); @@ -10098,8 +8695,8 @@ static void M_DrawControl(void) 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]; + keys[0] = setupcontrols[currentMenu->menuitems[i].mvar1][0]; + keys[1] = setupcontrols[currentMenu->menuitems[i].mvar1][1]; tmp[0] ='\0'; if (keys[0] == KEY_NULL && keys[1] == KEY_NULL) @@ -10244,7 +8841,7 @@ static void M_ChangeControl(INT32 choice) // If you change the below message, then change the size of this buffer! static char tmp[68]; - controltochange = currentMenu->menuitems[choice].alphaKey; + controltochange = currentMenu->menuitems[choice].mvar1; 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); @@ -10441,7 +9038,7 @@ static void M_DrawVideoMenu(void) { M_DrawGenericMenu(); - V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + OP_VideoOptionsMenu[0].alphaKey, + V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + OP_VideoOptionsMenu[0].mvar1, (SCR_IsAspectCorrect(vid.width, vid.height) ? recommendedflags : highlightflags), va("%dx%d", vid.width, vid.height)); } @@ -10696,23 +9293,23 @@ static void M_DrawMonitorToggles(void) } #ifdef ITEMTOGGLEBOTTOMRIGHT - if (currentMenu->menuitems[thisitem].alphaKey == 255) + if (currentMenu->menuitems[thisitem].mvar1 == 255) { V_DrawScaledPatch(x, y, V_TRANSLUCENT, W_CachePatchName("K_ISBG", PU_CACHE)); continue; } #endif - if (currentMenu->menuitems[thisitem].alphaKey == 0) + if (currentMenu->menuitems[thisitem].mvar1 == 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]; + cv = kartitemcvs[currentMenu->menuitems[thisitem].mvar1-1]; translucent = (cv->value ? 0 : V_TRANSLUCENT); - switch (currentMenu->menuitems[thisitem].alphaKey) + switch (currentMenu->menuitems[thisitem].mvar1) { case KRITEM_DUALJAWZ: drawnum = 2; @@ -10741,11 +9338,11 @@ static void M_DrawMonitorToggles(void) 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_DrawScaledPatch(x, y, translucent, W_CachePatchName(K_GetItemPatch(currentMenu->menuitems[thisitem].mvar1, 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)); + V_DrawScaledPatch(x, y, translucent, W_CachePatchName(K_GetItemPatch(currentMenu->menuitems[thisitem].mvar1, true), PU_CACHE)); y += spacing; } @@ -10756,7 +9353,7 @@ static void M_DrawMonitorToggles(void) { #ifdef ITEMTOGGLEBOTTOMRIGHT - if (currentMenu->menuitems[itemOn].alphaKey == 255) + if (currentMenu->menuitems[itemOn].mvar1 == 255) { V_DrawScaledPatch(onx-1, ony-2, V_TRANSLUCENT, W_CachePatchName("K_ITBG", PU_CACHE)); if (shitsfree) @@ -10771,17 +9368,17 @@ static void M_DrawMonitorToggles(void) } else #endif - if (currentMenu->menuitems[itemOn].alphaKey == 0) + if (currentMenu->menuitems[itemOn].mvar1 == 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]; + cv = kartitemcvs[currentMenu->menuitems[itemOn].mvar1-1]; translucent = (cv->value ? 0 : V_TRANSLUCENT); - switch (currentMenu->menuitems[itemOn].alphaKey) + switch (currentMenu->menuitems[itemOn].mvar1) { case KRITEM_DUALJAWZ: drawnum = 2; @@ -10806,12 +9403,12 @@ static void M_DrawMonitorToggles(void) 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-1, ony-2, translucent, W_CachePatchName(K_GetItemPatch(currentMenu->menuitems[itemOn].mvar1, 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)); + V_DrawScaledPatch(onx-1, ony-2, translucent, W_CachePatchName(K_GetItemPatch(currentMenu->menuitems[itemOn].mvar1, false), PU_CACHE)); } } @@ -10877,7 +9474,7 @@ static void M_HandleMonitorToggles(INT32 choice) case KEY_ENTER: #ifdef ITEMTOGGLEBOTTOMRIGHT - if (currentMenu->menuitems[itemOn].alphaKey == 255) + if (currentMenu->menuitems[itemOn].mvar1 == 255) { //S_StartSound(NULL, sfx_s26d); if (!shitsfree) @@ -10888,7 +9485,7 @@ static void M_HandleMonitorToggles(INT32 choice) } else #endif - if (currentMenu->menuitems[itemOn].alphaKey == 0) + if (currentMenu->menuitems[itemOn].mvar1 == 0) { INT32 v = cv_sneaker.value; S_StartSound(NULL, sfx_s1b4); @@ -10901,7 +9498,7 @@ static void M_HandleMonitorToggles(INT32 choice) else { S_StartSound(NULL, sfx_s1ba); - CV_AddValue(kartitemcvs[currentMenu->menuitems[itemOn].alphaKey-1], 1); + CV_AddValue(kartitemcvs[currentMenu->menuitems[itemOn].mvar1-1], 1); } break; @@ -11006,11 +9603,11 @@ static void M_OGL_DrawFogMenu(void) 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); + my + currentMenu->menuitems[FOG_COLOR_ITEM].mvar1, 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); + my + currentMenu->menuitems[FOG_COLOR_ITEM].mvar1, '_' | 0x80,false); } // ===================== @@ -11023,7 +9620,7 @@ static void M_OGL_DrawColorMenu(void) 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, + V_DrawString(mx, my + currentMenu->menuitems[0].mvar1 - 10, highlightflags, "Gamma correction"); } @@ -11075,6 +9672,7 @@ static void M_HandleFogColor(INT32 choice) } break; } + if (exitmenu) { if (currentMenu->prevMenu) diff --git a/src/m_menu.h b/src/m_menu.h index 62c852e4d..c2451b56f 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -150,8 +150,8 @@ typedef struct menuitem_s // FIXME: should be itemaction_t void *itemaction; - // hotkey in menu or y of the item - UINT8 alphaKey; + // extra variables + UINT8 mvar1; } menuitem_t; extern menuitem_t PlayerMenu[MAXSKINS]; @@ -244,6 +244,19 @@ void M_SetPlaybackMenuPointer(void); INT32 HU_GetHighlightColor(void); // These defines make it a little easier to make menus +#define KARTGAMEMODEMENU(header, source, prev)\ +{\ + header,\ + sizeof(source)/sizeof(menuitem_t),\ + prev,\ + source,\ + M_DrawGenericMenu,\ + M_DrawKartGamemodeMenu,\ + 0, 0,\ + 0,\ + NULL\ +} + #define DEFAULTMENUSTYLE(header, source, prev, x, y)\ {\ header,\ @@ -268,18 +281,6 @@ INT32 HU_GetHighlightColor(void); NULL\ } -#define CENTERMENUSTYLE(header, source, prev, y)\ -{\ - header,\ - sizeof(source)/sizeof(menuitem_t),\ - prev,\ - source,\ - M_DrawCenteredMenu,\ - BASEVIDWIDTH/2, y,\ - 0,\ - NULL\ -} - #define MAPICONMENUSTYLE(header, source, prev)\ {\ header,\ diff --git a/src/v_video.c b/src/v_video.c index 1b1d03baa..8cb0751f7 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -1608,6 +1608,18 @@ void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string) } } +void V_DrawCenteredString(INT32 x, INT32 y, INT32 option, const char *string) +{ + x -= V_StringWidth(string, option)/2; + V_DrawString(x, y, option, string); +} + +void V_DrawRightAlignedString(INT32 x, INT32 y, INT32 option, const char *string) +{ + x -= V_StringWidth(string, option); + V_DrawString(x, y, option, string); +} + // SRB2kart void V_DrawKartString(INT32 x, INT32 y, INT32 option, const char *string) { @@ -1709,20 +1721,79 @@ void V_DrawKartString(INT32 x, INT32 y, INT32 option, const char *string) cx += w; } } + +void V_DrawGamemodeString(INT32 x, INT32 y, INT32 option, const char *string, UINT8 color) +{ + INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, left = 0; + const char *ch = string; + const UINT8 *colormap = NULL; + + option &= ~V_FLIP; + + if (option & V_NOSCALESTART) + { + dupx = vid.dupx; + dupy = vid.dupy; + scrwidth = vid.width; + } + else + { + dupx = dupy = 1; + scrwidth = vid.width/vid.dupx; + left = (scrwidth - BASEVIDWIDTH)/2; + } + + colormap = R_GetTranslationColormap(TC_DEFAULT, color, GTC_MENUCACHE); + + for (;;ch++) + { + if (!*ch) + break; + + if (*ch == '\n') + { + cx = x; + cy += 34*dupy; + + continue; + } + + c = toupper(*ch) - AZ_FONTSTART; + + // character does not exist or is a space + if (c < 0 || c >= AZ_FONTSIZE || !gamemode_font[c]) + continue; + + w = (SHORT(gamemode_font[c]->width) - 2) * dupx; + + if (cx > scrwidth) + break; + + if (cx+left + w < 0) //left boundary check + { + cx += w; + continue; + } + + V_DrawFixedPatch(cx<= AZ_FONTSIZE || !gamemode_font[c]) + continue; + else + w += SHORT(gamemode_font[c]->width) - 2; + } + + return w; +} + boolean *heatshifter = NULL; INT32 lastheight = 0; INT32 heatindex[MAXSPLITSCREENPLAYERS] = {0, 0, 0, 0}; diff --git a/src/v_video.h b/src/v_video.h index a48fed4d3..3c2ee8478 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -175,10 +175,15 @@ char *V_WordWrap(INT32 x, INT32 w, INT32 option, const char *string); // draw a string using the hu_font void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string); -void V_DrawKartString(INT32 x, INT32 y, INT32 option, const char *string); // SRB2kart void V_DrawCenteredString(INT32 x, INT32 y, INT32 option, const char *string); void V_DrawRightAlignedString(INT32 x, INT32 y, INT32 option, const char *string); +// SRB2kart +void V_DrawKartString(INT32 x, INT32 y, INT32 option, const char *string); +void V_DrawGamemodeString(INT32 x, INT32 y, INT32 option, const char *string, UINT8 color); +void V_DrawCenteredGamemodeString(INT32 x, INT32 y, INT32 option, const char *string, UINT8 color); +void V_DrawRightAlignedGamemodeString(INT32 x, INT32 y, INT32 option, const char *string, UINT8 color); + // draw a string using the hu_font, 0.5x scale void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string); void V_DrawRightAlignedSmallString(INT32 x, INT32 y, INT32 option, const char *string); @@ -212,6 +217,9 @@ INT32 V_SmallStringWidth(const char *string, INT32 option); // Find string width from tny_font chars INT32 V_ThinStringWidth(const char *string, INT32 option); +// SRB2Kart +INT32 V_GamemodeStringWidth(const char *string, INT32 option); + void V_DoPostProcessor(INT32 view, postimg_t type, INT32 param); void V_DrawPatchFill(patch_t *pat); From ef9486f1f600cdd41db1bebdbe72d91c4d52439d Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Fri, 9 Aug 2019 04:25:42 -0400 Subject: [PATCH 002/379] Was not included in the first commit for whatever reason --- src/m_menu.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/m_menu.h b/src/m_menu.h index c2451b56f..3789f261c 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -250,7 +250,6 @@ INT32 HU_GetHighlightColor(void); sizeof(source)/sizeof(menuitem_t),\ prev,\ source,\ - M_DrawGenericMenu,\ M_DrawKartGamemodeMenu,\ 0, 0,\ 0,\ From ca83b06d93f75b6736397bfe99cfe94a7b7ea8bd Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Wed, 2 Oct 2019 11:31:11 -0400 Subject: [PATCH 003/379] Fuckhuge restructuring - m_menu.c and m_menu.h are dead. Everything has now been moved to k_menudef.c, k_menufunc.c, k_menudraw.c, and k_menu.h. - Expanded menu_t and menuitem_t to add transitions and tooltips - Early character select screen - Removed almost all menu definitions, I'll be reimporting them as they are needed --- src/Makefile | 5 +- src/command.c | 12 +- src/command.h | 2 +- src/console.c | 25 +- src/console.h | 2 +- src/d_clisrv.c | 2 +- src/d_main.c | 4 +- src/d_netcmd.c | 16 +- src/d_netfil.c | 2 +- src/dehacked.c | 22 +- src/f_finale.c | 2 +- src/f_wipe.c | 2 +- src/filesrch.c | 2 +- src/filesrch.h | 2 +- src/g_game.c | 6 +- src/hardware/hw_bsp.c | 2 +- src/hu_stuff.c | 10 +- src/{m_menu.h => k_menu.h} | 354 +- src/k_menudef.c | 132 + src/k_menudraw.c | 844 ++++ src/k_menufunc.c | 2021 ++++++++ src/m_cheat.c | 2 +- src/m_menu.c | 9684 ------------------------------------ src/m_misc.c | 2 +- src/mserv.c | 2 +- src/p_setup.c | 2 +- src/r_main.c | 2 +- src/sdl/i_video.c | 6 +- src/st_stuff.c | 10 +- src/y_inter.c | 11 +- 30 files changed, 3242 insertions(+), 9948 deletions(-) rename src/{m_menu.h => k_menu.h} (61%) create mode 100644 src/k_menudef.c create mode 100644 src/k_menudraw.c create mode 100644 src/k_menufunc.c delete mode 100644 src/m_menu.c diff --git a/src/Makefile b/src/Makefile index f4a77aedd..d106def5b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -467,7 +467,6 @@ DBGNAME?=$(EXENAME).debug # $(OBJDIR)/dstrings.o \ # not too sophisticated dependency -# SRB2kart kart.o on line 433 below OBJS:=$(i_main_o) \ $(OBJDIR)/comptime.o \ $(OBJDIR)/string.o \ @@ -496,7 +495,9 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/m_cheat.o \ $(OBJDIR)/m_cond.o \ $(OBJDIR)/m_fixed.o \ - $(OBJDIR)/m_menu.o \ + $(OBJDIR)/k_menudef.o \ + $(OBJDIR)/k_menufunc.o \ + $(OBJDIR)/k_menudraw.o \ $(OBJDIR)/m_misc.o \ $(OBJDIR)/m_random.o \ $(OBJDIR)/m_queue.o \ diff --git a/src/command.c b/src/command.c index 6d9c86d3e..e3fc0c292 100644 --- a/src/command.c +++ b/src/command.c @@ -21,7 +21,7 @@ #include "command.h" #include "console.h" #include "z_zone.h" -#include "m_menu.h" +#include "k_menu.h" #include "m_misc.h" #include "m_fixed.h" #include "m_argv.h" @@ -75,7 +75,7 @@ CV_PossibleValue_t kartspeed_cons_t[] = { // Filter consvars by EXECVERSION // First implementation is 2 (1.0.2), so earlier configs default at 1 (1.0.0) -// Also set CV_HIDEN during runtime, after config is loaded +// Also set CV_HIDDEN during runtime, after config is loaded static boolean execversion_enabled = false; consvar_t cv_execversion = {"execversion","1",CV_CALL,CV_Unsigned, CV_EnforceExecVersion, 0, NULL, NULL, 0, 0, NULL}; @@ -1112,7 +1112,7 @@ void CV_RegisterVar(consvar_t *variable) } // link the variable in - if (!(variable->flags & CV_HIDEN)) + if (!(variable->flags & CV_HIDDEN)) { variable->next = consvar_vars; consvar_vars = variable; @@ -1628,6 +1628,7 @@ void CV_AddValue(consvar_t *var, INT32 increment) if (var->PossibleValue) { +#if 0 if (var == &cv_nextmap) { // Special case for the nextmap variable, used only directly from the menu @@ -1662,9 +1663,12 @@ void CV_AddValue(consvar_t *var, INT32 increment) return; } } + else +#endif + #define MINVAL 0 #define MAXVAL 1 - else if (var->PossibleValue[MINVAL].strvalue && !strcmp(var->PossibleValue[MINVAL].strvalue, "MIN")) + if (var->PossibleValue[MINVAL].strvalue && !strcmp(var->PossibleValue[MINVAL].strvalue, "MIN")) { // SRB2Kart #ifdef PARANOIA if (!var->PossibleValue[MAXVAL].strvalue) diff --git a/src/command.h b/src/command.h index 6b5d513ef..d7a0109e2 100644 --- a/src/command.h +++ b/src/command.h @@ -95,7 +95,7 @@ typedef enum CV_SHOWMODIF = 128, // say something when modified CV_SHOWMODIFONETIME = 256, // same but will be reset to 0 when modified, set in toggle CV_NOSHOWHELP = 512, // Don't show variable in the HELP list Tails 08-13-2002 - CV_HIDEN = 1024, // variable is not part of the cvar list so cannot be accessed by the console + CV_HIDDEN = 1024, // variable is not part of the cvar list so cannot be accessed by the console // can only be set when we have the pointer to it // used on menus CV_CHEAT = 2048, // Don't let this be used in multiplayer unless cheats are on. diff --git a/src/console.c b/src/console.c index 1defa7e82..341145ab1 100644 --- a/src/console.c +++ b/src/console.c @@ -32,7 +32,7 @@ #include "z_zone.h" #include "i_system.h" #include "d_main.h" -#include "m_menu.h" +#include "k_menu.h" #include "filesrch.h" #ifdef _WINDOWS @@ -140,28 +140,6 @@ static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, {1, "Black"}, { {0, NULL}}; consvar_t cons_backcolor = {"con_backcolor", "Black", CV_CALL|CV_SAVE, backcolor_cons_t, CONS_backcolor_Change, 0, NULL, NULL, 0, 0, NULL}; -static CV_PossibleValue_t menuhighlight_cons_t[] = -{ - {0, "Game type"}, - {V_YELLOWMAP, "Always yellow"}, - {V_PURPLEMAP, "Always purple"}, - {V_GREENMAP, "Always green"}, - {V_BLUEMAP, "Always blue"}, - {V_REDMAP, "Always red"}, - {V_GRAYMAP, "Always gray"}, - {V_ORANGEMAP, "Always orange"}, - {V_SKYMAP, "Always sky-blue"}, - {V_GOLDMAP, "Always gold"}, - {V_LAVENDERMAP, "Always lavender"}, - {V_AQUAMAP, "Always aqua-green"}, - {V_MAGENTAMAP, "Always magenta"}, - {V_PINKMAP, "Always pink"}, - {V_BROWNMAP, "Always brown"}, - {V_TANMAP, "Always tan"}, - {0, NULL} -}; -consvar_t cons_menuhighlight = {"menuhighlight", "Game type", CV_SAVE, menuhighlight_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; - static void CON_Print(char *msg); // @@ -406,7 +384,6 @@ void CON_Init(void) CV_RegisterVar(&cons_height); CV_RegisterVar(&cons_backpic); CV_RegisterVar(&cons_backcolor); - CV_RegisterVar(&cons_menuhighlight); COM_AddCommand("bind", CONS_Bind_f); } else diff --git a/src/console.h b/src/console.h index 7ed585177..7b17965ae 100644 --- a/src/console.h +++ b/src/console.h @@ -36,7 +36,7 @@ extern INT32 con_clearlines; // lines of top of screen to refresh extern boolean con_hudupdate; // hud messages have changed, need refresh extern UINT32 con_scalefactor; // console text scale factor -extern consvar_t cons_backcolor, cons_menuhighlight; +extern consvar_t cons_backcolor; extern UINT8 *yellowmap, *purplemap, *greenmap, *bluemap, *graymap, *redmap, *orangemap,\ *skymap, *goldmap, *lavendermap, *aquamap, *magentamap, *pinkmap, *brownmap, *tanmap; diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 4d61194c4..59bb87aac 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -28,7 +28,7 @@ #include "hu_stuff.h" #include "keys.h" #include "g_input.h" // JOY1 -#include "m_menu.h" +#include "k_menu.h" #include "console.h" #include "d_netfil.h" #include "byteptr.h" diff --git a/src/d_main.c b/src/d_main.c index 098a2bf33..065ee07fa 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -52,7 +52,7 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #include "i_system.h" #include "i_video.h" #include "m_argv.h" -#include "m_menu.h" +#include "k_menu.h" #include "m_misc.h" #include "p_setup.h" #include "p_saveg.h" @@ -1187,7 +1187,9 @@ void D_SRB2Main(void) // Setup character tables // Have to be done here before files are loaded +#ifdef USEPLAYERMENU M_InitCharacterTables(); +#endif // load wad, including the main wad file CONS_Printf("W_InitMultipleFiles(): Adding main IWAD and PWADs.\n"); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 11c4547de..95cb9e543 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -20,7 +20,7 @@ #include "g_game.h" #include "hu_stuff.h" #include "g_input.h" -#include "m_menu.h" +#include "k_menu.h" #include "r_local.h" #include "r_things.h" #include "p_local.h" @@ -300,8 +300,8 @@ consvar_t cv_joyscale2 = {"joyscale2", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale2, consvar_t cv_joyscale3 = {"joyscale3", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale3, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_joyscale4 = {"joyscale4", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale4, 0, NULL, NULL, 0, 0, NULL}; #else -consvar_t cv_joyscale = {"joyscale", "1", CV_SAVE|CV_HIDEN, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; //Alam: Dummy for save -consvar_t cv_joyscale2 = {"joyscale2", "1", CV_SAVE|CV_HIDEN, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; //Alam: Dummy for save +consvar_t cv_joyscale = {"joyscale", "1", CV_SAVE|CV_HIDDEN, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; //Alam: Dummy for save +consvar_t cv_joyscale2 = {"joyscale2", "1", CV_SAVE|CV_HIDDEN, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; //Alam: Dummy for save #endif #if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON) consvar_t cv_mouse2port = {"mouse2port", "/dev/gpmdata", CV_SAVE, mouse2port_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -794,18 +794,22 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_playername); CV_RegisterVar(&cv_playercolor); CV_RegisterVar(&cv_skin); // r_things.c (skin NAME) + // secondary player (splitscreen) CV_RegisterVar(&cv_playername2); CV_RegisterVar(&cv_playercolor2); CV_RegisterVar(&cv_skin2); + // third player CV_RegisterVar(&cv_playername3); CV_RegisterVar(&cv_playercolor3); CV_RegisterVar(&cv_skin3); + // fourth player CV_RegisterVar(&cv_playername4); CV_RegisterVar(&cv_playercolor4); CV_RegisterVar(&cv_skin4); + // preferred number of players CV_RegisterVar(&cv_splitplayers); @@ -839,7 +843,7 @@ void D_RegisterClientCommands(void) // FIXME: not to be here.. but needs be done for config loading CV_RegisterVar(&cv_usegamma); - // m_menu.c + // k_menu.c //CV_RegisterVar(&cv_compactscoreboard); CV_RegisterVar(&cv_chatheight); CV_RegisterVar(&cv_chatwidth); @@ -925,10 +929,12 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_usejoystick2); CV_RegisterVar(&cv_usejoystick3); CV_RegisterVar(&cv_usejoystick4); + #ifdef LJOYSTICK CV_RegisterVar(&cv_joyport); CV_RegisterVar(&cv_joyport2); #endif + CV_RegisterVar(&cv_joyscale); CV_RegisterVar(&cv_joyscale2); CV_RegisterVar(&cv_joyscale3); @@ -3958,7 +3964,7 @@ static void Got_Removal(UINT8 **cp, INT32 playernum) } // Join password stuff -consvar_t cv_dummyjoinpassword = {"dummyjoinpassword", "", CV_HIDEN|CV_NOSHOWHELP|CV_PASSWORD, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_dummyjoinpassword = {"dummyjoinpassword", "", CV_HIDDEN|CV_NOSHOWHELP|CV_PASSWORD, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; #define NUMJOINCHALLENGES 32 static UINT8 joinpassmd5[MD5_LEN+1]; diff --git a/src/d_netfil.c b/src/d_netfil.c index 155700807..2b70e728e 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -56,7 +56,7 @@ #include "byteptr.h" #include "p_setup.h" #include "m_misc.h" -#include "m_menu.h" +#include "k_menu.h" #include "md5.h" #include "filesrch.h" diff --git a/src/dehacked.c b/src/dehacked.c index d94f6d91c..5932e7ead 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -19,7 +19,7 @@ #include "m_argv.h" #include "z_zone.h" #include "w_wad.h" -#include "m_menu.h" +#include "k_menu.h" #include "m_misc.h" #include "filesrch.h" // for refreshdirmenu #include "f_finale.h" @@ -34,6 +34,7 @@ #include "lua_script.h" #include "lua_hook.h" #include "d_clisrv.h" +#include "r_things.h" #include "m_cond.h" @@ -432,7 +433,8 @@ static void readAnimTex(MYFILE *f, INT32 num) } */ -static boolean findFreeSlot(INT32 *num) +#ifdef USEPLAYERMENU +static boolean findPlayerFreeSlot(INT32 *num) { // Send the character select entry to a free slot. while (*num < MAXSKINS && PlayerMenu[*num].status != IT_DISABLED) @@ -475,7 +477,7 @@ static void readPlayer(MYFILE *f, INT32 num) { char *playertext = NULL; - if (!slotfound && (slotfound = findFreeSlot(&num)) == false) + if (!slotfound && (slotfound = findPlayerFreeSlot(&num)) == false) goto done; PlayerMenu[num].status = IT_CALL; @@ -525,7 +527,7 @@ static void readPlayer(MYFILE *f, INT32 num) /*if (fastcmp(word, "PLAYERNAME")) { - if (!slotfound && (slotfound = findFreeSlot(&num)) == false) + if (!slotfound && (slotfound = findPlayerFreeSlot(&num)) == false) goto done; DEH_WriteUndoline(word, description[num].text, UNDO_NONE); strlcpy(description[num].text, word2, sizeof (description[num].text)); @@ -547,7 +549,7 @@ static void readPlayer(MYFILE *f, INT32 num) } */ /*else*/ if (fastcmp(word, "PICNAME")) { - if (!slotfound && (slotfound = findFreeSlot(&num)) == false) + if (!slotfound && (slotfound = findPlayerFreeSlot(&num)) == false) goto done; DEH_WriteUndoline(word, &description[num].picname[0], UNDO_NONE); PlayerMenu[num].status = IT_CALL; @@ -571,7 +573,7 @@ static void readPlayer(MYFILE *f, INT32 num) ... Or use MENUPOSITION first, that works too. Hell, you could edit multiple character slots in a single section that way, due to how SOC editing works. */ - if (i != IT_DISABLED && !slotfound && (slotfound = findFreeSlot(&num)) == false) + if (i != IT_DISABLED && !slotfound && (slotfound = findPlayerFreeSlot(&num)) == false) goto done; DEH_WriteUndoline(word, va("%d", PlayerMenu[num].status), UNDO_NONE); PlayerMenu[num].status = (INT16)i; @@ -579,7 +581,7 @@ static void readPlayer(MYFILE *f, INT32 num) else if (fastcmp(word, "SKINNAME")) { // Send to free slot. - if (!slotfound && (slotfound = findFreeSlot(&num)) == false) + if (!slotfound && (slotfound = findPlayerFreeSlot(&num)) == false) goto done; DEH_WriteUndoline(word, description[num].skinname, UNDO_NONE); PlayerMenu[num].status = IT_CALL; @@ -600,6 +602,7 @@ static void readPlayer(MYFILE *f, INT32 num) done: Z_Free(s); } +#endif static int freeslotusage[2][2] = {{0, 0}, {0, 0}}; // [S_, MT_][max, previous .wad's max] @@ -3452,6 +3455,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad) continue; } word2 = strtok(NULL, " "); +#ifdef USEPLAYERMENU if (fastcmp(word, "CHARACTER")) { if (word2) { @@ -3472,6 +3476,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad) // This is not a major mod. continue; } +#endif if (word2) { strupr(word2); @@ -8933,8 +8938,7 @@ struct { {"CV_SHOWMODIF",CV_SHOWMODIF}, {"CV_SHOWMODIFONETIME",CV_SHOWMODIFONETIME}, {"CV_NOSHOWHELP",CV_NOSHOWHELP}, - {"CV_HIDEN",CV_HIDEN}, - {"CV_HIDDEN",CV_HIDEN}, + {"CV_HIDDEN",CV_HIDDEN}, {"CV_CHEAT",CV_CHEAT}, // v_video flags diff --git a/src/f_finale.c b/src/f_finale.c index d45a9cfa9..956c2db94 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -24,7 +24,7 @@ #include "w_wad.h" #include "z_zone.h" #include "i_system.h" -#include "m_menu.h" +#include "k_menu.h" #include "dehacked.h" #include "g_input.h" #include "console.h" diff --git a/src/f_wipe.c b/src/f_wipe.c index 1ea2ec2fa..4faf61146 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -26,7 +26,7 @@ #include "z_zone.h" #include "i_system.h" -#include "m_menu.h" +#include "k_menu.h" #include "console.h" #include "d_main.h" #include "m_misc.h" // movie mode diff --git a/src/filesrch.c b/src/filesrch.c index d132e9fb4..22c6228c1 100644 --- a/src/filesrch.c +++ b/src/filesrch.c @@ -32,7 +32,7 @@ #include "d_netfil.h" #include "m_misc.h" #include "z_zone.h" -#include "m_menu.h" // Addons_option_Onchange +#include "k_menu.h" // Addons_option_Onchange #if (defined (_WIN32) && !defined (_WIN32_WCE)) && defined (_MSC_VER) && !defined (_XBOX) diff --git a/src/filesrch.h b/src/filesrch.h index 4cb92b238..8bfc92d1d 100644 --- a/src/filesrch.h +++ b/src/filesrch.h @@ -6,7 +6,7 @@ #include "doomdef.h" #include "d_netfil.h" -#include "m_menu.h" // MAXSTRINGLENGTH +#include "k_menu.h" // MAXSTRINGLENGTH extern consvar_t cv_addons_option, cv_addons_folder, cv_addons_md5, cv_addons_showall, cv_addons_search_case, cv_addons_search_type; diff --git a/src/g_game.c b/src/g_game.c index cb4131e6d..fcb815d3a 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -30,7 +30,7 @@ #include "g_game.h" #include "m_cheat.h" #include "m_misc.h" -#include "m_menu.h" +#include "k_menu.h" #include "m_argv.h" #include "hu_stuff.h" #include "st_stuff.h" @@ -3831,9 +3831,11 @@ void G_AfterIntermission(void) { G_StopDemo(); +#if 0 if (demo.inreplayhut) M_ReplayHut(0); else +#endif D_StartTitle(); return; @@ -4327,7 +4329,7 @@ static void M_ForceLoadGameResponse(INT32 ch) Z_Free(savebuffer); save_p = savebuffer = NULL; startonmapnum = 0; - M_SetupNextMenu(&SP_LoadDef); + M_SetupNextMenu(&SP_LoadDef, false); return; } diff --git a/src/hardware/hw_bsp.c b/src/hardware/hw_bsp.c index 9e454bcd5..99453853d 100644 --- a/src/hardware/hw_bsp.c +++ b/src/hardware/hw_bsp.c @@ -24,7 +24,7 @@ #include "../z_zone.h" #include "../console.h" #include "../v_video.h" -#include "../m_menu.h" +#include "../k_menu.h" #include "../i_system.h" #include "../m_argv.h" #include "../i_video.h" diff --git a/src/hu_stuff.c b/src/hu_stuff.c index cf3cc5872..44ed3ae81 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -15,7 +15,7 @@ #include "byteptr.h" #include "hu_stuff.h" -#include "m_menu.h" // gametype_cons_t +#include "k_menu.h" // gametype_cons_t #include "m_cond.h" // emblems #include "d_clisrv.h" @@ -111,7 +111,7 @@ static patch_t *tokenicon; // misc vars //------------------------------------------- -// crosshair 0 = off, 1 = cross, 2 = angle, 3 = point, see m_menu.c +// crosshair 0 = off, 1 = cross, 2 = angle, 3 = point, see k_menu.c static patch_t *crosshair[HU_CROSSHAIRS]; // 3 precached crosshair graphics // song credits static patch_t *songcreditbg; @@ -1624,7 +1624,7 @@ static void HU_drawChatLog(INT32 offset) INT32 x = chatx+2, y, dx = 0, dy = 0; UINT32 i = 0; INT32 chat_topy, chat_bottomy; - INT32 highlight = HU_GetHighlightColor(); + INT32 highlight = V_YELLOWMAP; boolean atbottom = false; // make sure that our scroll position isn't "illegal"; @@ -2947,9 +2947,7 @@ static void HU_DrawRankings(void) V_DrawFadeScreen(0xFF00, 16); // A little more readable, and prevents cheating the fades under other circumstances. - if (cons_menuhighlight.value) - hilicol = cons_menuhighlight.value; - else if (modeattacking) + if (modeattacking) hilicol = V_ORANGEMAP; else hilicol = ((gametype == GT_RACE) ? V_SKYMAP : V_REDMAP); diff --git a/src/m_menu.h b/src/k_menu.h similarity index 61% rename from src/m_menu.h rename to src/k_menu.h index 3789f261c..863838127 100644 --- a/src/m_menu.h +++ b/src/k_menu.h @@ -9,68 +9,15 @@ // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- -/// \file m_menu.h +/// \file k_menu.h /// \brief Menu widget stuff, selection and such -#ifndef __X_MENU__ -#define __X_MENU__ +#ifndef __K_MENU__ +#define __K_MENU__ #include "d_event.h" #include "command.h" -#include "r_things.h" // for SKINNAMESIZE - -// -// MENUS -// -// Called by main loop, -// saves config file and calls I_Quit when user exits. -// Even when the menu is not displayed, -// this can resize the view and change game parameters. -// Does all the real work of the menu interaction. -boolean M_Responder(event_t *ev); - -// Called by main loop, only used for menu (skull cursor) animation. -void M_Ticker(void); - -// Called by main loop, draws the menus directly into the screen buffer. -void M_Drawer(void); - -// Called by D_SRB2Main, loads the config file. -void M_Init(void); - -// Called by D_SRB2Main also, sets up the playermenu and description tables. -void M_InitCharacterTables(void); - -// Called by intro code to force menu up upon a keypress, -// does nothing if menu is already up. -void M_StartControlPanel(void); - -// Called upon end of a mode attack run -void M_EndModeAttackRun(void); - -// Called on new server add, or other reasons -void M_SortServerList(void); - -// Draws a box with a texture inside as background for messages -void M_DrawTextBox(INT32 x, INT32 y, INT32 width, INT32 boxlines); - -// the function to show a message box typing with the string inside -// string must be static (not in the stack) -// routine is a function taking a INT32 in parameter -typedef enum -{ - MM_NOTHING = 0, // is just displayed until the user do someting - MM_YESNO, // routine is called with only 'y' or 'n' in param - MM_EVENTHANDLER // the same of above but without 'y' or 'n' restriction - // and routine is void routine(event_t *) (ex: set control) -} menumessagetype_t; -void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtype); - -// Called by linux_x/i_video_xshm.c -void M_QuitResponse(INT32 ch); - -// Determines whether to show a level in the list -boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt); +#include "doomstat.h" // MAXSPLITSCREENPLAYERS // flags for items in the menu // menu handle (what we do when key is pressed @@ -141,179 +88,220 @@ typedef union // typedef struct menuitem_s { - // show IT_xxx - UINT16 status; + UINT16 status; // show IT_xxx - const char *patch; - const char *text; // used when FONTBxx lump is found + const char *text; // option title + const char *tooltip; // description of option used by K_MenuTooltips + const char *patch; // image of option used by K_MenuPreviews -// FIXME: should be itemaction_t - void *itemaction; + void *itemaction; // FIXME: should be itemaction_t // extra variables UINT8 mvar1; + UINT8 mvar2; } menuitem_t; -extern menuitem_t PlayerMenu[MAXSKINS]; - typedef struct menu_s { - const char *menutitlepic; INT16 numitems; // # of menu items struct menu_s *prevMenu; // previous menu - menuitem_t *menuitems; // menu items - void (*drawroutine)(void); // draw routine - INT16 x, y; // x, y of menu INT16 lastOn; // last item user was on in menu + menuitem_t *menuitems; // menu items + INT16 x, y; // x, y of menu + INT16 transitionInTics; // tics for transitions in + INT16 transitionOutTics; // tics for transitions out + void (*drawroutine)(void); // draw routine boolean (*quitroutine)(void); // called before quit a menu return true if we can } menu_t; -void M_SetupNextMenu(menu_t *menudef); -void M_ClearMenus(boolean callexitmenufunc); +typedef enum +{ + MM_NOTHING = 0, // is just displayed until the user do someting + MM_YESNO, // routine is called with only 'y' or 'n' in param + MM_EVENTHANDLER // the same of above but without 'y' or 'n' restriction + // and routine is void routine(event_t *) (ex: set control) +} menumessagetype_t; + +// =========== +// PROTOTYPING +// =========== + +// K_MENUDEF.C +extern menuitem_t MainMenu[]; +extern menu_t MainDef; + +typedef enum +{ + play = 0, + extra, + options, + quitkart +} main_e; + +extern menuitem_t PLAY_MainMenu[]; +extern menu_t PLAY_MainDef; + +extern menuitem_t PLAY_CharSelect[]; +extern menu_t PLAY_CharSelectDef; + +extern menuitem_t PLAY_Gamemodes[]; +extern menu_t PLAY_GamemodesDef; + +extern menuitem_t PLAY_RaceGamemodesMenu[]; +extern menu_t PLAY_RaceGamemodesDef; + +extern menuitem_t PAUSE_PlaybackMenu[]; +extern menu_t PAUSE_PlaybackMenuDef; + +typedef enum +{ + playback_hide, + playback_rewind, + playback_pause, + playback_fastforward, + playback_backframe, + playback_resume, + playback_advanceframe, + playback_viewcount, + playback_view1, + playback_view2, + playback_view3, + playback_view4, + playback_quit +} playback_e; + + +// K_MENUFUNC.C extern menu_t *currentMenu; -extern menu_t MainDef; -extern menu_t SP_LoadDef; +extern char dummystaffname[22]; -// Call upon joystick hotplug -void M_SetupJoystickMenu(INT32 choice); -extern menu_t OP_JoystickSetDef; +extern INT16 itemOn; // menu item skull is on, Hack by Tails 09-18-2002 +extern INT16 skullAnimCounter; // skull animation counter -// Stuff for customizing the player select screen -typedef struct -{ - char notes[441]; - char picname[8]; - char skinname[SKINNAMESIZE*2+2]; // skin&skin\0 -} description_t; +extern struct menutransition_s { + INT16 tics; + INT16 dest; + struct menu_s *newmenu; + boolean in; +} menutransition; -// mode descriptions for video mode menu -typedef struct -{ - INT32 modenum; // video mode number in the vidmodes list - const char *desc; // XXXxYYY - UINT8 goodratio; // aspect correct if 1 -} modedesc_t; - -// savegame struct for save game menu -typedef struct -{ - char playername[37]; - char levelname[32]; - UINT8 actnum; - UINT8 skincolor; - UINT8 skinnum; - UINT8 botskin; - UINT8 botcolor; - UINT8 numemeralds; - INT32 lives; - INT32 continues; - INT32 gamemap; - UINT8 netgame; -} saveinfo_t; - -extern description_t description[MAXSKINS]; +extern boolean menuwipe; extern consvar_t cv_showfocuslost; extern consvar_t cv_newgametype, cv_nextmap, cv_chooseskin, cv_serversort; extern CV_PossibleValue_t gametype_cons_t[]; -extern char dummystaffname[22]; - -extern INT16 startmap; -extern INT32 ultimate_selectable; - -#define MAXSAVEGAMES 31 //note: last save game is "no save" -#define NOSAVESLOT MAXSAVEGAMES-1 //slot where Play Without Saving appears - -void M_ForceSaveSlotSelected(INT32 sslot); - -void M_CheatActivationResponder(INT32 ch); - -// Screenshot menu updating void Moviemode_mode_Onchange(void); void Screenshot_option_Onchange(void); - -// Addons menu updating void Addons_option_Onchange(void); -void M_ReplayHut(INT32 choice); -void M_SetPlaybackMenuPointer(void); +void M_SortServerList(void); -INT32 HU_GetHighlightColor(void); +boolean M_Responder(event_t *ev); +void M_StartControlPanel(void); +void M_ClearMenus(boolean callexitmenufunc); +void M_SelectableClearMenus(INT32 choice); +void M_SetupNextMenu(menu_t *menudef, boolean nofade); +void M_GoBack(INT32 choice); +void M_Ticker(void); +void M_Init(void); + +menu_t MessageDef; +void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtype); +void M_StopMessage(INT32 choice); + +void M_HandleImageDef(INT32 choice); + +void M_QuitResponse(INT32 ch); +void M_QuitSRB2(INT32 choice); + +// If you want to waste a bunch of memory for a limit no one will hit, feel free to boost this to MAXSKINS :P +// I figure this will be enough clone characters to fit onto the character select. +// (If someone runs into it after release I'll probably boost it, though.) +#define MAXCLONES MAXSKINS/16 + +extern struct setup_chargrid_s { + SINT8 skinlist[MAXCLONES]; + UINT8 numskins; +} setup_chargrid[9][9]; + +typedef struct setup_player_s +{ + SINT8 gridx, gridy; + SINT8 skin; + SINT8 clonenum; + UINT8 color; + UINT8 mdepth; +} setup_player_t; + +extern setup_player_t setup_player[MAXSPLITSCREENPLAYERS]; + +extern UINT8 setup_numplayers; + +typedef enum +{ + SPLITCV_SKIN = 0, + SPLITCV_COLOR, + SPLITCV_NAME, + SPLITCV_MAX +} splitscreencvars_t; +consvar_t *setup_playercvars[MAXSPLITSCREENPLAYERS][SPLITCV_MAX]; + +void M_CharacterSelectInit(INT32 choice); +void M_CharacterSelectHandler(INT32 choice); +boolean M_CharacterSelectQuit(void); + +void M_EndModeAttackRun(void); +void M_SetPlaybackMenuPointer(void); +void M_PlaybackRewind(INT32 choice); +void M_PlaybackPause(INT32 choice); +void M_PlaybackFastForward(INT32 choice); +void M_PlaybackAdvance(INT32 choice); +void M_PlaybackSetViews(INT32 choice); +void M_PlaybackAdjustView(INT32 choice); +void M_PlaybackQuit(INT32 choice); + +void M_ReplayHut(INT32 choice); + +// M_MENUDRAW.C + +void M_Drawer(void); +void M_DrawGenericMenu(void); +void M_DrawKartGamemodeMenu(void); +void M_DrawTextBox(INT32 x, INT32 y, INT32 width, INT32 boxlines); +void M_DrawMessageMenu(void); +void M_DrawImageDef(void); + +void M_DrawCharacterSelect(void); + +void M_DrawPlaybackMenu(void); // These defines make it a little easier to make menus -#define KARTGAMEMODEMENU(header, source, prev)\ +#define DEFAULTMENUSTYLE(source, prev, x, y)\ {\ - header,\ - sizeof(source)/sizeof(menuitem_t),\ + sizeof(source) / sizeof(menuitem_t),\ prev,\ - source,\ - M_DrawKartGamemodeMenu,\ - 0, 0,\ 0,\ - NULL\ -} - -#define DEFAULTMENUSTYLE(header, source, prev, x, y)\ -{\ - header,\ - sizeof(source)/sizeof(menuitem_t),\ - prev,\ source,\ + x, y,\ + 0, 0,\ M_DrawGenericMenu,\ - x, y,\ - 0,\ NULL\ } -#define PAUSEMENUSTYLE(source, x, y)\ -{\ - NULL,\ - sizeof(source)/sizeof(menuitem_t),\ - NULL,\ - source,\ - M_DrawPauseMenu,\ - x, y,\ - 0,\ - NULL\ -} -#define MAPICONMENUSTYLE(header, source, prev)\ +#define KARTGAMEMODEMENU(source, prev)\ {\ - header,\ - sizeof (source)/sizeof (menuitem_t),\ + sizeof(source) / sizeof(menuitem_t),\ prev,\ - source,\ - M_DrawServerMenu,\ - 24,40,\ 0,\ - NULL\ -} - -#define CONTROLMENUSTYLE(source, prev)\ -{\ - "M_CONTRO",\ - sizeof (source)/sizeof (menuitem_t),\ - prev,\ source,\ - M_DrawControl,\ - 26, 40,\ - 0,\ - NULL\ -} - -#define IMAGEDEF(source)\ -{\ - NULL,\ - sizeof (source)/sizeof (menuitem_t),\ - NULL,\ - source,\ - M_DrawImageDef,\ 0, 0,\ - 0,\ + 10, 10,\ + M_DrawKartGamemodeMenu,\ NULL\ } -#endif //__X_MENU__ +#endif //__K_MENU__ diff --git a/src/k_menudef.c b/src/k_menudef.c new file mode 100644 index 000000000..07cd413b9 --- /dev/null +++ b/src/k_menudef.c @@ -0,0 +1,132 @@ +/// \file k_menudef.c +/// \brief SRB2Kart menu definitions + +#include "k_menu.h" +#include "screen.h" // BASEVIDWIDTH + +// ========================================================================== +// 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 +// --------- +menuitem_t MainMenu[] = +{ + {IT_STRING | IT_CALL, "Play", "Cut to the chase and start playing!", + NULL, M_CharacterSelectInit, 48, 0}, + + {IT_STRING, "Extra", "Check out some bonus features.", + NULL, NULL, 80, 0}, + + {IT_STRING, "Option", "Configure your controls, settings, and preferences.", + NULL, NULL, 112, 0}, + + {IT_STRING | IT_CALL, "Quit", "Exit SRB2Kart.", + NULL, M_QuitSRB2, 160, 0}, +}; + +menu_t MainDef = KARTGAMEMODEMENU(MainMenu, NULL); + +// --------- +// Play Menu +// --------- + +menuitem_t PLAY_CharSelect[] = +{ + {IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, M_CharacterSelectHandler, 0, 0}, +}; + +menu_t PLAY_CharSelectDef = { + sizeof (PLAY_CharSelect) / sizeof (menuitem_t), + &MainDef, + 0, + PLAY_CharSelect, + 0, 0, + 0, 0, + M_DrawCharacterSelect, + M_CharacterSelectQuit +}; + +menuitem_t PLAY_MainMenu[] = +{ + {IT_STRING | IT_SUBMENU, "Local Play", "Play only on this computer.", + NULL, &PLAY_GamemodesDef, 64, 0}, + + {IT_STRING, "Online", "Connect to other computers.", + NULL, NULL, 96, 0}, + + {IT_STRING | IT_CALL, "Back", NULL, NULL, M_GoBack, 160, 0}, +}; + +menu_t PLAY_MainDef = KARTGAMEMODEMENU(PLAY_MainMenu, &PLAY_CharSelectDef); + +menuitem_t PLAY_GamemodesMenu[] = +{ + {IT_STRING | IT_SUBMENU, "Race", "A competition for the best time!", + NULL, &PLAY_RaceGamemodesDef, 64, 0}, + + {IT_STRING, "Battle", "Clash against other players in a survival match!", + NULL, NULL, 96, 0}, + + {IT_STRING | IT_CALL, "Back", NULL, NULL, M_GoBack, 160, 0}, +}; + +menu_t PLAY_GamemodesDef = KARTGAMEMODEMENU(PLAY_GamemodesMenu, &PLAY_MainDef); + +menuitem_t PLAY_RaceGamemodesMenu[] = +{ + {IT_STRING, "Grand Prix", "Compete for the best rank over five races!", + NULL, NULL, 48, 0}, + + {IT_STRING, "Match Race", "Pick your own settings in a specialized single race.", + NULL, NULL, 80, 0}, + + {IT_STRING, "Time Attack", "Race against ghosts for the best time, no fluff.", + NULL, NULL, 112, 0}, + + {IT_STRING | IT_CALL, "Back", NULL, NULL, M_GoBack, 160, 0}, +}; + +menu_t PLAY_RaceGamemodesDef = KARTGAMEMODEMENU(PLAY_RaceGamemodesMenu, &PLAY_GamemodesDef); + +// ------------------- +// In-game/pause menus +// ------------------- + +menuitem_t PAUSE_PlaybackMenu[] = +{ + {IT_CALL | IT_STRING, "Hide Menu", NULL, "M_PHIDE", M_SelectableClearMenus, 0, 0}, + + {IT_CALL | IT_STRING, "Rewind", NULL, "M_PREW", M_PlaybackRewind, 20, 0}, + {IT_CALL | IT_STRING, "Pause", NULL, "M_PPAUSE", M_PlaybackPause, 36, 0}, + {IT_CALL | IT_STRING, "Fast-Forward", NULL, "M_PFFWD", M_PlaybackFastForward, 52, 0}, + + {IT_CALL | IT_STRING, "Backup Frame", NULL, "M_PSTEPB", M_PlaybackRewind, 20, 0}, + {IT_CALL | IT_STRING, "Resume", NULL, "M_PRESUM", M_PlaybackPause, 36, 0}, + {IT_CALL | IT_STRING, "Advance Frame", NULL, "M_PFADV", M_PlaybackAdvance, 52, 0}, + + {IT_ARROWS | IT_STRING, "View Count", NULL, "M_PVIEWS", M_PlaybackSetViews, 72, 0}, + {IT_ARROWS | IT_STRING, "Viewpoint", NULL, "M_PNVIEW", M_PlaybackAdjustView, 88, 0}, + {IT_ARROWS | IT_STRING, "Viewpoint 2", NULL, "M_PNVIEW", M_PlaybackAdjustView, 104, 0}, + {IT_ARROWS | IT_STRING, "Viewpoint 3", NULL, "M_PNVIEW", M_PlaybackAdjustView, 120, 0}, + {IT_ARROWS | IT_STRING, "Viewpoint 4", NULL, "M_PNVIEW", M_PlaybackAdjustView, 136, 0}, + + {IT_CALL | IT_STRING, "Stop Playback", NULL, "M_PEXIT", M_PlaybackQuit, 156, 0}, +}; + +menu_t PAUSE_PlaybackMenuDef = { + sizeof (PAUSE_PlaybackMenu) / sizeof (menuitem_t), + NULL, + 0, + PAUSE_PlaybackMenu, + BASEVIDWIDTH/2 - 88, 2, + 0, 0, + M_DrawPlaybackMenu, + NULL +}; diff --git a/src/k_menudraw.c b/src/k_menudraw.c new file mode 100644 index 000000000..abf696764 --- /dev/null +++ b/src/k_menudraw.c @@ -0,0 +1,844 @@ +/// \file k_menudraw.c +/// \brief SRB2Kart's menu drawer functions + +#ifdef __GNUC__ +#include +#endif + +#include "k_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 "doomstat.h" // MAXSPLITSCREENPLAYERS + +#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 + +// flags for text highlights +#define highlightflags V_ORANGEMAP +#define recommendedflags V_GREENMAP +#define warningflags V_GRAYMAP + +#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 + +// +// 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 (menuwipe) + F_WipeStartScreen(); + + if (menuactive) + { + if (gamestate == GS_MENU) // draw BG + V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUBG", PU_CACHE), NULL); + else if (!WipeInAction && currentMenu != &PAUSE_PlaybackMenuDef) + V_DrawFadeScreen(0xFF00, 16); // now that's more readable with a faded background (yeah like Quake...) + + if (currentMenu->drawroutine) + currentMenu->drawroutine(); // call current menu Draw routine + + // draw non-green resolution border + if ((gamestate == GS_MENU) && ((vid.width % BASEVIDWIDTH != 0) || (vid.height % BASEVIDHEIGHT != 0))) + V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("WEIRDRES", PU_CACHE), NULL); + + // 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 + } + } + } + + if (menuwipe) + { + F_WipeEndScreen(); + F_RunWipe(wipedefs[wipe_menu_final], false, "FADEMAP0", true); + menuwipe = false; + } + + // 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"); + } +} + +// ========================================================================== +// GENERIC MENUS +// ========================================================================== + +// +// M_DrawMenuTooltips +// +// Draw a banner across the top of the screen, with a description of the current option displayed +// +static void M_DrawMenuTooltips(void) +{ + V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL); + if (currentMenu->menuitems[itemOn].tooltip != NULL) + V_DrawCenteredThinString(BASEVIDWIDTH/2, 12, V_ALLOWLOWERCASE|V_6WIDTHSPACE, currentMenu->menuitems[itemOn].tooltip); +} + +// +// M_DrawMenuPreviews +// +// Draw a box with a preview image of the current option +// +static void M_DrawMenuPreviews(void) +{ + V_DrawFixedPatch(172<menuitems[itemOn].patch != NULL) + V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName(currentMenu->menuitems[itemOn].patch, PU_CACHE), NULL); +} + +// 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; +} + +// +// M_DrawGenericMenu +// +// Default, generic text-based list menu, used for Options +// +void M_DrawGenericMenu(void) +{ + INT32 x = 0, y = 0, w, i, cursory = 0; + + M_DrawMenuTooltips(); + + 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].mvar1;//+= LINEHEIGHT; + break; +#if 0 + case IT_BIGSLIDER: + M_DrawThermo(x, y, (consvar_t *)currentMenu->menuitems[i].itemaction); + y += LINEHEIGHT; + break; +#endif + case IT_STRING: + case IT_WHITESTRING: + if (currentMenu->menuitems[i].mvar1) + y = currentMenu->y+currentMenu->menuitems[i].mvar1; + 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) + { +#if 0 + 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; +#endif + 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].mvar1) + y = currentMenu->y+currentMenu->menuitems[i].mvar1; + /* FALLTHRU */ + case IT_TRANSTEXT2: + V_DrawString(x, y, V_TRANSLUCENT, currentMenu->menuitems[i].text); + y += SMALLLINEHEIGHT; + break; + case IT_QUESTIONMARKS: + if (currentMenu->menuitems[i].mvar1) + y = currentMenu->y+currentMenu->menuitems[i].mvar1; + + 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].mvar1) + y = currentMenu->y+currentMenu->menuitems[i].mvar1; + + 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); + } +} + +// +// M_DrawKartGamemodeMenu +// +// Huge gamemode-selection list for main menu +// +void M_DrawKartGamemodeMenu(void) +{ + INT16 i, x = 170; + + M_DrawMenuTooltips(); + M_DrawMenuPreviews(); + + if (menutransition.tics) + x -= 24 * menutransition.tics; + + for (i = 0; i < currentMenu->numitems; i++) + { + switch (currentMenu->menuitems[i].status & IT_DISPLAY) + { + case IT_STRING: + V_DrawRightAlignedGamemodeString(x, currentMenu->menuitems[i].mvar1, 0, currentMenu->menuitems[i].text, + (i == itemOn) ? SKINCOLOR_PLAGUE : SKINCOLOR_PIGEON); + break; + } + } +} + +#define MAXMSGLINELEN 256 + +// +// 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, 159); + //V_DrawFill(x+8, y+8, width*8, boxlines*8, 31); +} + +// +// M_DrawMessageMenu +// +// Generic message prompt +// +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); + + 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); + } +} + +// Draw an Image Def. Aka, Help images. +// Defines what image is used in (menuitem_t)->patch. +// You can even put multiple images in one menu! +void M_DrawImageDef(void) +{ + // Grr. Need to autodetect for pic_ts. + pic_t *pictest = (pic_t *)W_CachePatchName(currentMenu->menuitems[itemOn].patch, PU_CACHE); + if (!pictest->zero) + V_DrawScaledPic(0,0,0,W_GetNumForName(currentMenu->menuitems[itemOn].patch)); + else + { + patch_t *patch = W_CachePatchName(currentMenu->menuitems[itemOn].patch, PU_CACHE); + if (patch->height <= BASEVIDHEIGHT) + V_DrawScaledPatch(0,0,0,patch); + else + V_DrawSmallScaledPatch(0,0,0,patch); + } + + if (currentMenu->menuitems[itemOn].mvar1) + { + 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"); + } +} + +// +// PLAY MENUS +// + +static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) +{ + const fixed_t rad = 32<mdepth == 2) + numoptions = setup_chargrid[p->gridx][p->gridy].numskins; + else + { + numoptions = 16; + patch = W_CachePatchName("COLORSPH", PU_CACHE); + } + + angamt /= numoptions*2; + + for (i = 0; i < numoptions; i++) + { + fixed_t cx = x << FRACBITS, cy = y << FRACBITS; + fixed_t ang = -(angamt * i); + UINT8 *colormap; + INT16 n; + + ang += 90; + + cx += FixedMul(rad, FINECOSINE(FixedAngle(ang << FRACBITS) >> ANGLETOFINESHIFT)); + cy += FixedMul(rad, FINESINE(FixedAngle(ang << FRACBITS) >> ANGLETOFINESHIFT)) >> 2; + + if (p->mdepth == 2) + { + SINT8 skin; + + cx -= 8<clonenum + i) % setup_chargrid[p->gridx][p->gridy].numskins; + + skin = setup_chargrid[p->gridx][p->gridy].skinlist[n]; + + colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE); + patch = facerankprefix[skin]; + } + else + { + cx -= 4<color + i) % (MAXSKINCOLORS-1)); + colormap = R_GetTranslationColormap(TC_DEFAULT, n, GTC_MENUCACHE); + } + + V_DrawFixedPatch(cx, cy, FRACUNIT, 0, patch, colormap); + } +} + +static void M_DrawCharSelectPreview(UINT8 num) +{ + INT16 x = 11, y = 5; + char letter = 'A' + num; + SINT8 skin; + UINT8 color; + UINT8 *colormap; + patch_t *sprpatch; + spritedef_t *sprdef; + spriteframe_t *sprframe; + setup_player_t *p = &setup_player[num]; + + if (num & 1) + x += 233; + + if (num > 1) + y += 99; + + V_DrawScaledPatch(x, y+6, V_TRANSLUCENT, W_CachePatchName("PREVBACK", PU_CACHE)); + + if (p->mdepth > 0) + { + skin = setup_chargrid[p->gridx][p->gridy].skinlist[p->clonenum]; + + color = p->color; + colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); + + if (skin != -1) + { + statenum_t st = S_KART_WALK1; + UINT32 flags = 0; + UINT32 frame; + + if (skullAnimCounter & 1) + st++; + + sprdef = &skins[skin].spritedef; + + if (sprdef->numframes) // No frames ?? + { + frame = states[st].frame & FF_FRAMEMASK; + if (frame >= sprdef->numframes) // Walking animation missing + frame = 0; // Try to use standing frame + + sprframe = &sprdef->spriteframes[frame]; + sprpatch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE); + if (sprframe->flip & 1) // Only for first sprite + flags |= V_FLIP; // This sprite is left/right flipped! + + // Flip for left-side players + if (!(num & 1)) + flags ^= V_FLIP; + + if (skins[skin].flags & SF_HIRES) + { + V_DrawFixedPatch((x+32)<mdepth == 2 || p->mdepth == 3) + M_DrawCharSelectCircle(p, x+32, y+48); + } + + V_DrawScaledPatch(x+9, y+2, 0, W_CachePatchName("FILEBACK", PU_CACHE)); + V_DrawScaledPatch(x, y, 0, W_CachePatchName(va("CHARSEL%c", letter), PU_CACHE)); + + if (p->mdepth == 0 && (skullAnimCounter/5)) + V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PSTART", PU_CACHE)); +} + +void M_DrawCharacterSelect(void) +{ + UINT8 i, j, k; + UINT16 quadx, quady; + SINT8 skin; + + // We have to loop twice -- first time to draw the drop shadows, a second time to draw the icons. + for (i = 0; i < 9; i++) + { + for (j = 0; j < 9; j++) + { + skin = setup_chargrid[i][j].skinlist[0]; + quadx = 4 * (i / 3); + quady = 4 * (j / 3); + + // Here's a quick little cheat to save on drawing time! + // Don't draw a shadow if it'll get covered by another icon + if ((i % 3 < 2) && (j % 3 < 2)) + { + if ((setup_chargrid[i+1][j].skinlist[0] != -1) + && (setup_chargrid[i][j+1].skinlist[0] != -1) + && (setup_chargrid[i+1][j+1].skinlist[0] != -1)) + continue; + } + + if (skin != -1) + V_DrawScaledPatch(82 + (i*16) + quadx + 1, 22 + (j*16) + quady + 1, 0, W_CachePatchName("ICONBACK", PU_CACHE)); + } + } + + // Draw this inbetween. These drop shadows should be covered by the stat graph, but the icons shouldn't. + V_DrawScaledPatch(3, 2, 0, W_CachePatchName("STATGRPH", PU_CACHE)); + + // Draw the icons now + for (i = 0; i < 9; i++) + { + for (j = 0; j < 9; j++) + { + for (k = 0; k < setup_numplayers; k++) + { + if (setup_player[k].gridx == i && setup_player[k].gridy == j) + break; // k == setup_numplayers means no one has it selected + } + + skin = setup_chargrid[i][j].skinlist[0]; + quadx = 4 * (i / 3); + quady = 4 * (j / 3); + + if (skin != -1) + { + UINT8 *colormap; + + if (k == setup_numplayers) + colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_MENUCACHE); + else + colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE); + + V_DrawMappedPatch(82 + (i*16) + quadx, 22 + (j*16) + quady, 0, facerankprefix[skin], colormap); + } + } + } + + // Draw a preview for each player + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + M_DrawCharSelectPreview(i); +} + +// +// INGAME / PAUSE MENUS +// +void M_DrawPlaybackMenu(void) +{ + INT16 i; + patch_t *icon = NULL; + UINT8 *activemap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GOLD, GTC_MENUCACHE); + + // Toggle items + if (paused && !demo.rewinding) + { + PAUSE_PlaybackMenu[playback_pause].status = PAUSE_PlaybackMenu[playback_fastforward].status = PAUSE_PlaybackMenu[playback_rewind].status = IT_DISABLED; + PAUSE_PlaybackMenu[playback_resume].status = PAUSE_PlaybackMenu[playback_advanceframe].status = PAUSE_PlaybackMenu[playback_backframe].status = IT_CALL|IT_STRING; + + if (itemOn >= playback_rewind && itemOn <= playback_fastforward) + itemOn += playback_backframe - playback_rewind; + } + else + { + PAUSE_PlaybackMenu[playback_pause].status = PAUSE_PlaybackMenu[playback_fastforward].status = PAUSE_PlaybackMenu[playback_rewind].status = IT_CALL|IT_STRING; + PAUSE_PlaybackMenu[playback_resume].status = PAUSE_PlaybackMenu[playback_advanceframe].status = PAUSE_PlaybackMenu[playback_backframe].status = IT_DISABLED; + + if (itemOn >= playback_backframe && itemOn <= playback_advanceframe) + itemOn -= playback_backframe - playback_rewind; + } + + if (modeattacking) + { + for (i = playback_viewcount; i <= playback_view4; i++) + PAUSE_PlaybackMenu[i].status = IT_DISABLED; + + //PAUSE_PlaybackMenu[playback_moreoptions].mvar1 = 72; + //PAUSE_PlaybackMenu[playback_quit].mvar1 = 88; + PAUSE_PlaybackMenu[playback_quit].mvar1 = 72; + + //currentMenu->x = BASEVIDWIDTH/2 - 52; + currentMenu->x = BASEVIDWIDTH/2 - 44; + } + else + { + PAUSE_PlaybackMenu[playback_viewcount].status = IT_ARROWS|IT_STRING; + + for (i = 0; i <= splitscreen; i++) + PAUSE_PlaybackMenu[playback_view1+i].status = IT_ARROWS|IT_STRING; + for (i = splitscreen+1; i < 4; i++) + PAUSE_PlaybackMenu[playback_view1+i].status = IT_DISABLED; + + //PAUSE_PlaybackMenu[playback_moreoptions].mvar1 = 156; + //PAUSE_PlaybackMenu[playback_quit].mvar1 = 172; + PAUSE_PlaybackMenu[playback_quit].mvar1 = 156; + + //currentMenu->x = BASEVIDWIDTH/2 - 94; + currentMenu->x = BASEVIDWIDTH/2 - 88; + } + + // wip + //M_DrawTextBox(currentMenu->x-68, currentMenu->y-7, 15, 15); + //M_DrawCenteredMenu(); + + for (i = 0; i < currentMenu->numitems; i++) + { + UINT8 *inactivemap = NULL; + + if (i >= playback_view1 && i <= playback_view4) + { + if (modeattacking) continue; + + if (splitscreen >= i - playback_view1) + { + INT32 ply = displayplayers[i - playback_view1]; + + icon = facerankprefix[players[ply].skin]; + if (i != itemOn) + inactivemap = R_GetTranslationColormap(players[ply].skin, players[ply].skincolor, GTC_MENUCACHE); + } + else if (currentMenu->menuitems[i].patch && W_CheckNumForName(currentMenu->menuitems[i].patch) != LUMPERROR) + icon = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE); + } + else if (currentMenu->menuitems[i].status == IT_DISABLED) + continue; + else if (currentMenu->menuitems[i].patch && W_CheckNumForName(currentMenu->menuitems[i].patch) != LUMPERROR) + icon = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE); + + if ((i == playback_fastforward && cv_playbackspeed.value > 1) || (i == playback_rewind && demo.rewinding)) + V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].mvar1, currentMenu->y, V_SNAPTOTOP, icon, R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_JAWZ, GTC_MENUCACHE)); + else + V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].mvar1, currentMenu->y, V_SNAPTOTOP, icon, (i == itemOn) ? activemap : inactivemap); + + if (i == itemOn) + { + V_DrawCharacter(currentMenu->x + currentMenu->menuitems[i].mvar1 + 4, currentMenu->y + 14, + '\x1A' | V_SNAPTOTOP|highlightflags, false); + + V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y + 18, V_SNAPTOTOP|V_ALLOWLOWERCASE, currentMenu->menuitems[i].text); + + if ((currentMenu->menuitems[i].status & IT_TYPE) == IT_ARROWS) + { + char *str; + + if (!(i == playback_viewcount && splitscreen == 3)) + V_DrawCharacter(BASEVIDWIDTH/2 - 4, currentMenu->y + 28 - (skullAnimCounter/5), + '\x1A' | V_SNAPTOTOP|highlightflags, false); // up arrow + + if (!(i == playback_viewcount && splitscreen == 0)) + V_DrawCharacter(BASEVIDWIDTH/2 - 4, currentMenu->y + 48 + (skullAnimCounter/5), + '\x1B' | V_SNAPTOTOP|highlightflags, false); // down arrow + + switch (i) + { + case playback_viewcount: + str = va("%d", splitscreen+1); + break; + + case playback_view1: + case playback_view2: + case playback_view3: + case playback_view4: + str = player_names[displayplayers[i - playback_view1]]; // 0 to 3 + break; + + default: // shouldn't ever be reached but whatever + continue; + } + + V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y + 38, V_SNAPTOTOP|V_ALLOWLOWERCASE|highlightflags, str); + } + } + } +} diff --git a/src/k_menufunc.c b/src/k_menufunc.c new file mode 100644 index 000000000..10dd84f2b --- /dev/null +++ b/src/k_menufunc.c @@ -0,0 +1,2021 @@ +/// \file k_menufunc.c +/// \brief SRB2Kart's menu functions + +#ifdef __GNUC__ +#include +#endif + +#include "k_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 "doomstat.h" // MAXSPLITSCREENPLAYERS + +#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 + +// ========================================================================== +// GLOBAL VARIABLES +// ========================================================================== + +boolean menuactive = false; +boolean fromlevelselect = false; + +// current menudef +menu_t *currentMenu = &MainDef; + +char dummystaffname[22]; + +INT16 itemOn = 0; // menu item skull is on, Hack by Tails 09-18-2002 +INT16 skullAnimCounter = 10; // skull animation counter +struct menutransition_s menutransition; // Menu transition properties + +// finish wipes between screens +boolean menuwipe = false; + +// 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; + +// ========================================================================== +// CONSOLE VARIABLES AND THEIR POSSIBLE VALUES GO HERE. +// ========================================================================== + +// 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); + +consvar_t cv_showfocuslost = {"showfocuslost", "Yes", CV_SAVE, CV_YesNo, NULL, 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}; + +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}; + +//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 CV_PossibleValue_t map_cons_t[] = { + {0,"MIN"}, + {NUMMAPS, "MAX"}, + {0, NULL} +}; +consvar_t cv_nextmap = {"nextmap", "1", CV_HIDDEN|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_HIDDEN|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 dehacked.c and doomstat.h! +CV_PossibleValue_t gametype_cons_t[NUMGAMETYPES+1]; +consvar_t cv_newgametype = {"newgametype", "Race", CV_HIDDEN|CV_CALL, gametype_cons_t, Newgametype_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 consvar_t cv_dummymenuplayer = {"dummymenuplayer", "P1", CV_HIDDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange, 0, NULL, NULL, 0, 0, NULL}; + +static CV_PossibleValue_t dummyteam_cons_t[] = {{0, "Spectator"}, {1, "Red"}, {2, "Blue"}, {0, NULL}}; +static consvar_t cv_dummyteam = {"dummyteam", "Spectator", CV_HIDDEN, dummyteam_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; + +static CV_PossibleValue_t dummyspectate_cons_t[] = {{0, "Spectator"}, {1, "Playing"}, {0, NULL}}; +static consvar_t cv_dummyspectate = {"dummyspectate", "Spectator", CV_HIDDEN, dummyspectate_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; + +static CV_PossibleValue_t dummyscramble_cons_t[] = {{0, "Random"}, {1, "Points"}, {0, NULL}}; +static consvar_t cv_dummyscramble = {"dummyscramble", "Random", CV_HIDDEN, dummyscramble_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; + +static CV_PossibleValue_t dummystaff_cons_t[] = {{0, "MIN"}, {100, "MAX"}, {0, NULL}}; +static consvar_t cv_dummystaff = {"dummystaff", "0", CV_HIDDEN|CV_CALL, dummystaff_cons_t, Dummystaff_OnChange, 0, NULL, NULL, 0, 0, NULL}; + +// ========================================================================== +// CVAR ONCHANGE EVENTS GO HERE +// ========================================================================== +// (there's only a couple anyway) + +// Prototypes +#if 0 +static INT32 M_FindFirstMap(INT32 gtype); +static INT32 M_GetFirstLevelInList(void); +#endif + +// Nextmap. Used for Time Attack. +static void Nextmap_OnChange(void) +{ + char *leveltitle; + + // 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 0 + 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; + UINT8 active; + + 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); + } +#endif +} + +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 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 0 + 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)) + || (cv_newgametype.value == GT_MATCH && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_MATCH))) + { + INT32 value = 0; + + switch (cv_newgametype.value) + { + default: + case GT_RACE: + value = TOL_RACE; + break; + case GT_MATCH: + value = TOL_MATCH; + break; + } + + CV_SetValue(&cv_nextmap, M_FindFirstMap(value)); + } + } +#endif +} + +void Screenshot_option_Onchange(void) +{ +#if 0 + OP_ScreenshotOptionsMenu[op_screenshot_folder].status = + (cv_screenshot_option.value == 3 ? IT_CVAR|IT_STRING|IT_CV_STRING : IT_DISABLED); +#endif +} + +void Moviemode_mode_Onchange(void) +{ +#if 0 + 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; +#endif +} + +void Addons_option_Onchange(void) +{ +#if 0 + OP_AddonsOptionsMenu[op_addons_folder].status = + (cv_addons_option.value == 3 ? IT_CVAR|IT_STRING|IT_CV_STRING : IT_DISABLED); +#endif +} + +void M_SortServerList(void) +{ +#if 0 +#ifndef NONET + switch(cv_serversort.value) + { + case 0: // Ping. + qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_time); + break; + case 1: // Modified state. + qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_modified); + break; + case 2: // Most players. + qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer_reverse); + break; + case 3: // Least players. + qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer); + break; + case 4: // Max players. + qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_maxplayer_reverse); + break; + case 5: // Gametype. + qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametype); + break; + } +#endif +#endif +} + +// ========================================================================= +// 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; + else if (cv == &cv_maxping) + choice *= 50; +#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_s3k5b); // 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_s3k5b); // Tails + CV_Set(cv, ""); + } + return true; + default: + if (choice >= 32 && choice <= 127) + { + len = strlen(cv->string); + if (len < MAXSTRINGLENGTH - 1) + { + S_StartSound(NULL, sfx_s3k5b); // 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); +} + +// +// 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-1) * cv_deadzone.value) >> FRACBITS; + 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; + } + } + } + + 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) + { +#if 0 + 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, false); + 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 +#endif + 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].mvar1 != 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; + } + + if (currentMenu == &PAUSE_PlaybackMenuDef) + { + // Flip left/right with up/down for the playback menu, since it's a horizontal icon row. + switch (ch) + { + case KEY_LEFTARROW: ch = KEY_UPARROW; break; + case KEY_UPARROW: ch = KEY_RIGHTARROW; break; + case KEY_RIGHTARROW: ch = KEY_DOWNARROW; break; + case KEY_DOWNARROW: ch = KEY_LEFTARROW; break; + default: break; + } + } + + // Keys usable within menu + switch (ch) + { + case KEY_DOWNARROW: + M_NextOpt(); + S_StartSound(NULL, sfx_s3k5b); + return true; + + case KEY_UPARROW: + M_PrevOpt(); + S_StartSound(NULL, sfx_s3k5b); + return true; + + case KEY_LEFTARROW: + if (routine && ((currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_ARROWS + || (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_CVAR)) + { +#if 0 + if (currentMenu != &OP_SoundOptionsDef || itemOn > 3) +#endif + S_StartSound(NULL, sfx_s3k5b); + 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 0 + if (currentMenu != &OP_SoundOptionsDef || itemOn > 3) +#endif + S_StartSound(NULL, sfx_s3k5b); + routine(1); + } + return true; + + case KEY_ENTER: + noFurtherInput = true; + currentMenu->lastOn = itemOn; + +#if 0 + if (currentMenu == &PAUSE_PlaybackMenuDef) + { + boolean held = (boolean)playback_enterheld; + playback_enterheld = TICRATE/7; + if (held) + return true; + } +#endif + + if (routine) + { + S_StartSound(NULL, sfx_s3k5b); + + 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) + { + 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; + } + } + + 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, false); + break; + } + } + + return true; + + case KEY_ESCAPE: + //case KEY_JOY1 + 2: + M_GoBack(0); + return true; + + case KEY_BACKSPACE: +#if 0 + if ((currentMenu->menuitems[itemOn].status) == IT_CONTROL) + { + // detach any keys associated with the game control + G_ClearControlKeys(setupcontrols, currentMenu->menuitems[itemOn].mvar1); + S_StartSound(NULL, sfx_shldls); + return true; + } +#endif + + 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 0 + if (currentMenu != &OP_SoundOptionsDef || itemOn > 3) +#endif + S_StartSound(NULL, sfx_s3k5b); + + routine(-1); + return true; + } + + return false; + + default: + break; + } + + return true; +} + +// +// M_StartControlPanel +// +void M_StartControlPanel(void) +{ + // intro might call this repeatedly + if (menuactive) + { + CON_ToggleOff(); // move away console + return; + } + + if (gamestate == GS_TITLESCREEN) // Set up menu state + { + G_SetGamestate(GS_MENU); + + gameaction = ga_nothing; + paused = false; + CON_ToggleOff(); + + S_ChangeMusicInternal("menu", true); + } + + menuactive = true; + + if (demo.playback) + { + currentMenu = &PAUSE_PlaybackMenuDef; + } + else if (!Playing()) + { + currentMenu = &MainDef; + itemOn = 0; + } +#if 0 + else if (modeattacking) + { + currentMenu = &MAPauseDef; + itemOn = mapause_continue; + } + else if (!(netgame || multiplayer)) // Single Player + { + if (gamestate != GS_LEVEL) // intermission, so gray out stuff. + SPauseMenu[spause_retry].status = IT_GRAYEDOUT; + else + { + //INT32 numlives = 2; + + /*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); + } + + 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].mvar1 = 48; + MPauseMenu[mpause_switchspectate].mvar1 = 48; + MPauseMenu[mpause_options].mvar1 = 64; + MPauseMenu[mpause_title].mvar1 = 80; + MPauseMenu[mpause_quit].mvar1 = 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].mvar1 += ((splitscreen+1) * 8); + MPauseMenu[mpause_options].mvar1 += 8; + MPauseMenu[mpause_title].mvar1 += 8; + MPauseMenu[mpause_quit].mvar1 += 8; + } + else if (G_GametypeHasSpectators()) + { + MPauseMenu[mpause_switchspectate].status = IT_STRING | IT_SUBMENU; + MPauseMenu[mpause_switchspectate].mvar1 += ((splitscreen+1) * 8); + MPauseMenu[mpause_options].mvar1 += 8; + MPauseMenu[mpause_title].mvar1 += 8; + MPauseMenu[mpause_quit].mvar1 += 8; + } + } + + if (splitscreen > 1) + { + MPauseMenu[mpause_psetupsplit3].status = IT_STRING | IT_CALL; + + MPauseMenu[mpause_options].mvar1 += 8; + MPauseMenu[mpause_title].mvar1 += 8; + MPauseMenu[mpause_quit].mvar1 += 8; + + if (splitscreen > 2) + { + MPauseMenu[mpause_psetupsplit4].status = IT_STRING | IT_CALL; + MPauseMenu[mpause_options].mvar1 += 8; + MPauseMenu[mpause_title].mvar1 += 8; + MPauseMenu[mpause_quit].mvar1 += 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; + } +#endif + + CON_ToggleOff(); // move away console +} + +// +// 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 + + if (gamestate == GS_MENU) // Back to title screen + D_StartTitle(); + + menuactive = false; +} + +void M_SelectableClearMenus(INT32 choice) +{ + (void)choice; + M_ClearMenus(true); +} + +// +// M_SetupNextMenu +// +void M_SetupNextMenu(menu_t *menudef, boolean notransition) +{ + INT16 i; + + if (!notransition) + { + if (currentMenu->transitionOutTics) + { + menutransition.tics = 0; + menutransition.dest = currentMenu->transitionOutTics; + menutransition.in = false; + menutransition.newmenu = menudef; + return; // Don't change menu yet, the transition will call this again + } + else if (gamestate == GS_MENU) + { + menuwipe = true; + F_WipeStartScreen(); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeEndScreen(); + F_RunWipe(wipedefs[wipe_menu_toblack], false, "FADEMAP0", false); + } + } + + 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; + } + } + } +} + +void M_GoBack(INT32 choice) +{ + (void)choice; + + 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; + } + + M_SetupNextMenu(currentMenu->prevMenu, false); + } + else + M_ClearMenus(true); +} + +// +// M_Ticker +// +void M_Ticker(void) +{ + if (menutransition.tics != 0 || menutransition.dest != 0) + { + noFurtherInput = true; + + if (menutransition.tics < menutransition.dest) + menutransition.tics++; + else if (menutransition.tics > menutransition.dest) + menutransition.tics--; + + // If dest is non-zero, we've started transition and want to switch menus + // If dest is zero, we're mid-transition and want to end it + if (menutransition.tics == menutransition.dest && menutransition.newmenu != NULL) + { + M_SetupNextMenu(menutransition.newmenu, true); + + if (menutransition.newmenu->transitionInTics) + { + menutransition.tics = currentMenu->transitionOutTics; + menutransition.dest = 0; + menutransition.in = true; + menutransition.newmenu = NULL; + } + else if (gamestate == GS_MENU) + { + memset(&menutransition, 0, sizeof(menutransition)); + + menuwipe = true; + F_WipeStartScreen(); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeEndScreen(); + F_RunWipe(wipedefs[wipe_menu_toblack], false, "FADEMAP0", false); + } + } + } + else + { + // reset input trigger + noFurtherInput = false; + } + + if (dedicated) + return; + + if (--skullAnimCounter <= 0) + skullAnimCounter = 8; + +#if 0 + if (currentMenu == &PAUSE_PlaybackMenuDef) + { + if (playback_enterheld > 0) + playback_enterheld--; + } + else + playback_enterheld = 0; + + //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; + } +#endif +} + +// +// M_Init +// +void M_Init(void) +{ + //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_dummystaff); + +#if 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_kartman].status = + OP_VideoOptionsMenu[op_video_md2] .status = IT_DISABLED; +#endif +#endif + +#ifndef NONET + CV_RegisterVar(&cv_serversort); +#endif + + //todo put this somewhere better... + CV_RegisterVar(&cv_allcaps); +} + +// ================================================== +// MESSAGE BOX (aka: a hacked, cobbled together menu) +// ================================================== +// Because this is just a "fake" menu, these definitions are not with the others +static menuitem_t MessageMenu[] = +{ + // TO HACK + {0, NULL, NULL, NULL, NULL, 0, 0} +}; + +menu_t MessageDef = +{ + 1, // # of menu items + NULL, // previous menu (TO HACK) + 0, // lastOn, flags (TO HACK) + MessageMenu, // menuitem_t -> + 0, 0, // x, y (TO HACK) + 0, 0, // transition tics + M_DrawMessageMenu, // drawing routine -> + NULL +}; + +// +// 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; +} + +// default message handler +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 = ((demo.playback) ? &PAUSE_PlaybackMenuDef : &MainDef); + else + MessageDef.prevMenu = currentMenu; + + MessageDef.menuitems[0].text = message; + MessageDef.menuitems[0].mvar1 = (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; +} + +void M_StopMessage(INT32 choice) +{ + (void)choice; + if (menuactive) + M_SetupNextMenu(MessageDef.prevMenu, true); +} + +// ========= +// IMAGEDEFS +// ========= + +// Handles the ImageDefs. Just a specialized function that +// uses left and right movement. +void M_HandleImageDef(INT32 choice) +{ + boolean exitmenu = false; + + switch (choice) + { + case KEY_RIGHTARROW: + if (itemOn >= (INT16)(currentMenu->numitems-1)) + break; + S_StartSound(NULL, sfx_s3k5b); + itemOn++; + break; + + case KEY_LEFTARROW: + if (!itemOn) + break; + + S_StartSound(NULL, sfx_s3k5b); + itemOn--; + break; + + case KEY_ESCAPE: + case KEY_ENTER: + exitmenu = true; + break; + } + + if (exitmenu) + { + if (currentMenu->prevMenu) + M_SetupNextMenu(currentMenu->prevMenu, false); + else + M_ClearMenus(true); + } +} + +// ========= +// MAIN MENU +// ========= + +// 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(); +} + +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("Are you sure you want to quit playing?\n\n(Press 'Y' to exit)", M_QuitResponse, MM_YESNO); +} + +// ========= +// PLAY MENU +// ========= + +// Character Select! +struct setup_chargrid_s setup_chargrid[9][9]; +setup_player_t setup_player[MAXSPLITSCREENPLAYERS]; +UINT8 setup_numplayers = 0; +consvar_t *setup_playercvars[MAXSPLITSCREENPLAYERS][SPLITCV_MAX]; + +void M_CharacterSelectInit(INT32 choice) +{ + UINT8 i, j; + + (void)choice; + + memset(setup_chargrid, -1, sizeof(setup_chargrid)); + memset(setup_player, 0, sizeof(setup_player)); + + for (i = 0; i < 9; i++) + { + for (j = 0; j < 9; j++) + setup_chargrid[i][j].numskins = 0; + } + + // Keep these in a table for the sake of my sanity later + setup_playercvars[0][SPLITCV_SKIN] = &cv_skin; + setup_playercvars[1][SPLITCV_SKIN] = &cv_skin2; + setup_playercvars[2][SPLITCV_SKIN] = &cv_skin3; + setup_playercvars[3][SPLITCV_SKIN] = &cv_skin4; + + setup_playercvars[0][SPLITCV_COLOR] = &cv_playercolor; + setup_playercvars[1][SPLITCV_COLOR] = &cv_playercolor2; + setup_playercvars[2][SPLITCV_COLOR] = &cv_playercolor3; + setup_playercvars[3][SPLITCV_COLOR] = &cv_playercolor4; + + setup_playercvars[0][SPLITCV_NAME] = &cv_playername; + setup_playercvars[1][SPLITCV_NAME] = &cv_playername2; + setup_playercvars[2][SPLITCV_NAME] = &cv_playername3; + setup_playercvars[3][SPLITCV_NAME] = &cv_playername4; + + for (i = 0; i < numskins; i++) + { + UINT8 x = skins[i].kartspeed-1; + UINT8 y = skins[i].kartweight-1; + + if (setup_chargrid[x][y].numskins >= MAXCLONES) + CONS_Alert(CONS_ERROR, "Max character alts reached for %d,%d\n", x+1, y+1); + else + { + setup_chargrid[x][y].skinlist[setup_chargrid[x][y].numskins] = i; + setup_chargrid[x][y].numskins++; + } + + for (j = 0; j < MAXSPLITSCREENPLAYERS; j++) + { + if (setup_playercvars[j][SPLITCV_SKIN]->value == i) + { + setup_player[j].gridx = x; + setup_player[j].gridy = y; + setup_player[j].color = skins[i].prefcolor; + } + } + } + + setup_player[0].mdepth = 1; + setup_numplayers = 1; + + M_SetupNextMenu(&PLAY_CharSelectDef, false); +} + +static void M_HandleCharacterGrid(INT32 choice, setup_player_t *p) +{ + switch (choice) + { + case KEY_DOWNARROW: + p->gridy++; + if (p->gridy > 8) + p->gridy = 0; + S_StartSound(NULL, sfx_s3k5b); + break; + case KEY_UPARROW: + p->gridy--; + if (p->gridy < 0) + p->gridy = 8; + S_StartSound(NULL, sfx_s3k5b); + break; + case KEY_RIGHTARROW: + p->gridx++; + if (p->gridx > 8) + p->gridx = 0; + S_StartSound(NULL, sfx_s3k5b); + break; + case KEY_LEFTARROW: + p->gridx--; + if (p->gridx < 0) + p->gridx = 8; + S_StartSound(NULL, sfx_s3k5b); + break; + case KEY_ENTER: + if (setup_chargrid[p->gridx][p->gridy].numskins == 0) + S_StartSound(NULL, sfx_s3k5b); + else + { + p->mdepth++; + if (setup_chargrid[p->gridx][p->gridy].numskins == 1) + p->mdepth++; // Skip clones menu + S_StartSound(NULL, sfx_s3k5b); + } + break; + case KEY_ESCAPE: + p->mdepth--; + S_StartSound(NULL, sfx_s3k5b); + break; + default: + break; + } +} + +static void M_HandleCharRotate(INT32 choice, setup_player_t *p) +{ + UINT8 numclones = setup_chargrid[p->gridx][p->gridy].numskins; + + switch (choice) + { + case KEY_RIGHTARROW: + p->clonenum++; + if (p->clonenum >= numclones) + p->clonenum = 0; + + //p->skin = setup_chargrid[p->gridx][p->gridy].skinlist[p->clonenum]; + S_StartSound(NULL, sfx_s3k5b); + break; + case KEY_LEFTARROW: + p->clonenum--; + if (p->clonenum < 0) + p->clonenum = numclones-1; + + //p->skin = setup_chargrid[p->gridx][p->gridy].skinlist[p->clonenum]; + S_StartSound(NULL, sfx_s3k5b); + break; + case KEY_ENTER: + p->mdepth++; + S_StartSound(NULL, sfx_s3k5b); + break; + case KEY_ESCAPE: + p->mdepth--; + S_StartSound(NULL, sfx_s3k5b); + break; + default: + break; + } +} + +static void M_HandleColorRotate(INT32 choice, setup_player_t *p) +{ + switch (choice) + { + case KEY_RIGHTARROW: + p->color++; + if (p->color >= MAXSKINCOLORS) + p->color = 1; + S_StartSound(NULL, sfx_s3k5b); + break; + case KEY_LEFTARROW: + p->color--; + if (p->color < 1) + p->color = MAXSKINCOLORS-1; + S_StartSound(NULL, sfx_s3k5b); + break; + case KEY_ENTER: + p->mdepth++; + S_StartSound(NULL, sfx_s3k5b); + break; + case KEY_ESCAPE: + p->mdepth--; + if (setup_chargrid[p->gridx][p->gridy].numskins <= 1) + p->mdepth--; // Skip clones menu + S_StartSound(NULL, sfx_s3k5b); + break; + default: + break; + } +} + +void M_CharacterSelectHandler(INT32 choice) +{ + UINT8 i; + + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + setup_player_t *p = &setup_player[i]; + + if (i > 0) + break; // temp + + switch (p->mdepth) + { + case 0: // Enter Game + if (choice == KEY_ENTER) + setup_player[i].mdepth++; + break; + case 1: // Character Select grid + M_HandleCharacterGrid(choice, p); + break; + case 2: // Select clone + M_HandleCharRotate(choice, p); + break; + case 3: // Select color + M_HandleColorRotate(choice, p); + break; + default: // Unready + if (choice == KEY_ESCAPE) + setup_player[i].mdepth--; + break; + } + + if (p->mdepth < 2) + p->clonenum = 0; + + // Just makes it easier to access later + p->skin = setup_chargrid[p->gridx][p->gridy].skinlist[p->clonenum]; + + if (p->mdepth < 3) + p->color = skins[p->skin].prefcolor; + + if (p->mdepth == 0) + break; + else + setup_numplayers = i+1; + } + + // If the first player unjoins, then we get outta here + if (setup_player[0].mdepth == 0) + { + if (currentMenu->prevMenu) + M_SetupNextMenu(currentMenu->prevMenu, false); + else + M_ClearMenus(true); + } + else + { + boolean setupnext = false; + + for (i = 0; i < setup_numplayers; i++) + { + if (setup_player[i].mdepth >= 4) + setupnext = true; + else + { + // Someone's not ready yet. + setupnext = false; + break; + } + } + + if (setupnext) + { + for (i = 0; i < setup_numplayers; i++) + { + CV_StealthSetValue(setup_playercvars[i][SPLITCV_SKIN], setup_player[i].skin); + CV_StealthSetValue(setup_playercvars[i][SPLITCV_COLOR], setup_player[i].color); + } + + M_SetupNextMenu(&PLAY_MainDef, false); + } + } +} + +boolean M_CharacterSelectQuit(void) +{ + M_CharacterSelectInit(0); + return true; +} + +// ===================== +// PAUSE / IN-GAME MENUS +// ===================== +void M_EndModeAttackRun(void) +{ +#if 0 + M_ModeAttackEndGame(0); +#endif +} + +// Replay Playback Menu +void M_SetPlaybackMenuPointer(void) +{ + itemOn = playback_pause; +} + +void M_PlaybackRewind(INT32 choice) +{ + static tic_t lastconfirmtime; + + (void)choice; + + if (!demo.rewinding) + { + if (paused) + { + G_ConfirmRewind(leveltime-1); + paused = true; + S_PauseAudio(); + } + else + demo.rewinding = paused = true; + } + else if (lastconfirmtime + TICRATE/2 < I_GetTime()) + { + lastconfirmtime = I_GetTime(); + G_ConfirmRewind(leveltime); + } + + CV_SetValue(&cv_playbackspeed, 1); +} + +void M_PlaybackPause(INT32 choice) +{ + (void)choice; + + paused = !paused; + + if (demo.rewinding) + { + G_ConfirmRewind(leveltime); + paused = true; + S_PauseAudio(); + } + else if (paused) + S_PauseAudio(); + else + S_ResumeAudio(); + + CV_SetValue(&cv_playbackspeed, 1); +} + +void M_PlaybackFastForward(INT32 choice) +{ + (void)choice; + + if (demo.rewinding) + { + G_ConfirmRewind(leveltime); + paused = false; + S_ResumeAudio(); + } + CV_SetValue(&cv_playbackspeed, cv_playbackspeed.value == 1 ? 4 : 1); +} + +void M_PlaybackAdvance(INT32 choice) +{ + (void)choice; + + paused = false; + TryRunTics(1); + paused = true; +} + +void M_PlaybackSetViews(INT32 choice) +{ + if (choice > 0) + { + if (splitscreen < 3) + G_AdjustView(splitscreen + 2, 0, true); + } + else if (splitscreen) + { + splitscreen--; + R_ExecuteSetViewSize(); + } +} + +void M_PlaybackAdjustView(INT32 choice) +{ + G_AdjustView(itemOn - playback_viewcount, (choice > 0) ? 1 : -1, true); +} + +void M_PlaybackQuit(INT32 choice) +{ + (void)choice; + G_StopDemo(); + + if (demo.inreplayhut) + M_ReplayHut(choice); + else if (modeattacking) + M_EndModeAttackRun(); + else + D_StartTitle(); +} + + +void M_ReplayHut(INT32 choice) +{ + (void)choice; +} + +static void Splitplayers_OnChange(void) +{ +#if 0 + if (cv_splitplayers.value < setupm_pselect) + setupm_pselect = 1; +#endif +} diff --git a/src/m_cheat.c b/src/m_cheat.c index bba7cd65b..6bab3c7bd 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -22,7 +22,7 @@ #include "d_net.h" #include "m_cheat.h" -#include "m_menu.h" +#include "k_menu.h" #include "m_random.h" #include "m_misc.h" diff --git a/src/m_menu.c b/src/m_menu.c deleted file mode 100644 index 342bbf4d5..000000000 --- a/src/m_menu.c +++ /dev/null @@ -1,9684 +0,0 @@ -// 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[MAXSKINS]; - -//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 boolean menuwipe = false; // finish wipes between screens - -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 - -menu_t PY_MainDef; - -// Extra - -static void M_Statistics(INT32 choice); -static char *M_GetConditionString(condition_t cond); - -menu_t EX_MainDef, EX_UnlockChecklistDef; -menu_t EX_LevelStatsDef; - -// 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_Credits(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_TimeAttack(INT32 choice); -static boolean M_QuitTimeAttackMenu(void); -static void M_HandleStaffReplay(INT32 choice); -static void M_ReplayTimeAttack(INT32 choice); -static void M_ChooseTimeAttack(INT32 choice); -static void M_ModeAttackRetry(INT32 choice); -static void M_ModeAttackEndGame(INT32 choice); -static void M_SetGuestReplay(INT32 choice); - -static menu_t SP_TimeAttackDef, SP_ReplayDef, SP_GuestReplayDef, SP_GhostDef; - -// 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 -menu_t EX_ReplayHutDef; -menu_t OP_ReplayOptionsDef; -static void M_HandleReplayHutList(INT32 choice); -static void M_DrawReplayHut(void); -static void M_DrawReplayStartMenu(void); -static boolean M_QuitReplayHut(void); -static void M_HutStartReplay(INT32 choice); - -static void M_DrawPlaybackMenu(void); -static void M_PlaybackRewind(INT32 choice); -static void M_PlaybackPause(INT32 choice); -static void M_PlaybackFastForward(INT32 choice); -static void M_PlaybackAdvance(INT32 choice); -static void M_PlaybackSetViews(INT32 choice); -static void M_PlaybackAdjustView(INT32 choice); -static void M_PlaybackQuit(INT32 choice); - -static UINT8 playback_enterheld = 0; // horrid hack to prevent holding the button from being extremely fucked - -// Drawing functions -static void M_DrawKartGamemodeMenu(void); -static void M_DrawGenericMenu(void); -static void M_DrawAddons(void); -static void M_DrawSoundOptions(void); -static void M_DrawChecklist(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_DrawLevelStats(void); -static void M_DrawTimeAttackMenu(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_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 dehacked.c and doomstat.h! -CV_PossibleValue_t gametype_cons_t[NUMGAMETYPES+1]; - -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, "Play", &PY_MainDef, 48}, - {IT_SUBMENU|IT_STRING, NULL, "Extra", &EX_MainDef, 80}, - {IT_CALL|IT_STRING, NULL, "Options", M_Options, 112}, - {IT_CALL|IT_STRING, NULL, "Quit", M_QuitSRB2, 160}, -}; - -typedef enum -{ - play = 0, - extra, - options, - quitkart -} main_e; - -static menuitem_t PlayMenu[] = -{ - {IT_CALL|IT_STRING, NULL, "Local Play", M_TimeAttack, 64}, - {IT_SUBMENU|IT_STRING, NULL, "Online", &MP_MainDef, 96}, - - {IT_SUBMENU|IT_STRING, NULL, "Back", &MainDef, 160}, -}; - -// --------------------------------- -// 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[] = -{ - {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_continue, - spause_retry, - spause_options, - spause_title, - spause_quit -} spause_e; - -// Playback -static menuitem_t PlaybackMenu[] = -{ - {IT_CALL | IT_STRING, "M_PHIDE", "Hide Menu", M_SelectableClearMenus, 0}, - - {IT_CALL | IT_STRING, "M_PREW", "Rewind", M_PlaybackRewind, 20}, - {IT_CALL | IT_STRING, "M_PPAUSE", "Pause", M_PlaybackPause, 36}, - {IT_CALL | IT_STRING, "M_PFFWD", "Fast-Forward", M_PlaybackFastForward, 52}, - {IT_CALL | IT_STRING, "M_PSTEPB", "Backup Frame", M_PlaybackRewind, 20}, - {IT_CALL | IT_STRING, "M_PRESUM", "Resume", M_PlaybackPause, 36}, - {IT_CALL | IT_STRING, "M_PFADV", "Advance Frame", M_PlaybackAdvance, 52}, - - {IT_ARROWS | IT_STRING, "M_PVIEWS", "View Count", M_PlaybackSetViews, 72}, - {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint", M_PlaybackAdjustView, 88}, - {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint 2", M_PlaybackAdjustView, 104}, - {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint 3", M_PlaybackAdjustView, 120}, - {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint 4", M_PlaybackAdjustView, 136}, - - //{IT_CALL | IT_STRING, "M_POPTS", "More Options...", M_ReplayHut, 156}, - //{IT_CALL | IT_STRING, "M_PEXIT", "Stop Playback", M_PlaybackQuit, 172}, - {IT_CALL | IT_STRING, "M_PEXIT", "Stop Playback", M_PlaybackQuit, 156}, -}; - -typedef enum -{ - playback_hide, - playback_rewind, - playback_pause, - playback_fastforward, - playback_backframe, - playback_resume, - playback_advanceframe, - playback_viewcount, - playback_view1, - playback_view2, - playback_view3, - playback_view4, - //playback_moreoptions, - playback_quit -} playback_e; - -// PLAY - -// 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_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_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} -}; - -// EXTRAS - -static menuitem_t EX_MainMenu[] = -{ - {IT_STRING|IT_SUBMENU, NULL, "Unlocks", &EX_UnlockChecklistDef, 32}, - {IT_STRING|IT_CALL|IT_CALL_NOTMODIFIED, NULL, "Stats", M_Statistics, 64}, - {IT_STRING|IT_CALL, NULL, "Addons", M_Addons, 96}, - {IT_STRING|IT_CALL, NULL, "Replays", M_ReplayHut, 128}, - {IT_STRING|IT_SUBMENU, NULL, "Back", &MainDef, 160}, -}; - -static menuitem_t EX_UnlockChecklistMenu[] = -{ - {IT_SUBMENU|IT_STRING, NULL, "Back", &EX_MainDef, 200}, -}; - -static menuitem_t EX_LevelStatsMenu[] = -{ - {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelStats, '\0'}, // dummy menuitem for the control func -}; - -static menuitem_t EX_AddonsMenu[] = -{ - {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleAddons, 0}, // dummy menuitem for the control func -}; - -static menuitem_t EX_ReplayHutMenu[] = -{ - {IT_KEYHANDLER|IT_NOTHING, NULL, "", M_HandleReplayHutList, 0}, // Dummy menuitem for the replay list - {IT_NOTHING, NULL, "", NULL, 0}, // Dummy for handling wrapping to the top of the menu.. -}; - -static menuitem_t EX_ReplayStartMenu[] = -{ - {IT_CALL |IT_STRING, NULL, "Load Addons and Watch", M_HutStartReplay, 0}, - {IT_CALL |IT_STRING, NULL, "Watch Without Addons", M_HutStartReplay, 10}, - {IT_CALL |IT_STRING, NULL, "Watch Replay", M_HutStartReplay, 10}, - {IT_SUBMENU |IT_STRING, NULL, "Back", &EX_ReplayHutDef, 30}, -}; - -// OPTIONS - -static menuitem_t OP_ReplayOptionsMenu[] = -{ - {IT_CVAR|IT_STRING, NULL, "Record Replays", &cv_recordmultiplayerdemos, 0}, - {IT_CVAR|IT_STRING, NULL, "Sync Check Interval", &cv_netdemosyncquality, 10}, -}; - -// ----------------- -// 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}, -}; - -// -------------------------------- -// 1 Player and all of its submenus -// -------------------------------- -// Prefix: SP_ - - - -// 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_SUBMENU|IT_STRING, NULL, "Gameplay Options...", &OP_GameOptionsDef, 70}, - {IT_SUBMENU|IT_STRING, NULL, "Server Options...", &OP_ServerOptionsDef, 80}, - - {IT_SUBMENU|IT_STRING, NULL, "Data Options...", &OP_DataOptionsDef, 100}, - - {IT_CALL|IT_STRING, NULL, "Tricks & Secrets (F1)", M_Manual, 120}, - {IT_CALL|IT_STRING, NULL, "Play Credits", M_Credits, 130}, -}; - -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, "Field of View", &cv_fov, 75}, - - {IT_STRING | IT_CVAR, NULL, "Show FPS", &cv_ticrate, 90}, - {IT_STRING | IT_CVAR, NULL, "Vertical Sync", &cv_vidwait, 100}, - -#ifdef HWRENDER - {IT_STRING | IT_CVAR, NULL, "3D models", &cv_grmdls, 115}, - {IT_STRING | IT_CVAR, NULL, "Fallback Player 3D Model", &cv_grfallbackplayermodel, 125}, - {IT_SUBMENU|IT_STRING, NULL, "OpenGL Options...", &OP_OpenGLOptionsDef, 135}, -#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_fov, - op_video_fps, - op_video_vsync, -#ifdef HWRENDER - op_video_md2, - op_video_kartman, - 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, "Quality", &cv_scr_depth, 35}, - {IT_STRING|IT_CVAR, NULL, "Texture Filter", &cv_grfiltermode, 45}, - {IT_STRING|IT_CVAR, NULL, "Anisotropic", &cv_granisotropicmode, 55}, -/*#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_CALL, NULL, "Add-on Options...", M_AddonsOptions, 20}, - {IT_STRING | IT_SUBMENU, NULL, "Replay Options...", &OP_ReplayOptionsDef, 30}, - - {IT_STRING | IT_SUBMENU, NULL, "Erase Data...", &OP_EraseDataDef, 50}, -}; - -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, "Super Rings", M_HandleMonitorToggles, KITEM_SUPERRING}, - {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 = KARTGAMEMODEMENU(NULL, MainMenu, NULL); -menu_t PY_MainDef = KARTGAMEMODEMENU(NULL, PlayMenu, &MainDef); - -menu_t PlaybackMenuDef = { - NULL, - sizeof (PlaybackMenu)/sizeof (menuitem_t), - NULL, - PlaybackMenu, - M_DrawPlaybackMenu, - //BASEVIDWIDTH/2 - 94, 2, - BASEVIDWIDTH/2 - 88, 2, - 0, - NULL -}; - -menu_t MAPauseDef = PAUSEMENUSTYLE(MAPauseMenu, 40, 72); -menu_t SPauseDef = PAUSEMENUSTYLE(SPauseMenu, 40, 72); -menu_t MPauseDef = PAUSEMENUSTYLE(MPauseMenu, 40, 72); - -// MISC menus - -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); - -// PLAY MENU - -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 -}; - - -// TIME ATTACK - -static menu_t SP_TimeAttackDef = -{ - "M_ATTACK", - sizeof (SP_TimeAttackMenu)/sizeof (menuitem_t), - &PY_MainDef, - SP_TimeAttackMenu, - M_DrawTimeAttackMenu, - 34, 40, - 0, - M_QuitTimeAttackMenu -}; - -static menu_t SP_ReplayDef = -{ - NULL, - sizeof(SP_ReplayMenu)/sizeof(menuitem_t), - &EX_MainDef, - 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 -}; - -// EXTRAS MENU -menu_t EX_MainDef = KARTGAMEMODEMENU(NULL, EX_MainMenu, &MainDef); - -menu_t EX_UnlockChecklistDef = -{ - NULL, - 1, - &EX_MainDef, - EX_UnlockChecklistMenu, - M_DrawChecklist, - 280, 185, - 0, - NULL -}; - -menu_t EX_LevelStatsDef = -{ - NULL, - 1, - &EX_MainDef, - EX_LevelStatsMenu, - M_DrawLevelStats, - 280, 185, - 0, - NULL -}; - -menu_t EX_AddonsDef = -{ - NULL, - sizeof (EX_AddonsMenu)/sizeof (menuitem_t), - &EX_MainDef, - EX_AddonsMenu, - M_DrawAddons, - 50, 28, - 0, - NULL -}; - -menu_t EX_ReplayHutDef = -{ - NULL, - sizeof (EX_ReplayHutMenu)/sizeof (menuitem_t), - &EX_MainDef, - EX_ReplayHutMenu, - M_DrawReplayHut, - 30, 80, - 0, - M_QuitReplayHut -}; - -menu_t EX_ReplayStartDef = -{ - NULL, - sizeof (EX_ReplayStartMenu)/sizeof (menuitem_t), - &EX_ReplayHutDef, - EX_ReplayStartMenu, - M_DrawReplayStartMenu, - 30, 90, - 0, - NULL -}; - -// 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_DrawSoundOptions, - 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_DataOptionsDef, 30, 30); -menu_t OP_AddonsOptionsDef = DEFAULTMENUSTYLE("M_ADDONS", OP_AddonsOptionsMenu, &OP_DataOptionsDef, 30, 30); -menu_t OP_EraseDataDef = DEFAULTMENUSTYLE("M_DATA", OP_EraseDataMenu, &OP_DataOptionsDef, 30, 30); - -menu_t OP_ReplayOptionsDef = -{ - "M_REPOPT", - sizeof (OP_ReplayOptionsMenu)/sizeof (menuitem_t), - &OP_DataOptionsDef, - OP_ReplayOptionsMenu, - M_DrawGenericMenu, - 27, 40, - 0, - NULL -}; - -// -// 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 - { - 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; -} - -// ========================================================================== -// 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; - else if (cv == &cv_maxping) - choice *= 50; -#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-1) * cv_deadzone.value) >> FRACBITS; - 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; - } - } - } - - 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].mvar1 != 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; - } - - if (currentMenu == &PlaybackMenuDef) - { - // Flip left/right with up/down for the playback menu, since it's a horizontal icon row. - switch (ch) - { - case KEY_LEFTARROW: ch = KEY_UPARROW; break; - case KEY_UPARROW: ch = KEY_RIGHTARROW; break; - case KEY_RIGHTARROW: ch = KEY_DOWNARROW; break; - case KEY_DOWNARROW: ch = KEY_LEFTARROW; break; - default: break; - } - } - - // 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 (currentMenu == &PlaybackMenuDef) - { - boolean held = (boolean)playback_enterheld; - playback_enterheld = TICRATE/7; - if (held) - return true; - } - - 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; - } - - 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].mvar1); - 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 (menuwipe) - F_WipeStartScreen(); - - if (menuactive) - { - if (gamestate == GS_MENU) // draw BG - { - V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("WEIRDRES", PU_CACHE), NULL); - V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUBG", PU_CACHE), NULL); - } - else if (!WipeInAction && currentMenu != &PlaybackMenuDef) // Replay playback has its own background - V_DrawFadeScreen(0xFF00, 16); // now that's more readable with a faded background (yeah like Quake...) - - 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 - } - } - } - - if (menuwipe) - { - F_WipeEndScreen(); - F_RunWipe(wipedefs[wipe_menu_final], false, "FADEMAP0", true); - menuwipe = false; - } - - // 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) -{ - // intro might call this repeatedly - if (menuactive) - { - CON_ToggleOff(); // move away console - return; - } - - if (gamestate == GS_TITLESCREEN) // Set up menu state - { - G_SetGamestate(GS_MENU); - - gameaction = ga_nothing; - paused = false; - CON_ToggleOff(); - - S_ChangeMusicInternal("menu", true); - } - - menuactive = true; - - if (demo.playback) - { - currentMenu = &PlaybackMenuDef; - } - else if (!Playing()) - { - currentMenu = &MainDef; - itemOn = 0; - } - else if (modeattacking) - { - currentMenu = &MAPauseDef; - itemOn = mapause_continue; - } - else if (!(netgame || multiplayer)) // Single Player - { - if (gamestate != GS_LEVEL) // intermission, so gray out stuff. - SPauseMenu[spause_retry].status = IT_GRAYEDOUT; - else - { - //INT32 numlives = 2; - - /*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); - } - - 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].mvar1 = 48; - MPauseMenu[mpause_switchspectate].mvar1 = 48; - MPauseMenu[mpause_options].mvar1 = 64; - MPauseMenu[mpause_title].mvar1 = 80; - MPauseMenu[mpause_quit].mvar1 = 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].mvar1 += ((splitscreen+1) * 8); - MPauseMenu[mpause_options].mvar1 += 8; - MPauseMenu[mpause_title].mvar1 += 8; - MPauseMenu[mpause_quit].mvar1 += 8; - } - else if (G_GametypeHasSpectators()) - { - MPauseMenu[mpause_switchspectate].status = IT_STRING | IT_SUBMENU; - MPauseMenu[mpause_switchspectate].mvar1 += ((splitscreen+1) * 8); - MPauseMenu[mpause_options].mvar1 += 8; - MPauseMenu[mpause_title].mvar1 += 8; - MPauseMenu[mpause_quit].mvar1 += 8; - } - } - - if (splitscreen > 1) - { - MPauseMenu[mpause_psetupsplit3].status = IT_STRING | IT_CALL; - - MPauseMenu[mpause_options].mvar1 += 8; - MPauseMenu[mpause_title].mvar1 += 8; - MPauseMenu[mpause_quit].mvar1 += 8; - - if (splitscreen > 2) - { - MPauseMenu[mpause_psetupsplit4].status = IT_STRING | IT_CALL; - MPauseMenu[mpause_options].mvar1 += 8; - MPauseMenu[mpause_title].mvar1 += 8; - MPauseMenu[mpause_quit].mvar1 += 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 - - if (gamestate == GS_MENU) // Back to title screen - D_StartTitle(); - - menuactive = false; -} - -// -// M_SetupNextMenu -// -void M_SetupNextMenu(menu_t *menudef) -{ - INT16 i; - - if (gamestate == GS_MENU) - { - menuwipe = true; - F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); - F_WipeEndScreen(); - F_RunWipe(wipedefs[wipe_menu_toblack], false, "FADEMAP0", false); - } - - 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; - - if (currentMenu == &PlaybackMenuDef) - { - if (playback_enterheld > 0) - playback_enterheld--; - } - else - playback_enterheld = 0; - - //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].mvar1 = 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_kartman].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); -} - -void M_InitCharacterTables(void) -{ - UINT8 i; - - // Setup PlayerMenu table - for (i = 0; i < MAXSKINS; i++) - { - PlayerMenu[i].status = (i < 4 ? IT_CALL : IT_DISABLED); - PlayerMenu[i].patch = PlayerMenu[i].text = NULL; - PlayerMenu[i].itemaction = M_ChoosePlayer; - PlayerMenu[i].mvar1 = 0; - } - - // Setup description table - for (i = 0; i < MAXSKINS; i++) - { - if (i == 0) - { - strcpy(description[i].notes, "\x82Sonic\x80 is the fastest of the three, but also the hardest to control. Beginners beware, but experts will find Sonic very powerful.\n\n\x82""Ability:\x80 Speed Thok\nDouble jump to zoom forward with a huge burst of speed.\n\n\x82Tip:\x80 Simply letting go of forward does not slow down in SRB2. To slow down, hold the opposite direction."); - strcpy(description[i].picname, ""); - strcpy(description[i].skinname, "sonic"); - } - else if (i == 1) - { - strcpy(description[i].notes, "\x82Tails\x80 is the most mobile of the three, but has the slowest speed. Because of his mobility, he's well-\nsuited to beginners.\n\n\x82""Ability:\x80 Fly\nDouble jump to start flying for a limited time. Repetitively hit the jump button to ascend.\n\n\x82Tip:\x80 To quickly descend while flying, hit the spin button."); - strcpy(description[i].picname, ""); - strcpy(description[i].skinname, "tails"); - } - else if (i == 2) - { - strcpy(description[i].notes, "\x82Knuckles\x80 is well-\nrounded and can destroy breakable walls simply by touching them, but he can't jump as high as the other two.\n\n\x82""Ability:\x80 Glide & Climb\nDouble jump to glide in the air as long as jump is held. Glide into a wall to climb it.\n\n\x82Tip:\x80 Press spin while climbing to jump off the wall; press jump instead to jump off\nand face away from\nthe wall."); - strcpy(description[i].picname, ""); - strcpy(description[i].skinname, "knuckles"); - } - else if (i == 3) - { - strcpy(description[i].notes, "\x82Sonic & Tails\x80 team up to take on Dr. Eggman!\nControl Sonic while Tails desperately struggles to keep up.\n\nPlayer 2 can control Tails directly by setting the controls in the options menu.\nTails's directional controls are relative to Player 1's camera.\n\nTails can pick up Sonic while flying and carry him around."); - strcpy(description[i].picname, "CHRS&T"); - strcpy(description[i].skinname, "sonic&tails"); - } - else - { - strcpy(description[i].notes, "???"); - strcpy(description[i].picname, ""); - strcpy(description[i].skinname, ""); - } - } -} - -// ========================================================================== -// 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, 159); - //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_DrawKartGamemodeMenu(void) -{ - INT32 i; - - V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL); - V_DrawCenteredThinString(BASEVIDWIDTH/2, 12, V_ALLOWLOWERCASE|V_6WIDTHSPACE, "Configure your controls, settings, and preferences."); - - V_DrawFixedPatch(172<numitems; i++) - { - switch (currentMenu->menuitems[i].status & IT_DISPLAY) - { - case IT_STRING: - V_DrawRightAlignedGamemodeString(170, currentMenu->menuitems[i].mvar1, 0, currentMenu->menuitems[i].text, - (i == itemOn) ? SKINCOLOR_PLAGUE : SKINCOLOR_PIGEON); - break; - } - } -} - -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].mvar1;//+= 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].mvar1) - y = currentMenu->y+currentMenu->menuitems[i].mvar1; - 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].mvar1) - y = currentMenu->y+currentMenu->menuitems[i].mvar1; - /* FALLTHRU */ - case IT_TRANSTEXT2: - V_DrawString(x, y, V_TRANSLUCENT, currentMenu->menuitems[i].text); - y += SMALLLINEHEIGHT; - break; - case IT_QUESTIONMARKS: - if (currentMenu->menuitems[i].mvar1) - y = currentMenu->y+currentMenu->menuitems[i].mvar1; - - 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].mvar1) - y = currentMenu->y+currentMenu->menuitems[i].mvar1; - - 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_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(); -} - -// -// 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 (currentMenu != &SP_TimeAttackDef && !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 = ((demo.playback) ? &PlaybackMenuDef : &MainDef); - else - MessageDef.prevMenu = currentMenu; - - MessageDef.menuitems[0].text = message; - MessageDef.menuitems[0].mvar1 = (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 background in menus - if (gamestate == GS_MENU) - 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].mvar1) - { - 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); - - EX_AddonsDef.prevMenu = currentMenu; - M_SetupNextMenu(&EX_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, 0); - V_DrawFill(x + width, vpadding, 1, h, 0); - V_DrawFill(x - 1, vpadding-1, width+2, 1, 0); - V_DrawFill(x - 1, vpadding+h, width+2, 1, 0); - - // 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(EX_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 - { - y = FixedDiv(((ssize_t)(numwadfiles) - (ssize_t)(mainwads+1))< 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)[0]; - - 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, 159); - - // 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); - - if (currentMenu->prevMenu) - M_SetupNextMenu(currentMenu->prevMenu); - else - M_ClearMenus(true); - } -} - -// ---- REPLAY HUT ----- -menudemo_t *demolist; - -#define DF_ENCORE 0x40 -static INT16 replayScrollTitle = 0; -static SINT8 replayScrollDelay = TICRATE, replayScrollDir = 1; - -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_UP) - { - demolist[i].type = MD_SUBDIR; - sprintf(demolist[i].title, "UP"); - } - else 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, "....."); - } - } -} - -void M_ReplayHut(INT32 choice) -{ - (void)choice; - - if (!demo.inreplayhut) - { - snprintf(menupath, 1024, "%s"PATHSEP"replay"PATHSEP"online"PATHSEP, srb2home); - menupathindex[(menudepthleft = menudepth-1)] = strlen(menupath); - } - - if (!preparefilemenu(false, true)) - { - M_StartMessage("No replays found.\n\n(Press a key)\n", NULL, MM_NOTHING); - return; - } - else if (!demo.inreplayhut) - dir_on[menudepthleft] = 0; - - demo.inreplayhut = true; - - replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; - - PrepReplayList(); - M_SetupNextMenu(&EX_ReplayHutDef); - - demo.rewinding = false; -} - -static void M_HandleReplayHutList(INT32 choice) -{ - switch (choice) - { - case KEY_UPARROW: - if (dir_on[menudepthleft]) - dir_on[menudepthleft]--; - else - M_PrevOpt(); - - S_StartSound(NULL, sfx_menu1); - replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; - break; - - case KEY_DOWNARROW: - if (dir_on[menudepthleft] < sizedirmenu-1) - dir_on[menudepthleft]++; - 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: - 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, true)) - { - 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, true)) - { - M_QuitReplayHut(); - return; - } - } - else - { - S_StartSound(NULL, sfx_menu1); - dir_on[menudepthleft] = 1; - PrepReplayList(); - } - } - 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, true)) - { - M_QuitReplayHut(); - return; - } - PrepReplayList(); - break; - default: - // We can't just use M_SetupNextMenu because that'll run ReplayDef's quitroutine and boot us back to the title screen! - currentMenu->lastOn = itemOn; - currentMenu = &EX_ReplayStartDef; - - replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; - - switch (demolist[dir_on[menudepthleft]].addonstatus) - { - case DFILE_ERROR_CANNOTLOAD: - // Only show "Watch Replay Without Addons" - EX_ReplayStartMenu[0].status = IT_DISABLED; - EX_ReplayStartMenu[1].status = IT_CALL|IT_STRING; - //EX_ReplayStartMenu[1].mvar1 = 0; - EX_ReplayStartMenu[2].status = IT_DISABLED; - itemOn = 1; - break; - - case DFILE_ERROR_NOTLOADED: - case DFILE_ERROR_INCOMPLETEOUTOFORDER: - // Show "Load Addons and Watch Replay" and "Watch Replay Without Addons" - EX_ReplayStartMenu[0].status = IT_CALL|IT_STRING; - EX_ReplayStartMenu[1].status = IT_CALL|IT_STRING; - //EX_ReplayStartMenu[1].mvar1 = 10; - EX_ReplayStartMenu[2].status = IT_DISABLED; - itemOn = 0; - break; - - case DFILE_ERROR_EXTRAFILES: - case DFILE_ERROR_OUTOFORDER: - default: - // Show "Watch Replay" - EX_ReplayStartMenu[0].status = IT_DISABLED; - EX_ReplayStartMenu[1].status = IT_DISABLED; - EX_ReplayStartMenu[2].status = IT_CALL|IT_STRING; - //EX_ReplayStartMenu[2].mvar1 = 0; - itemOn = 2; - break; - } - } - - break; - } -} - -#define SCALEDVIEWWIDTH (vid.width/vid.dupx) -#define SCALEDVIEWHEIGHT (vid.height/vid.dupy) -static void DrawReplayHutReplayInfo(void) -{ - lumpnum_t lumpnum; - patch_t *patch; - UINT8 *colormap; - INT32 x, y, w, h; - - switch (demolist[dir_on[menudepthleft]].type) - { - case MD_NOTLOADED: - V_DrawCenteredString(160, 40, V_SNAPTOTOP, "Loading replay information..."); - break; - - case MD_INVALID: - V_DrawCenteredString(160, 40, V_SNAPTOTOP|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, 64, V_SNAPTOTOP|V_ALLOWLOWERCASE|V_TRANSLUCENT|highlightflags, "Recorded on an outdated version."); - /*fallthru*/ - default: - // Draw level stuff - x = 15; y = 15; - - // A 160x100 image of the level as entry MAPxxP - //CONS_Printf("%d %s\n", demolist[dir_on[menudepthleft]].map, G_BuildMapName(demolist[dir_on[menudepthleft]].map)); - lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(demolist[dir_on[menudepthleft]].map))); - if (lumpnum != LUMPERROR) - patch = W_CachePatchNum(lumpnum, PU_CACHE); - else - patch = W_CachePatchName("M_NOLVL", PU_CACHE); - - if (!(demolist[dir_on[menudepthleft]].kartspeed & DF_ENCORE)) - V_DrawSmallScaledPatch(x, y, V_SNAPTOTOP, patch); - else - { - w = SHORT(patch->width); - h = SHORT(patch->height); - V_DrawSmallScaledPatch(x+(w>>1), y, V_SNAPTOTOP|V_FLIP, patch); - - { - static angle_t rubyfloattime = 0; - const fixed_t rubyheight = FINESINE(rubyfloattime>>ANGLETOFINESHIFT); - V_DrawFixedPatch((x+(w>>2))<>2))<width), y+20, V_SNAPTOTOP, patch, colormap); - - 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; - - if (cv_vhseffect.value) - V_DrawVhsEffect(false); - - // Draw menu choices - x = currentMenu->x; - y = currentMenu->y; - - if (itemOn > replaylistitem) - { - itemOn = replaylistitem; - dir_on[menudepthleft] = sizedirmenu-1; - replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; - } - else if (itemOn < replaylistitem) - { - dir_on[menudepthleft] = 0; - replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; - } - - if (itemOn == replaylistitem) - { - INT32 maxy; - // Scroll menu items if needed - cursory = y + currentMenu->menuitems[replaylistitem].mvar1 + dir_on[menudepthleft]*10; - maxy = y + currentMenu->menuitems[replaylistitem].mvar1 + sizedirmenu*10; - - if (cursory > maxy - 20) - cursory = maxy - 20; - - if (cursory - replayhutmenuy > SCALEDVIEWHEIGHT-50) - replayhutmenuy += (cursory-SCALEDVIEWHEIGHT-replayhutmenuy + 51)/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++) - { - INT32 localy = y + currentMenu->menuitems[i].mvar1; - - if (localy < 65) - continue; - - if (i == itemOn) - cursory = localy; - - if ((currentMenu->menuitems[i].status & IT_DISPLAY)==IT_STRING) - V_DrawString(x, localy, V_SNAPTOTOP|V_SNAPTOLEFT, currentMenu->menuitems[i].text); - else - V_DrawString(x, localy, V_SNAPTOTOP|V_SNAPTOLEFT|highlightflags, currentMenu->menuitems[i].text); - } - - y += currentMenu->menuitems[replaylistitem].mvar1; - - for (i = 0; i < (INT16)sizedirmenu; i++) - { - INT32 localy = y+i*10; - INT32 localx = x; - - if (localy < 65) - continue; - if (localy >= SCALEDVIEWHEIGHT) - break; - - if (demolist[i].type == MD_NOTLOADED && !processed_one_this_frame) - { - processed_one_this_frame = true; - G_LoadDemoInfo(&demolist[i]); - } - - if (demolist[i].type == MD_SUBDIR) - { - localx += 8; - V_DrawScaledPatch(x - 4, localy, V_SNAPTOTOP|V_SNAPTOLEFT, W_CachePatchName(dirmenu[i][DIR_TYPE] == EXT_UP ? "M_RBACK" : "M_RFLDR", PU_CACHE)); - } - - if (itemOn == replaylistitem && i == (INT16)dir_on[menudepthleft]) - { - cursory = localy; - - if (replayScrollDelay) - replayScrollDelay--; - else if (replayScrollDir > 0) - { - if (replayScrollTitle < (V_StringWidth(demolist[i].title, 0) - (SCALEDVIEWWIDTH - (x<<1)))<<1) - replayScrollTitle++; - else - { - replayScrollDelay = TICRATE; - replayScrollDir = -1; - } - } - else - { - if (replayScrollTitle > 0) - replayScrollTitle--; - else - { - replayScrollDelay = TICRATE; - replayScrollDir = 1; - } - } - - V_DrawString(localx - (replayScrollTitle>>1), localy, V_SNAPTOTOP|V_SNAPTOLEFT|highlightflags|V_ALLOWLOWERCASE, demolist[i].title); - } - else - V_DrawString(localx, localy, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, demolist[i].title); - } - - // Draw scrollbar - y = sizedirmenu*10 + currentMenu->menuitems[replaylistitem].mvar1 + 30; - if (y > SCALEDVIEWHEIGHT-80) - { - V_DrawFill(BASEVIDWIDTH-4, 75, 4, SCALEDVIEWHEIGHT-80, V_SNAPTOTOP|V_SNAPTORIGHT|159); - V_DrawFill(BASEVIDWIDTH-3, 76 + (SCALEDVIEWHEIGHT-80) * replayhutmenuy / y, 2, (((SCALEDVIEWHEIGHT-80) * (SCALEDVIEWHEIGHT-80))-1) / y - 1, V_SNAPTOTOP|V_SNAPTORIGHT|149); - } - - // Draw the cursor - V_DrawScaledPatch(currentMenu->x - 24, cursory, V_SNAPTOTOP|V_SNAPTOLEFT, - W_CachePatchName("M_CURSOR", PU_CACHE)); - V_DrawString(currentMenu->x, cursory, V_SNAPTOTOP|V_SNAPTOLEFT|highlightflags, currentMenu->menuitems[itemOn].text); - - // Now draw some replay info! - V_DrawFill(10, 10, 300, 60, V_SNAPTOTOP|159); - - if (itemOn == replaylistitem) - { - DrawReplayHutReplayInfo(); - } -} - -static void M_DrawReplayStartMenu(void) -{ - const char *warning; - UINT8 i; - - M_DrawGenericMenu(); - -#define STARTY 62-(replayScrollTitle>>1) - // Draw rankings beyond first - for (i = 1; i < MAXPLAYERS && demolist[dir_on[menudepthleft]].standings[i].ranking; i++) - { - patch_t *patch; - UINT8 *colormap; - - V_DrawRightAlignedString(BASEVIDWIDTH-100, STARTY + i*20, V_SNAPTOTOP|highlightflags, va("%2d", demolist[dir_on[menudepthleft]].standings[i].ranking)); - V_DrawThinString(BASEVIDWIDTH-96, STARTY + i*20, V_SNAPTOTOP|V_ALLOWLOWERCASE, demolist[dir_on[menudepthleft]].standings[i].name); - - if (demolist[dir_on[menudepthleft]].standings[i].timeorscore == UINT32_MAX-1) - V_DrawThinString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, "NO CONTEST"); - else if (demolist[dir_on[menudepthleft]].gametype == GT_RACE) - V_DrawRightAlignedString(BASEVIDWIDTH-40, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d'%02d\"%02d", - G_TicsToMinutes(demolist[dir_on[menudepthleft]].standings[i].timeorscore, true), - G_TicsToSeconds(demolist[dir_on[menudepthleft]].standings[i].timeorscore), - G_TicsToCentiseconds(demolist[dir_on[menudepthleft]].standings[i].timeorscore) - )); - else - V_DrawString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d", demolist[dir_on[menudepthleft]].standings[i].timeorscore)); - - // Character face! - if (W_CheckNumForName(skins[demolist[dir_on[menudepthleft]].standings[i].skin].facerank) != LUMPERROR) - { - patch = facerankprefix[demolist[dir_on[menudepthleft]].standings[i].skin]; - colormap = R_GetTranslationColormap( - demolist[dir_on[menudepthleft]].standings[i].skin, - demolist[dir_on[menudepthleft]].standings[i].color, - GTC_MENUCACHE); - } - else - { - patch = W_CachePatchName("M_NORANK", PU_CACHE); - colormap = R_GetTranslationColormap( - TC_RAINBOW, - demolist[dir_on[menudepthleft]].standings[i].color, - GTC_MENUCACHE); - } - - V_DrawMappedPatch(BASEVIDWIDTH-5 - SHORT(patch->width), STARTY + i*20, V_SNAPTOTOP, patch, colormap); - } -#undef STARTY - - // Handle scrolling rankings - if (replayScrollDelay) - replayScrollDelay--; - else if (replayScrollDir > 0) - { - if (replayScrollTitle < (i*20 - SCALEDVIEWHEIGHT + 100)<<1) - replayScrollTitle++; - else - { - replayScrollDelay = TICRATE; - replayScrollDir = -1; - } - } - else - { - if (replayScrollTitle > 0) - replayScrollTitle--; - else - { - replayScrollDelay = TICRATE; - replayScrollDir = 1; - } - } - - V_DrawFill(10, 10, 300, 60, V_SNAPTOTOP|159); - DrawReplayHutReplayInfo(); - - V_DrawString(10, 72, V_SNAPTOTOP|highlightflags|V_ALLOWLOWERCASE, demolist[dir_on[menudepthleft]].title); - - // Draw a warning prompt if needed - switch (demolist[dir_on[menudepthleft]].addonstatus) - { - case DFILE_ERROR_CANNOTLOAD: - warning = "Some addons in this replay cannot be loaded.\nYou can watch anyway, but desyncs may occur."; - break; - - case DFILE_ERROR_NOTLOADED: - case DFILE_ERROR_INCOMPLETEOUTOFORDER: - warning = "Loading addons will mark your game as modified, and record attack may be unavailable.\nYou can watch without loading addons, but desyncs may occur."; - break; - - case DFILE_ERROR_EXTRAFILES: - warning = "You have addons loaded that were not present in this replay.\nYou can watch anyway, but desyncs may occur."; - break; - - case DFILE_ERROR_OUTOFORDER: - warning = "You have this replay's addons loaded, but they are out of order.\nYou can watch anyway, but desyncs may occur."; - break; - - default: - return; - } - - V_DrawSmallString(4, BASEVIDHEIGHT-14, V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, warning); -} - -static boolean M_QuitReplayHut(void) -{ - if (demolist) - Z_Free(demolist); - demolist = NULL; - - demo.inreplayhut = false; - - if (currentMenu->prevMenu) - M_SetupNextMenu(currentMenu->prevMenu); - else - M_ClearMenus(true); - - return true; -} - -static void M_HutStartReplay(INT32 choice) -{ - (void)choice; - - M_ClearMenus(false); - demo.loadfiles = (itemOn == 0); - demo.ignorefiles = (itemOn != 0); - - G_DoPlayDemo(demolist[dir_on[menudepthleft]].filepath); -} - -void M_SetPlaybackMenuPointer(void) -{ - itemOn = playback_pause; -} - -static void M_DrawPlaybackMenu(void) -{ - INT16 i; - patch_t *icon; - UINT8 *activemap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GOLD, GTC_MENUCACHE); - - // Toggle items - if (paused && !demo.rewinding) - { - PlaybackMenu[playback_pause].status = PlaybackMenu[playback_fastforward].status = PlaybackMenu[playback_rewind].status = IT_DISABLED; - PlaybackMenu[playback_resume].status = PlaybackMenu[playback_advanceframe].status = PlaybackMenu[playback_backframe].status = IT_CALL|IT_STRING; - - if (itemOn >= playback_rewind && itemOn <= playback_fastforward) - itemOn += playback_backframe - playback_rewind; - } - else - { - PlaybackMenu[playback_pause].status = PlaybackMenu[playback_fastforward].status = PlaybackMenu[playback_rewind].status = IT_CALL|IT_STRING; - PlaybackMenu[playback_resume].status = PlaybackMenu[playback_advanceframe].status = PlaybackMenu[playback_backframe].status = IT_DISABLED; - - if (itemOn >= playback_backframe && itemOn <= playback_advanceframe) - itemOn -= playback_backframe - playback_rewind; - } - - if (modeattacking) - { - for (i = playback_viewcount; i <= playback_view4; i++) - PlaybackMenu[i].status = IT_DISABLED; - - //PlaybackMenu[playback_moreoptions].mvar1 = 72; - //PlaybackMenu[playback_quit].mvar1 = 88; - PlaybackMenu[playback_quit].mvar1 = 72; - - //currentMenu->x = BASEVIDWIDTH/2 - 52; - currentMenu->x = BASEVIDWIDTH/2 - 44; - } - else - { - PlaybackMenu[playback_viewcount].status = IT_ARROWS|IT_STRING; - - for (i = 0; i <= splitscreen; i++) - PlaybackMenu[playback_view1+i].status = IT_ARROWS|IT_STRING; - for (i = splitscreen+1; i < 4; i++) - PlaybackMenu[playback_view1+i].status = IT_DISABLED; - - //PlaybackMenu[playback_moreoptions].mvar1 = 156; - //PlaybackMenu[playback_quit].mvar1 = 172; - PlaybackMenu[playback_quit].mvar1 = 156; - - //currentMenu->x = BASEVIDWIDTH/2 - 94; - currentMenu->x = BASEVIDWIDTH/2 - 88; - } - - // wip - //M_DrawTextBox(currentMenu->x-68, currentMenu->y-7, 15, 15); - //M_DrawCenteredMenu(); - - for (i = 0; i < currentMenu->numitems; i++) - { - UINT8 *inactivemap = NULL; - - if (i >= playback_view1 && i <= playback_view4) - { - if (modeattacking) continue; - - if (splitscreen >= i - playback_view1) - { - INT32 ply = displayplayers[i - playback_view1]; - - icon = facerankprefix[players[ply].skin]; - if (i != itemOn) - inactivemap = R_GetTranslationColormap(players[ply].skin, players[ply].skincolor, GTC_MENUCACHE); - } - else if (currentMenu->menuitems[i].patch && W_CheckNumForName(currentMenu->menuitems[i].patch) != LUMPERROR) - icon = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE); - else - icon = W_CachePatchName("PLAYRANK", PU_CACHE); // temp - } - else if (currentMenu->menuitems[i].status == IT_DISABLED) - continue; - else if (currentMenu->menuitems[i].patch && W_CheckNumForName(currentMenu->menuitems[i].patch) != LUMPERROR) - icon = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE); - else - icon = W_CachePatchName("PLAYRANK", PU_CACHE); // temp - - if ((i == playback_fastforward && cv_playbackspeed.value > 1) || (i == playback_rewind && demo.rewinding)) - V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].mvar1, currentMenu->y, V_SNAPTOTOP, icon, R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_JAWZ, GTC_MENUCACHE)); - else - V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].mvar1, currentMenu->y, V_SNAPTOTOP, icon, (i == itemOn) ? activemap : inactivemap); - - if (i == itemOn) - { - V_DrawCharacter(currentMenu->x + currentMenu->menuitems[i].mvar1 + 4, currentMenu->y + 14, - '\x1A' | V_SNAPTOTOP|highlightflags, false); - - V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y + 18, V_SNAPTOTOP|V_ALLOWLOWERCASE, currentMenu->menuitems[i].text); - - if ((currentMenu->menuitems[i].status & IT_TYPE) == IT_ARROWS) - { - char *str; - - if (!(i == playback_viewcount && splitscreen == 3)) - V_DrawCharacter(BASEVIDWIDTH/2 - 4, currentMenu->y + 28 - (skullAnimCounter/5), - '\x1A' | V_SNAPTOTOP|highlightflags, false); // up arrow - - if (!(i == playback_viewcount && splitscreen == 0)) - V_DrawCharacter(BASEVIDWIDTH/2 - 4, currentMenu->y + 48 + (skullAnimCounter/5), - '\x1B' | V_SNAPTOTOP|highlightflags, false); // down arrow - - switch (i) - { - case playback_viewcount: - str = va("%d", splitscreen+1); - break; - - case playback_view1: - case playback_view2: - case playback_view3: - case playback_view4: - str = player_names[displayplayers[i - playback_view1]]; // 0 to 3 - break; - - default: // shouldn't ever be reached but whatever - continue; - } - - V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y + 38, V_SNAPTOTOP|V_ALLOWLOWERCASE|highlightflags, str); - } - } - } -} - -static void M_PlaybackRewind(INT32 choice) -{ - static tic_t lastconfirmtime; - - (void)choice; - - if (!demo.rewinding) - { - if (paused) - { - G_ConfirmRewind(leveltime-1); - paused = true; - S_PauseAudio(); - } - else - demo.rewinding = paused = true; - } - else if (lastconfirmtime + TICRATE/2 < I_GetTime()) - { - lastconfirmtime = I_GetTime(); - G_ConfirmRewind(leveltime); - } - - CV_SetValue(&cv_playbackspeed, 1); -} - -static void M_PlaybackPause(INT32 choice) -{ - (void)choice; - - paused = !paused; - - if (demo.rewinding) - { - G_ConfirmRewind(leveltime); - paused = true; - S_PauseAudio(); - } - else if (paused) - S_PauseAudio(); - else - S_ResumeAudio(); - - CV_SetValue(&cv_playbackspeed, 1); -} - -static void M_PlaybackFastForward(INT32 choice) -{ - (void)choice; - - if (demo.rewinding) - { - G_ConfirmRewind(leveltime); - paused = false; - S_ResumeAudio(); - } - CV_SetValue(&cv_playbackspeed, cv_playbackspeed.value == 1 ? 4 : 1); -} - -static void M_PlaybackAdvance(INT32 choice) -{ - (void)choice; - - paused = false; - TryRunTics(1); - paused = true; -} - - -static void M_PlaybackSetViews(INT32 choice) -{ - if (choice > 0) - { - if (splitscreen < 3) - G_AdjustView(splitscreen + 2, 0, true); - } - else if (splitscreen) - { - splitscreen--; - R_ExecuteSetViewSize(); - } -} - -static void M_PlaybackAdjustView(INT32 choice) -{ - G_AdjustView(itemOn - playback_viewcount, (choice > 0) ? 1 : -1, true); -} - -static void M_PlaybackQuit(INT32 choice) -{ - (void)choice; - G_StopDemo(); - - if (demo.inreplayhut) - M_ReplayHut(choice); - else if (modeattacking) - M_EndModeAttackRun(); - else - D_StartTitle(); -} - -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[4].status = OP_MainMenu[5].status = (Playing() && !(server || IsPlayerAdmin(consoleplayer))) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); - - OP_MainMenu[8].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL); // Play credits - OP_DataOptionsMenu[3].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); // Erase data - - 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); -} - -// ======== -// 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 - -static void M_DrawSoundOptions(void) -{ - INT32 i, y = 0; - INT32 lengthstring = 0; - - M_DrawGenericMenu(); - - if (currentMenu == &OP_SoundOptionsDef) - { - V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, - currentMenu->y+currentMenu->menuitems[0].mvar1, - (sound_disabled ? warningflags : highlightflags), - (sound_disabled ? "OFF" : "ON")); - - V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, - currentMenu->y+currentMenu->menuitems[2].mvar1, - (digital_disabled ? warningflags : highlightflags), - (digital_disabled ? "OFF" : "ON")); - - /*V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, - currentMenu->y+currentMenu->menuitems[5].mvar1, - (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].mvar1; - 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].mvar1, - '\x1C' | highlightflags, false); // left arrow - V_DrawCharacter(BASEVIDWIDTH - currentMenu->x + 2 + (skullAnimCounter/5), currentMenu->y+currentMenu->menuitems[itemOn].mvar1, - '\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); - } -} - -static void M_Credits(INT32 choice) -{ - (void)choice; - cursaveslot = -2; - M_ClearMenus(true); - F_StartCredits(); -} - -// ================== -// 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 - -// =============== -// 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(&EX_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("menu", true); // Eww, but needed for when user hits escape during demo playback - - 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].mvar1; - 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, 159); - - 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].mvar1; - 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); - - if (cv_nextmap.value) - Nextmap_OnChange(); - else - CV_AddValue(&cv_nextmap, 1); - - itemOn = tastart; // "Start" is selected. -} - -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; -} - -// 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(); - - G_SetGamestate(GS_MENU); - - gameaction = ga_nothing; - paused = false; - CON_ToggleOff(); - - S_ChangeMusicInternal("menu", true); - - M_StartControlPanel(); - - switch(modeattacking) - { - default: - case ATTACKING_RECORD: - currentMenu = &SP_TimeAttackDef; - break; - /*case ATTACKING_NIGHTS: - currentMenu = &SP_NightsAttackDef; - break;*/ - } - itemOn = currentMenu->lastOn; - - modeattacking = ATTACKING_NONE; - // 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; - 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].mvar1, - highlightflags, (itemOn == mp_connect_room) ? "" : ""); - else - V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].mvar1, - 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].mvar1, - ((itemOn == 4) ? highlightflags : 0), "(2-16 players)"); -#endif - - V_DrawRightAlignedString(BASEVIDWIDTH-x, y+MP_MainMenu[5].mvar1, - ((itemOn == 5) ? highlightflags : 0), - "(2-4 players)" - ); - -#ifndef NONET - y += MP_MainMenu[8].mvar1; - - V_DrawFill(x+5, y+4+5, /*16*8 + 6,*/ BASEVIDWIDTH - 2*(x+5), 8+6, 159); - - // 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, 159); - - // 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[displayplayers[1]]; - 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(displayplayers[1])) - 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[displayplayers[2]]; - 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(displayplayers[2])) - 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[displayplayers[3]]; - 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(displayplayers[3])) - 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 extras - 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("Extras 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].mvar1][0]; - keys[1] = setupcontrols[currentMenu->menuitems[i].mvar1][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].mvar1; - 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].mvar1, - (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_superring, - &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].mvar1 == 255) - { - V_DrawScaledPatch(x, y, V_TRANSLUCENT, W_CachePatchName("K_ISBG", PU_CACHE)); - continue; - } -#endif - if (currentMenu->menuitems[thisitem].mvar1 == 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].mvar1-1]; - translucent = (cv->value ? 0 : V_TRANSLUCENT); - - switch (currentMenu->menuitems[thisitem].mvar1) - { - 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].mvar1, 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].mvar1, true), PU_CACHE)); - - y += spacing; - } - - x += spacing; - y = currentMenu->y+(spacing/4); - } - - { -#ifdef ITEMTOGGLEBOTTOMRIGHT - if (currentMenu->menuitems[itemOn].mvar1 == 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].mvar1 == 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].mvar1-1]; - translucent = (cv->value ? 0 : V_TRANSLUCENT); - - switch (currentMenu->menuitems[itemOn].mvar1) - { - 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].mvar1, 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].mvar1, 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].mvar1 == 255) - { - //S_StartSound(NULL, sfx_s26d); - if (!shitsfree) - { - shitsfree = TICRATE; - S_StartSound(NULL, sfx_itfree); - } - } - else -#endif - if (currentMenu->menuitems[itemOn].mvar1 == 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].mvar1-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].mvar1, 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].mvar1, '_' | 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].mvar1 - 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 diff --git a/src/m_misc.c b/src/m_misc.c index f4a4ec291..6e53dc2ad 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -42,7 +42,7 @@ #include "m_anigif.h" // So that the screenshot menu auto-updates... -#include "m_menu.h" +#include "k_menu.h" #ifdef HWRENDER #include "hardware/hw_main.h" diff --git a/src/mserv.c b/src/mserv.c index c7344b16a..20a66ad4e 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -87,7 +87,7 @@ #include "i_tcp.h" #include "i_system.h" #include "byteptr.h" -#include "m_menu.h" +#include "k_menu.h" #include "m_argv.h" // Alam is going to kill me <3 #include "m_misc.h" // GetRevisionString() diff --git a/src/p_setup.c b/src/p_setup.c index 50260d4de..3dcfff839 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2598,7 +2598,7 @@ static void P_ForceCharacter(const char *forcecharskin) static void P_LoadRecordGhosts(void) { - // see also m_menu.c's Nextmap_OnChange + // see also k_menu.c's Nextmap_OnChange const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1; char *gpath = malloc(glen); INT32 i; diff --git a/src/r_main.c b/src/r_main.c index 0d14bed73..17a82d4d4 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -23,7 +23,7 @@ #include "p_local.h" #include "keys.h" #include "i_video.h" -#include "m_menu.h" +#include "k_menu.h" #include "am_map.h" #include "d_main.h" #include "v_video.h" diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index e5f1c23fc..d1144c53b 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -61,7 +61,7 @@ #include "../i_system.h" #include "../v_video.h" #include "../m_argv.h" -#include "../m_menu.h" +#include "../k_menu.h" #include "../d_main.h" #include "../s_sound.h" #include "../i_sound.h" // midi pause/unpause @@ -1081,9 +1081,11 @@ void I_GetEvent(void) CONS_Debug(DBG_GAMELOGIC, "Joystick3 device index: %d\n", JoyInfo3.oldjoy); CONS_Debug(DBG_GAMELOGIC, "Joystick4 device index: %d\n", JoyInfo4.oldjoy); +#if 0 // update the menu if (currentMenu == &OP_JoystickSetDef) M_SetupJoystickMenu(0); +#endif if (JoyInfo.dev != newjoy && JoyInfo2.dev != newjoy && JoyInfo3.dev != newjoy && JoyInfo4.dev != newjoy) SDL_JoystickClose(newjoy); @@ -1242,9 +1244,11 @@ void I_GetEvent(void) CONS_Debug(DBG_GAMELOGIC, "Joystick3 device index: %d\n", JoyInfo3.oldjoy); CONS_Debug(DBG_GAMELOGIC, "Joystick4 device index: %d\n", JoyInfo4.oldjoy); +#if 0 // update the menu if (currentMenu == &OP_JoystickSetDef) M_SetupJoystickMenu(0); +#endif break; case SDL_QUIT: I_Quit(); diff --git a/src/st_stuff.c b/src/st_stuff.c index e59846aed..2b4f87c01 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -25,7 +25,7 @@ #include "hu_stuff.h" #include "s_sound.h" #include "i_system.h" -#include "m_menu.h" +#include "k_menu.h" #include "m_cheat.h" #include "p_setup.h" // NiGHTS grading #include "k_kart.h" // SRB2kart @@ -2045,11 +2045,11 @@ static void ST_overlayDrawer(void) void ST_DrawDemoTitleEntry(void) { - static UINT8 skullAnimCounter = 0; + static UINT8 anim = 0; char *nametodraw; - skullAnimCounter++; - skullAnimCounter %= 8; + anim++; + anim %= 8; nametodraw = demo.titlename; while (V_StringWidth(nametodraw, 0) > MAXSTRINGLENGTH*8 - 8) @@ -2059,7 +2059,7 @@ void ST_DrawDemoTitleEntry(void) #define y (BASEVIDHEIGHT/2) M_DrawTextBox(x, y + 4, MAXSTRINGLENGTH, 1); V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, nametodraw); - if (skullAnimCounter < 4) + if (anim < 4) V_DrawCharacter(x + 8 + V_StringWidth(nametodraw, 0), y + 12, '_' | 0x80, false); diff --git a/src/y_inter.c b/src/y_inter.c index c270f04ab..128fb8563 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -26,7 +26,7 @@ #include "w_wad.h" #include "y_inter.h" #include "z_zone.h" -#include "m_menu.h" +#include "k_menu.h" #include "m_misc.h" #include "i_system.h" #include "p_setup.h" @@ -39,7 +39,6 @@ #include "m_random.h" // M_RandomKey #include "g_input.h" // PLAYER1INPUTDOWN #include "k_kart.h" // colortranslations -#include "console.h" // cons_menuhighlight #include "lua_hook.h" // IntermissionThinker hook #ifdef HWRENDER @@ -363,9 +362,7 @@ void Y_IntermissionDrawer(void) if (!splitscreen) whiteplayer = demo.playback ? displayplayers[0] : consoleplayer; - if (cons_menuhighlight.value) - hilicol = cons_menuhighlight.value; - else if (modeattacking) + if (modeattacking) hilicol = V_ORANGEMAP; else hilicol = ((intertype == int_race) ? V_SKYMAP : V_REDMAP); @@ -1198,9 +1195,7 @@ void Y_VoteDrawer(void) if (timer) { INT32 hilicol, tickdown = (timer+1)/TICRATE; - if (cons_menuhighlight.value) - hilicol = cons_menuhighlight.value; - else if (gametype == GT_RACE) + if (gametype == GT_RACE) hilicol = V_SKYMAP; else //if (gametype == GT_MATCH) hilicol = V_REDMAP; From dae7dd003a364cdfa08e5c55348eaf66ddbee452 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Wed, 2 Oct 2019 19:13:56 -0400 Subject: [PATCH 004/379] More complete character select --- src/g_game.c | 32 +++---- src/hu_stuff.c | 13 ++- src/hu_stuff.h | 3 +- src/k_menu.h | 24 +++++ src/k_menudef.c | 4 +- src/k_menudraw.c | 202 +++++++++++++++++++++++++++++++++++------- src/k_menufunc.c | 224 +++++++++++++++++++++++++++++++++-------------- src/v_video.c | 92 +++++++++++++++++++ src/v_video.h | 4 + 9 files changed, 480 insertions(+), 118 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index fcb815d3a..89987a29f 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -524,22 +524,22 @@ player_t *seenplayer; // player we're aiming at right now char player_names[MAXPLAYERS][MAXPLAYERNAME+1] = { - "Player 1", - "Player 2", - "Player 3", - "Player 4", - "Player 5", - "Player 6", - "Player 7", - "Player 8", - "Player 9", - "Player 10", - "Player 11", - "Player 12", - "Player 13", - "Player 14", - "Player 15", - "Player 16" + "A Player", + "B Player", + "C Player", + "D Player", + "E Player", + "F Player", + "G Player", + "H Player", + "I Player", + "J Player", + "K Player", + "L Player", + "M Player", + "N Player", + "O Player", + "P Player" }; // SRB2kart - removed Players 17 through 32 INT16 rw_maximums[NUM_WEAPONS] = diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 44ed3ae81..3be1d6473 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -65,12 +65,14 @@ // heads up font //------------------------------------------- patch_t *hu_font[HU_FONTSIZE]; -patch_t *kart_font[KART_FONTSIZE]; // SRB2kart -patch_t *gamemode_font[AZ_FONTSIZE]; patch_t *tny_font[HU_FONTSIZE]; patch_t *tallnum[10]; // 0-9 patch_t *nightsnum[10]; // 0-9 +patch_t *kart_font[KART_FONTSIZE]; // SRB2kart +patch_t *gamemode_font[AZ_FONTSIZE]; +patch_t *file_font[AZ_FONTSIZE]; + // Level title and credits fonts patch_t *lt_font[LT_FONTSIZE]; patch_t *cred_font[CRED_FONTSIZE]; @@ -248,6 +250,13 @@ void HU_LoadGraphics(void) gamemode_font[i] = NULL; else gamemode_font[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); + + // File select font + sprintf(buffer, "FILEF%.3d", j); + if (W_CheckNumForName(buffer) == LUMPERROR) + file_font[i] = NULL; + else + file_font[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); } // diff --git a/src/hu_stuff.h b/src/hu_stuff.h index 2c5fcbb29..9c9206f83 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -84,7 +84,8 @@ void HU_AddChatText(const char *text, boolean playsound); extern boolean chat_on; extern patch_t *hu_font[HU_FONTSIZE], *tny_font[HU_FONTSIZE]; -extern patch_t *kart_font[KART_FONTSIZE], *gamemode_font[AZ_FONTSIZE]; // SRB2kart +extern patch_t *kart_font[KART_FONTSIZE]; // SRB2kart +extern patch_t *gamemode_font[AZ_FONTSIZE], *file_font[AZ_FONTSIZE]; extern patch_t *tallnum[10]; extern patch_t *pingnum[10]; extern patch_t *pinggfx[5]; diff --git a/src/k_menu.h b/src/k_menu.h index 863838127..d57618a4a 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -111,6 +111,7 @@ typedef struct menu_s INT16 transitionInTics; // tics for transitions in INT16 transitionOutTics; // tics for transitions out void (*drawroutine)(void); // draw routine + void (*tickroutine)(void); // ticker routine boolean (*quitroutine)(void); // called before quit a menu return true if we can } menu_t; @@ -227,11 +228,19 @@ extern struct setup_chargrid_s { UINT8 numskins; } setup_chargrid[9][9]; +#define CSSTEP_NONE 0 +#define CSSTEP_CHARS 1 +#define CSSTEP_ALTS 2 +#define CSSTEP_COLORS 3 +#define CSSTEP_READY 4 + typedef struct setup_player_s { SINT8 gridx, gridy; SINT8 skin; SINT8 clonenum; + SINT8 rotate; + UINT8 delay; UINT8 color; UINT8 mdepth; } setup_player_t; @@ -239,6 +248,18 @@ typedef struct setup_player_s extern setup_player_t setup_player[MAXSPLITSCREENPLAYERS]; extern UINT8 setup_numplayers; +extern UINT16 setup_animcounter; + +// The selection spawns 3 explosions in 4 directions, and there's 4 players -- 3 * 4 * 4 = 48 +#define CSEXPLOSIONS 48 + +#define CSROTATETICS 6 + +extern struct setup_explosions_s { + UINT8 x, y; + UINT8 tics; + UINT8 color; +} setup_explosions[CSEXPLOSIONS]; typedef enum { @@ -251,6 +272,7 @@ consvar_t *setup_playercvars[MAXSPLITSCREENPLAYERS][SPLITCV_MAX]; void M_CharacterSelectInit(INT32 choice); void M_CharacterSelectHandler(INT32 choice); +void M_CharacterSelectTick(void); boolean M_CharacterSelectQuit(void); void M_EndModeAttackRun(void); @@ -288,6 +310,7 @@ void M_DrawPlaybackMenu(void); x, y,\ 0, 0,\ M_DrawGenericMenu,\ + NULL,\ NULL\ } @@ -301,6 +324,7 @@ void M_DrawPlaybackMenu(void); 0, 0,\ 10, 10,\ M_DrawKartGamemodeMenu,\ + NULL,\ NULL\ } diff --git a/src/k_menudef.c b/src/k_menudef.c index 07cd413b9..5dc155fd0 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -18,7 +18,7 @@ // --------- menuitem_t MainMenu[] = { - {IT_STRING | IT_CALL, "Play", "Cut to the chase and start playing!", + {IT_STRING | IT_CALL, "Play", "Cut to the chase and start the race!", NULL, M_CharacterSelectInit, 48, 0}, {IT_STRING, "Extra", "Check out some bonus features.", @@ -50,6 +50,7 @@ menu_t PLAY_CharSelectDef = { 0, 0, 0, 0, M_DrawCharacterSelect, + M_CharacterSelectTick, M_CharacterSelectQuit }; @@ -128,5 +129,6 @@ menu_t PAUSE_PlaybackMenuDef = { BASEVIDWIDTH/2 - 88, 2, 0, 0, M_DrawPlaybackMenu, + NULL, NULL }; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index abf696764..556955d42 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -518,54 +518,91 @@ void M_DrawImageDef(void) static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) { - const fixed_t rad = 32<mdepth == 2) + if (p->mdepth == CSSTEP_ALTS) numoptions = setup_chargrid[p->gridx][p->gridy].numskins; else - { - numoptions = 16; - patch = W_CachePatchName("COLORSPH", PU_CACHE); - } + numoptions = MAXSKINCOLORS-1; - angamt /= numoptions*2; + angamt /= numoptions; for (i = 0; i < numoptions; i++) { fixed_t cx = x << FRACBITS, cy = y << FRACBITS; - fixed_t ang = -(angamt * i); + boolean subtract = (i & 1); + angle_t ang = ((i+1)/2) * angamt; + patch_t *patch = NULL; UINT8 *colormap; + fixed_t radius = 24<> ANGLETOFINESHIFT)); - cy += FixedMul(rad, FINESINE(FixedAngle(ang << FRACBITS) >> ANGLETOFINESHIFT)) >> 2; - - if (p->mdepth == 2) + if (p->mdepth == CSSTEP_ALTS) { SINT8 skin; - cx -= 8<clonenum + i) % setup_chargrid[p->gridx][p->gridy].numskins; + n = (p->clonenum) + numoptions/2; + if (subtract) + n -= ((i+1)/2); + else + n += ((i+1)/2); + n %= numoptions; skin = setup_chargrid[p->gridx][p->gridy].skinlist[n]; - - colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE); patch = facerankprefix[skin]; + colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE); } else { - cx -= 4<color + i) % (MAXSKINCOLORS-1)); + INT16 diff; + + n = (p->color-1) + numoptions/2; + if (subtract) + n -= ((i+1)/2); + else + n += ((i+1)/2); + n %= numoptions; + n++; + colormap = R_GetTranslationColormap(TC_DEFAULT, n, GTC_MENUCACHE); + + if (n > p->color) + diff = n - p->color; + else + diff = p->color - n; + + if (diff == 0) + patch = W_CachePatchName("COLORSP2", PU_CACHE); + else if (abs(diff) < 25) + patch = W_CachePatchName("COLORSP1", PU_CACHE); + else + patch = W_CachePatchName("COLORSP0", PU_CACHE); + + radius -= SHORT(patch->width) << FRACBITS; } + cx -= (SHORT(patch->width) << FRACBITS) >> 1; + cy -= (SHORT(patch->height) << FRACBITS) >> 1; + + if (subtract) + ang = (signed)(ANGLE_90 - ang); + else + ang = ANGLE_90 + ang; + + if (numoptions % 2) + ang = (signed)(ang - (angamt/2)); + + if (p->rotate) + ang = (signed)(ang + ((angamt / CSROTATETICS) * p->rotate)); + + cx += FixedMul(radius, FINECOSINE(ang >> ANGLETOFINESHIFT)); + cy -= FixedMul(radius, FINESINE(ang >> ANGLETOFINESHIFT)) / 3; + V_DrawFixedPatch(cx, cy, FRACUNIT, 0, patch, colormap); + if (p->mdepth == CSSTEP_ALTS && n != p->clonenum) + V_DrawFixedPatch(cx, cy, FRACUNIT, V_TRANSLUCENT, W_CachePatchName("ICONDARK", PU_CACHE), NULL); } } @@ -589,7 +626,7 @@ static void M_DrawCharSelectPreview(UINT8 num) V_DrawScaledPatch(x, y+6, V_TRANSLUCENT, W_CachePatchName("PREVBACK", PU_CACHE)); - if (p->mdepth > 0) + if (p->mdepth >= CSSTEP_CHARS) { skin = setup_chargrid[p->gridx][p->gridy].skinlist[p->clonenum]; @@ -634,21 +671,117 @@ static void M_DrawCharSelectPreview(UINT8 num) } } - if (p->mdepth == 2 || p->mdepth == 3) - M_DrawCharSelectCircle(p, x+32, y+48); + if (p->mdepth == CSSTEP_ALTS || p->mdepth == CSSTEP_COLORS) + M_DrawCharSelectCircle(p, x+32, y+64); + } + + if ((setup_animcounter/10) & 1) + { + if (p->mdepth == CSSTEP_NONE) + V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PSTART", PU_CACHE)); + //else if (p->mdepth >= CSSTEP_READY) + // V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PREADY", PU_CACHE)); } V_DrawScaledPatch(x+9, y+2, 0, W_CachePatchName("FILEBACK", PU_CACHE)); - V_DrawScaledPatch(x, y, 0, W_CachePatchName(va("CHARSEL%c", letter), PU_CACHE)); - - if (p->mdepth == 0 && (skullAnimCounter/5)) - V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PSTART", PU_CACHE)); + V_DrawScaledPatch(x, y+2, 0, W_CachePatchName(va("CHARSEL%c", letter), PU_CACHE)); + V_DrawFileString(x+16, y+2, 0, "PLAYER"); } +static void M_DrawCharSelectExplosions(void) +{ + UINT8 i; + + for (i = 0; i < CSEXPLOSIONS; i++) + { + INT16 quadx, quady; + UINT8 *colormap; + UINT8 frame; + + if (setup_explosions[i].tics == 0 || setup_explosions[i].tics > 5) + continue; + + frame = 6 - setup_explosions[i].tics; + + quadx = 4 * (setup_explosions[i].x / 3); + quady = 4 * (setup_explosions[i].y / 3); + + colormap = R_GetTranslationColormap(TC_DEFAULT, setup_explosions[i].color, GTC_MENUCACHE); + + V_DrawMappedPatch( + 82 + (setup_explosions[i].x*16) + quadx - 6, + 22 + (setup_explosions[i].y*16) + quady - 6, + 0, W_CachePatchName(va("CHCNFRM%d", frame), PU_CACHE), + colormap + ); + } +} + +#define IDLELEN 8 +#define SELECTLEN (8 + IDLELEN + 7 + IDLELEN) + +static void M_DrawCharSelectCursors(void) +{ + UINT8 i; + static const char *idleframes[IDLELEN] = { + "CHHOV1", "CHHOV1", "CHHOV1", "CHHOV2", "CHHOV1", "CHHOV3", "CHHOV1", "CHHOV2" + }; + static const char *selectframesa[SELECTLEN] = { + "CHHOV1", "CHPIKA1", "CHHOV2", "CHPIKA2", "CHHOV3", "CHPIKA3", "CHHOV2", "CHPIKA4", + "CHHOV1", "CHHOV1", "CHHOV1", "CHHOV2", "CHHOV1", "CHHOV3", "CHHOV1", "CHHOV2", + "CHPIKA5", "CHHOV2", "CHPIKA6", "CHHOV3", "CHPIKA7", "CHHOV2", "CHPIKA8", + "CHHOV1", "CHHOV1", "CHHOV1", "CHHOV2", "CHHOV1", "CHHOV3", "CHHOV1", "CHHOV2" + }; + static const char *selectframesb[SELECTLEN] = { + "CHHOV1", "CHPIKB1", "CHHOV2", "CHPIKB2", "CHHOV3", "CHPIKB3", "CHHOV2", "CHPIKB4", + "CHHOV1", "CHHOV1", "CHHOV1", "CHHOV2", "CHHOV1", "CHHOV3", "CHHOV1", "CHHOV2", + "CHPIKB5", "CHHOV2", "CHPIKB6", "CHHOV3", "CHPIKB7", "CHHOV2", "CHPIKB8", + "CHHOV1", "CHHOV1", "CHHOV1", "CHHOV2", "CHHOV1", "CHHOV3", "CHHOV1", "CHHOV2" + }; + + for (i = 0; i < setup_numplayers; i++) + { + setup_player_t *p = &setup_player[i]; + char letter = 'A' + i; + UINT8 *colormap; + INT16 x, y; + INT16 quadx, quady; + + quadx = 4 * (p->gridx / 3); + quady = 4 * (p->gridy / 3); + + x = 82 + (p->gridx*16) + quadx - 13, + y = 22 + (p->gridy*16) + quady - 12, + + colormap = R_GetTranslationColormap(TC_DEFAULT, (p->color != SKINCOLOR_NONE ? p->color : SKINCOLOR_GREY), GTC_MENUCACHE); + + if (p->mdepth >= CSSTEP_READY) + { + V_DrawMappedPatch(x, y, 0, W_CachePatchName("CHCNFRM0", PU_CACHE), colormap); + } + else if (p->mdepth > CSSTEP_CHARS) + { + V_DrawMappedPatch(x, y, 0, W_CachePatchName(selectframesa[setup_animcounter % SELECTLEN], PU_CACHE), colormap); + V_DrawMappedPatch(x, y, V_TRANSLUCENT, W_CachePatchName(selectframesb[(setup_animcounter-1) % SELECTLEN], PU_CACHE), colormap); + } + else + { + V_DrawMappedPatch(x, y, 0, W_CachePatchName(idleframes[setup_animcounter % IDLELEN], PU_CACHE), colormap); + } + + if (p->mdepth < CSSTEP_READY) + V_DrawMappedPatch(x, y, 0, W_CachePatchName(va("CSELH%c", letter), PU_CACHE), colormap); + } +} + +#undef IDLE +#undef IDLELEN +#undef SELECTLEN + void M_DrawCharacterSelect(void) { UINT8 i, j, k; - UINT16 quadx, quady; + INT16 quadx, quady; SINT8 skin; // We have to loop twice -- first time to draw the drop shadows, a second time to draw the icons. @@ -703,10 +836,19 @@ void M_DrawCharacterSelect(void) colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE); V_DrawMappedPatch(82 + (i*16) + quadx, 22 + (j*16) + quady, 0, facerankprefix[skin], colormap); + + if (setup_chargrid[i][j].numskins > 1) + V_DrawScaledPatch(82 + (i*16) + quadx, 22 + (j*16) + quady + 11, 0, W_CachePatchName("ALTSDOT", PU_CACHE)); } } } + // Explosions when you've made your final selection + M_DrawCharSelectExplosions(); + + // Draw the cursors + M_DrawCharSelectCursors(); + // Draw a preview for each player for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) M_DrawCharSelectPreview(i); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 10dd84f2b..22c064f32 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1290,6 +1290,9 @@ void M_Ticker(void) noFurtherInput = false; } + if (currentMenu->tickroutine) + currentMenu->tickroutine(); + if (dedicated) return; @@ -1374,7 +1377,8 @@ menu_t MessageDef = 0, 0, // x, y (TO HACK) 0, 0, // transition tics M_DrawMessageMenu, // drawing routine -> - NULL + NULL, // ticker routine + NULL // quit routine }; // @@ -1622,7 +1626,11 @@ void M_QuitSRB2(INT32 choice) // Character Select! struct setup_chargrid_s setup_chargrid[9][9]; setup_player_t setup_player[MAXSPLITSCREENPLAYERS]; +struct setup_explosions_s setup_explosions[48]; + UINT8 setup_numplayers = 0; +UINT16 setup_animcounter = 0; + consvar_t *setup_playercvars[MAXSPLITSCREENPLAYERS][SPLITCV_MAX]; void M_CharacterSelectInit(INT32 choice) @@ -1632,14 +1640,19 @@ void M_CharacterSelectInit(INT32 choice) (void)choice; memset(setup_chargrid, -1, sizeof(setup_chargrid)); - memset(setup_player, 0, sizeof(setup_player)); - for (i = 0; i < 9; i++) { for (j = 0; j < 9; j++) setup_chargrid[i][j].numskins = 0; } + memset(setup_player, 0, sizeof(setup_player)); + setup_player[0].mdepth = CSSTEP_CHARS; + setup_numplayers = 1; + + memset(setup_explosions, 0, sizeof(setup_explosions)); + setup_animcounter = 0; + // Keep these in a table for the sake of my sanity later setup_playercvars[0][SPLITCV_SKIN] = &cv_skin; setup_playercvars[1][SPLITCV_SKIN] = &cv_skin2; @@ -1680,12 +1693,56 @@ void M_CharacterSelectInit(INT32 choice) } } - setup_player[0].mdepth = 1; - setup_numplayers = 1; - M_SetupNextMenu(&PLAY_CharSelectDef, false); } +static void M_SetupReadyExplosions(setup_player_t *p) +{ + UINT8 i, j; + UINT8 e = 0; + + while (setup_explosions[e].tics) + { + e++; + if (e == CSEXPLOSIONS) + return; + } + + for (i = 0; i < 3; i++) + { + UINT8 t = 5 + (i*2); + UINT8 offset = (i+1); + + for (j = 0; j < 4; j++) + { + SINT8 x = p->gridx, y = p->gridy; + + switch (j) + { + case 0: x += offset; break; + case 1: x -= offset; break; + case 2: y += offset; break; + case 3: y -= offset; break; + } + + if ((x < 0 || x > 8) || (y < 0 || y > 8)) + continue; + + setup_explosions[e].tics = t; + setup_explosions[e].color = p->color; + setup_explosions[e].x = x; + setup_explosions[e].y = y; + + while (setup_explosions[e].tics) + { + e++; + if (e == CSEXPLOSIONS) + return; + } + } + } +} + static void M_HandleCharacterGrid(INT32 choice, setup_player_t *p) { switch (choice) @@ -1716,17 +1773,19 @@ static void M_HandleCharacterGrid(INT32 choice, setup_player_t *p) break; case KEY_ENTER: if (setup_chargrid[p->gridx][p->gridy].numskins == 0) - S_StartSound(NULL, sfx_s3k5b); + S_StartSound(NULL, sfx_s3k7b); //sfx_s3kb2 else { - p->mdepth++; if (setup_chargrid[p->gridx][p->gridy].numskins == 1) - p->mdepth++; // Skip clones menu + p->mdepth = CSSTEP_COLORS; // Skip clones menu + else + p->mdepth = CSSTEP_ALTS; + S_StartSound(NULL, sfx_s3k5b); } break; case KEY_ESCAPE: - p->mdepth--; + p->mdepth = CSSTEP_NONE; S_StartSound(NULL, sfx_s3k5b); break; default: @@ -1744,24 +1803,24 @@ static void M_HandleCharRotate(INT32 choice, setup_player_t *p) p->clonenum++; if (p->clonenum >= numclones) p->clonenum = 0; - - //p->skin = setup_chargrid[p->gridx][p->gridy].skinlist[p->clonenum]; - S_StartSound(NULL, sfx_s3k5b); + p->rotate = CSROTATETICS; + p->delay = CSROTATETICS; + S_StartSound(NULL, sfx_s3kc3s); break; case KEY_LEFTARROW: p->clonenum--; if (p->clonenum < 0) p->clonenum = numclones-1; - - //p->skin = setup_chargrid[p->gridx][p->gridy].skinlist[p->clonenum]; - S_StartSound(NULL, sfx_s3k5b); + p->rotate = -CSROTATETICS; + p->delay = CSROTATETICS; + S_StartSound(NULL, sfx_s3kc3s); break; case KEY_ENTER: - p->mdepth++; + p->mdepth = CSSTEP_COLORS; S_StartSound(NULL, sfx_s3k5b); break; case KEY_ESCAPE: - p->mdepth--; + p->mdepth = CSSTEP_CHARS; S_StartSound(NULL, sfx_s3k5b); break; default: @@ -1777,22 +1836,29 @@ static void M_HandleColorRotate(INT32 choice, setup_player_t *p) p->color++; if (p->color >= MAXSKINCOLORS) p->color = 1; - S_StartSound(NULL, sfx_s3k5b); + p->rotate = CSROTATETICS; + //p->delay = CSROTATETICS; + S_StartSound(NULL, sfx_s3kc3s); break; case KEY_LEFTARROW: p->color--; if (p->color < 1) p->color = MAXSKINCOLORS-1; - S_StartSound(NULL, sfx_s3k5b); + p->rotate = -CSROTATETICS; + //p->delay = CSROTATETICS; + S_StartSound(NULL, sfx_s3kc3s); break; case KEY_ENTER: - p->mdepth++; - S_StartSound(NULL, sfx_s3k5b); + p->mdepth = CSSTEP_READY; + p->delay = TICRATE; + M_SetupReadyExplosions(p); + S_StartSound(NULL, sfx_s3k4e); break; case KEY_ESCAPE: - p->mdepth--; - if (setup_chargrid[p->gridx][p->gridy].numskins <= 1) - p->mdepth--; // Skip clones menu + if (setup_chargrid[p->gridx][p->gridy].numskins == 1) + p->mdepth = CSSTEP_CHARS; // Skip clones menu + else + p->mdepth = CSSTEP_ALTS; S_StartSound(NULL, sfx_s3k5b); break; default: @@ -1811,76 +1877,98 @@ void M_CharacterSelectHandler(INT32 choice) if (i > 0) break; // temp - switch (p->mdepth) + if (p->delay == 0) { - case 0: // Enter Game - if (choice == KEY_ENTER) - setup_player[i].mdepth++; - break; - case 1: // Character Select grid - M_HandleCharacterGrid(choice, p); - break; - case 2: // Select clone - M_HandleCharRotate(choice, p); - break; - case 3: // Select color - M_HandleColorRotate(choice, p); - break; - default: // Unready - if (choice == KEY_ESCAPE) - setup_player[i].mdepth--; - break; + switch (p->mdepth) + { + case CSSTEP_NONE: // Enter Game + if (choice == KEY_ENTER) + p->mdepth = CSSTEP_CHARS; + break; + case CSSTEP_CHARS: // Character Select grid + M_HandleCharacterGrid(choice, p); + break; + case CSSTEP_ALTS: // Select clone + M_HandleCharRotate(choice, p); + break; + case CSSTEP_COLORS: // Select color + M_HandleColorRotate(choice, p); + break; + case CSSTEP_READY: + default: // Unready + if (choice == KEY_ESCAPE) + p->mdepth = CSSTEP_COLORS; + break; + } } - if (p->mdepth < 2) + if (p->mdepth < CSSTEP_ALTS) p->clonenum = 0; // Just makes it easier to access later p->skin = setup_chargrid[p->gridx][p->gridy].skinlist[p->clonenum]; - if (p->mdepth < 3) + if (p->mdepth < CSSTEP_COLORS) p->color = skins[p->skin].prefcolor; - if (p->mdepth == 0) + if (p->mdepth == CSSTEP_NONE) break; else setup_numplayers = i+1; } // If the first player unjoins, then we get outta here - if (setup_player[0].mdepth == 0) + if (setup_player[0].mdepth == CSSTEP_NONE) { if (currentMenu->prevMenu) M_SetupNextMenu(currentMenu->prevMenu, false); else M_ClearMenus(true); } - else - { - boolean setupnext = false; +} +void M_CharacterSelectTick(void) +{ + UINT8 i; + boolean setupnext = true; + + setup_animcounter++; + + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + if (setup_player[i].delay) + setup_player[i].delay--; + + if (setup_player[i].rotate > 0) + setup_player[i].rotate--; + else if (setup_player[i].rotate < 0) + setup_player[i].rotate++; + + if (i >= setup_numplayers) + continue; + + if (setup_player[i].mdepth < CSSTEP_READY || setup_player[i].delay > 0) + { + // Someone's not ready yet. + setupnext = false; + } + } + + for (i = 0; i < CSEXPLOSIONS; i++) + { + if (setup_explosions[i].tics > 0) + setup_explosions[i].tics--; + } + + if (setupnext) + { for (i = 0; i < setup_numplayers; i++) { - if (setup_player[i].mdepth >= 4) - setupnext = true; - else - { - // Someone's not ready yet. - setupnext = false; - break; - } + CV_StealthSetValue(setup_playercvars[i][SPLITCV_SKIN], setup_player[i].skin); + CV_StealthSetValue(setup_playercvars[i][SPLITCV_COLOR], setup_player[i].color); } - if (setupnext) - { - for (i = 0; i < setup_numplayers; i++) - { - CV_StealthSetValue(setup_playercvars[i][SPLITCV_SKIN], setup_player[i].skin); - CV_StealthSetValue(setup_playercvars[i][SPLITCV_COLOR], setup_player[i].color); - } - - M_SetupNextMenu(&PLAY_MainDef, false); - } + M_SetupNextMenu(&PLAY_MainDef, false); } } diff --git a/src/v_video.c b/src/v_video.c index 8cb0751f7..c85fea3c7 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -1792,6 +1792,74 @@ void V_DrawRightAlignedGamemodeString(INT32 x, INT32 y, INT32 option, const char x -= V_GamemodeStringWidth(string, option); V_DrawGamemodeString(x, y, option, string, color); } + +void V_DrawFileString(INT32 x, INT32 y, INT32 option, const char *string) +{ + INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, left = 0; + const char *ch = string; + + option &= ~V_FLIP; + + if (option & V_NOSCALESTART) + { + dupx = vid.dupx; + dupy = vid.dupy; + scrwidth = vid.width; + } + else + { + dupx = dupy = 1; + scrwidth = vid.width/vid.dupx; + left = (scrwidth - BASEVIDWIDTH)/2; + } + + for (;;ch++) + { + if (!*ch) + break; + + if (*ch == '\n') + { + cx = x; + cy += 14*dupy; + + continue; + } + + c = toupper(*ch) - AZ_FONTSTART; + + // character does not exist or is a space + if (c < 0 || c >= AZ_FONTSIZE || !file_font[c]) + continue; + + w = (SHORT(file_font[c]->width) - 3) * dupx; + + if (cx > scrwidth) + break; + + if (cx+left + w < 0) //left boundary check + { + cx += w; + continue; + } + + V_DrawFixedPatch(cx<= AZ_FONTSIZE || !file_font[c]) + continue; + else + w += SHORT(file_font[c]->width) - 3; + } + + return w; +} + boolean *heatshifter = NULL; INT32 lastheight = 0; INT32 heatindex[MAXSPLITSCREENPLAYERS] = {0, 0, 0, 0}; diff --git a/src/v_video.h b/src/v_video.h index 3c2ee8478..18968b77b 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -183,6 +183,9 @@ void V_DrawKartString(INT32 x, INT32 y, INT32 option, const char *string); void V_DrawGamemodeString(INT32 x, INT32 y, INT32 option, const char *string, UINT8 color); void V_DrawCenteredGamemodeString(INT32 x, INT32 y, INT32 option, const char *string, UINT8 color); void V_DrawRightAlignedGamemodeString(INT32 x, INT32 y, INT32 option, const char *string, UINT8 color); +void V_DrawFileString(INT32 x, INT32 y, INT32 option, const char *string); +void V_DrawCenteredFileString(INT32 x, INT32 y, INT32 option, const char *string); +void V_DrawRightAlignedFileString(INT32 x, INT32 y, INT32 option, const char *string); // draw a string using the hu_font, 0.5x scale void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string); @@ -219,6 +222,7 @@ INT32 V_ThinStringWidth(const char *string, INT32 option); // SRB2Kart INT32 V_GamemodeStringWidth(const char *string, INT32 option); +INT32 V_FileStringWidth(const char *string, INT32 option); void V_DoPostProcessor(INT32 view, postimg_t type, INT32 param); From 7d8a512d2aa698cfcbbf031425f616d9f239c784 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Thu, 3 Oct 2019 00:13:09 -0400 Subject: [PATCH 005/379] Several minor fixes & tweaks for character select --- src/k_menudraw.c | 27 ++++++++++++++++----------- src/k_menufunc.c | 38 ++++++++++++++++++++++++++++---------- 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 556955d42..8c433c5d5 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -536,7 +536,7 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) angle_t ang = ((i+1)/2) * angamt; patch_t *patch = NULL; UINT8 *colormap; - fixed_t radius = 24<mdepth == CSSTEP_ALTS) @@ -553,6 +553,10 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) skin = setup_chargrid[p->gridx][p->gridy].skinlist[n]; patch = facerankprefix[skin]; colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE); + radius = 24<width) << FRACBITS) >> 1; + cy -= (SHORT(patch->height) << FRACBITS) >> 1; } else { @@ -580,11 +584,11 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) else patch = W_CachePatchName("COLORSP0", PU_CACHE); - radius -= SHORT(patch->width) << FRACBITS; - } + radius = 28<width) << FRACBITS; - cx -= (SHORT(patch->width) << FRACBITS) >> 1; - cy -= (SHORT(patch->height) << FRACBITS) >> 1; + cx -= (SHORT(patch->width) << FRACBITS) >> 1; + } if (subtract) ang = (signed)(ANGLE_90 - ang); @@ -677,7 +681,7 @@ static void M_DrawCharSelectPreview(UINT8 num) if ((setup_animcounter/10) & 1) { - if (p->mdepth == CSSTEP_NONE) + if (p->mdepth == CSSTEP_NONE && num == setup_numplayers) V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PSTART", PU_CACHE)); //else if (p->mdepth >= CSSTEP_READY) // V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PREADY", PU_CACHE)); @@ -733,10 +737,10 @@ static void M_DrawCharSelectCursors(void) "CHHOV1", "CHHOV1", "CHHOV1", "CHHOV2", "CHHOV1", "CHHOV3", "CHHOV1", "CHHOV2" }; static const char *selectframesb[SELECTLEN] = { - "CHHOV1", "CHPIKB1", "CHHOV2", "CHPIKB2", "CHHOV3", "CHPIKB3", "CHHOV2", "CHPIKB4", - "CHHOV1", "CHHOV1", "CHHOV1", "CHHOV2", "CHHOV1", "CHHOV3", "CHHOV1", "CHHOV2", - "CHPIKB5", "CHHOV2", "CHPIKB6", "CHHOV3", "CHPIKB7", "CHHOV2", "CHPIKB8", - "CHHOV1", "CHHOV1", "CHHOV1", "CHHOV2", "CHHOV1", "CHHOV3", "CHHOV1", "CHHOV2" + NULL, "CHPIKB1", NULL, "CHPIKB2", NULL, "CHPIKB3", NULL, "CHPIKB4", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "CHPIKB5", NULL, "CHPIKB6", NULL, "CHPIKB7", NULL, "CHPIKB8", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; for (i = 0; i < setup_numplayers; i++) @@ -762,7 +766,8 @@ static void M_DrawCharSelectCursors(void) else if (p->mdepth > CSSTEP_CHARS) { V_DrawMappedPatch(x, y, 0, W_CachePatchName(selectframesa[setup_animcounter % SELECTLEN], PU_CACHE), colormap); - V_DrawMappedPatch(x, y, V_TRANSLUCENT, W_CachePatchName(selectframesb[(setup_animcounter-1) % SELECTLEN], PU_CACHE), colormap); + if (selectframesb[(setup_animcounter-1) % SELECTLEN] != NULL) + V_DrawMappedPatch(x, y, V_TRANSLUCENT, W_CachePatchName(selectframesb[(setup_animcounter-1) % SELECTLEN], PU_CACHE), colormap); } else { diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 22c064f32..e13d87b5c 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1684,7 +1684,7 @@ void M_CharacterSelectInit(INT32 choice) for (j = 0; j < MAXSPLITSCREENPLAYERS; j++) { - if (setup_playercvars[j][SPLITCV_SKIN]->value == i) + if (!strcmp(setup_playercvars[j][SPLITCV_SKIN]->string, skins[i].name)) { setup_player[j].gridx = x; setup_player[j].gridy = y; @@ -1743,7 +1743,7 @@ static void M_SetupReadyExplosions(setup_player_t *p) } } -static void M_HandleCharacterGrid(INT32 choice, setup_player_t *p) +static void M_HandleCharacterGrid(INT32 choice, setup_player_t *p, UINT8 num) { switch (choice) { @@ -1785,8 +1785,15 @@ static void M_HandleCharacterGrid(INT32 choice, setup_player_t *p) } break; case KEY_ESCAPE: - p->mdepth = CSSTEP_NONE; - S_StartSound(NULL, sfx_s3k5b); + if (num == setup_numplayers-1) + { + p->mdepth = CSSTEP_NONE; + S_StartSound(NULL, sfx_s3k5b); + } + else + { + S_StartSound(NULL, sfx_s3kb2); + } break; default: break; @@ -1838,7 +1845,7 @@ static void M_HandleColorRotate(INT32 choice, setup_player_t *p) p->color = 1; p->rotate = CSROTATETICS; //p->delay = CSROTATETICS; - S_StartSound(NULL, sfx_s3kc3s); + S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s break; case KEY_LEFTARROW: p->color--; @@ -1846,7 +1853,7 @@ static void M_HandleColorRotate(INT32 choice, setup_player_t *p) p->color = MAXSKINCOLORS-1; p->rotate = -CSROTATETICS; //p->delay = CSROTATETICS; - S_StartSound(NULL, sfx_s3kc3s); + S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s break; case KEY_ENTER: p->mdepth = CSSTEP_READY; @@ -1882,11 +1889,14 @@ void M_CharacterSelectHandler(INT32 choice) switch (p->mdepth) { case CSSTEP_NONE: // Enter Game - if (choice == KEY_ENTER) + if (choice == KEY_ENTER && i == setup_numplayers) + { p->mdepth = CSSTEP_CHARS; + S_StartSound(NULL, sfx_s3k65); + } break; case CSSTEP_CHARS: // Character Select grid - M_HandleCharacterGrid(choice, p); + M_HandleCharacterGrid(choice, p, i); break; case CSSTEP_ALTS: // Select clone M_HandleCharRotate(choice, p); @@ -1897,7 +1907,10 @@ void M_CharacterSelectHandler(INT32 choice) case CSSTEP_READY: default: // Unready if (choice == KEY_ESCAPE) + { p->mdepth = CSSTEP_COLORS; + S_StartSound(NULL, sfx_s3k5b); + } break; } } @@ -1910,8 +1923,12 @@ void M_CharacterSelectHandler(INT32 choice) if (p->mdepth < CSSTEP_COLORS) p->color = skins[p->skin].prefcolor; + } - if (p->mdepth == CSSTEP_NONE) + // Setup new numplayers + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + if (setup_player[i].mdepth == CSSTEP_NONE) break; else setup_numplayers = i+1; @@ -1964,10 +1981,11 @@ void M_CharacterSelectTick(void) { for (i = 0; i < setup_numplayers; i++) { - CV_StealthSetValue(setup_playercvars[i][SPLITCV_SKIN], setup_player[i].skin); + CV_StealthSet(setup_playercvars[i][SPLITCV_SKIN], skins[setup_player[i].skin].name); CV_StealthSetValue(setup_playercvars[i][SPLITCV_COLOR], setup_player[i].color); } + CV_StealthSetValue(&cv_splitplayers, setup_numplayers); M_SetupNextMenu(&PLAY_MainDef, false); } } From 67e63442da2d629d7e9c13250fc15d3f4c6e20fa Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Fri, 4 Oct 2019 00:18:34 -0400 Subject: [PATCH 006/379] Start on Cup/Level selects --- src/d_main.c | 2 +- src/hu_stuff.c | 19 +++- src/hu_stuff.h | 1 + src/k_menu.h | 61 +++++++++-- src/k_menudef.c | 89 ++++++++++++---- src/k_menudraw.c | 250 ++++++++++++++++++++++++++++++++++++------- src/k_menufunc.c | 270 ++++++++++++++++++++++++++++++++++++++++++++--- src/v_video.c | 188 +++++++++++++++++++++++++++++++++ src/v_video.h | 8 ++ 9 files changed, 800 insertions(+), 88 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 1758c4a16..1beff2292 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -318,7 +318,7 @@ static void D_Display(void) F_WipeStartScreen(); V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); F_WipeEndScreen(); - F_RunWipe(wipedefs[wipedefindex], gamestate != GS_MENU, "FADEMAP0", false); + F_RunWipe(wipedefs[wipedefindex], gamestate != GS_MENU, "FADEMAP0", false, false); } if (gamestate != GS_LEVEL && rendermode != render_none) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 3be1d6473..455d90394 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -72,6 +72,8 @@ patch_t *nightsnum[10]; // 0-9 patch_t *kart_font[KART_FONTSIZE]; // SRB2kart patch_t *gamemode_font[AZ_FONTSIZE]; patch_t *file_font[AZ_FONTSIZE]; +patch_t *title_font_high[LT_FONTSIZE]; +patch_t *title_font_low[LT_FONTSIZE]; // Level title and credits fonts patch_t *lt_font[LT_FONTSIZE]; @@ -261,15 +263,26 @@ void HU_LoadGraphics(void) // j = LT_FONTSTART; - for (i = 0; i < LT_FONTSIZE; i++) + for (i = 0; i < LT_FONTSIZE; i++, j++) { sprintf(buffer, "LTFNT%.3d", j); - j++; - if (W_CheckNumForName(buffer) == LUMPERROR) lt_font[i] = NULL; else lt_font[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); + + // Level select title fonts + sprintf(buffer, "THIFN%.3d", j); + if (W_CheckNumForName(buffer) == LUMPERROR) + title_font_high[i] = NULL; + else + title_font_high[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); + + sprintf(buffer, "TLWFN%.3d", j); + if (W_CheckNumForName(buffer) == LUMPERROR) + title_font_low[i] = NULL; + else + title_font_low[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); } // cache the credits font for entire game execution (why not?) diff --git a/src/hu_stuff.h b/src/hu_stuff.h index 9c9206f83..0df283b29 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -86,6 +86,7 @@ extern boolean chat_on; extern patch_t *hu_font[HU_FONTSIZE], *tny_font[HU_FONTSIZE]; extern patch_t *kart_font[KART_FONTSIZE]; // SRB2kart extern patch_t *gamemode_font[AZ_FONTSIZE], *file_font[AZ_FONTSIZE]; +extern patch_t *title_font_high[LT_FONTSIZE], *title_font_low[LT_FONTSIZE]; extern patch_t *tallnum[10]; extern patch_t *pingnum[10]; extern patch_t *pinggfx[5]; diff --git a/src/k_menu.h b/src/k_menu.h index d57618a4a..20b9e778d 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -108,8 +108,8 @@ typedef struct menu_s INT16 lastOn; // last item user was on in menu menuitem_t *menuitems; // menu items INT16 x, y; // x, y of menu - INT16 transitionInTics; // tics for transitions in - INT16 transitionOutTics; // tics for transitions out + INT16 transitionID; // only transition if IDs match + INT16 transitionTics; // tics for transitions out void (*drawroutine)(void); // draw routine void (*tickroutine)(void); // ticker routine boolean (*quitroutine)(void); // called before quit a menu return true if we can @@ -139,18 +139,27 @@ typedef enum quitkart } main_e; -extern menuitem_t PLAY_MainMenu[]; -extern menu_t PLAY_MainDef; - extern menuitem_t PLAY_CharSelect[]; extern menu_t PLAY_CharSelectDef; +extern menuitem_t PLAY_MainMenu[]; +extern menu_t PLAY_MainDef; + extern menuitem_t PLAY_Gamemodes[]; extern menu_t PLAY_GamemodesDef; extern menuitem_t PLAY_RaceGamemodesMenu[]; extern menu_t PLAY_RaceGamemodesDef; +extern menuitem_t PLAY_CupSelect[]; +extern menu_t PLAY_CupSelectDef; + +extern menuitem_t PLAY_LevelSelect[]; +extern menu_t PLAY_LevelSelectDef; + +extern menuitem_t PLAY_BattleGamemodesMenu[]; +extern menu_t PLAY_BattleGamemodesDef; + extern menuitem_t PAUSE_PlaybackMenu[]; extern menu_t PAUSE_PlaybackMenuDef; @@ -171,11 +180,9 @@ typedef enum playback_quit } playback_e; - // K_MENUFUNC.C extern menu_t *currentMenu; - extern char dummystaffname[22]; extern INT16 itemOn; // menu item skull is on, Hack by Tails 09-18-2002 @@ -248,13 +255,13 @@ typedef struct setup_player_s extern setup_player_t setup_player[MAXSPLITSCREENPLAYERS]; extern UINT8 setup_numplayers; -extern UINT16 setup_animcounter; +extern tic_t setup_animcounter; + +#define CSROTATETICS 6 // The selection spawns 3 explosions in 4 directions, and there's 4 players -- 3 * 4 * 4 = 48 #define CSEXPLOSIONS 48 -#define CSROTATETICS 6 - extern struct setup_explosions_s { UINT8 x, y; UINT8 tics; @@ -275,6 +282,35 @@ void M_CharacterSelectHandler(INT32 choice); void M_CharacterSelectTick(void); boolean M_CharacterSelectQuit(void); +#define CUPS_COLUMNS 7 +#define CUPS_ROWS 2 +#define CUPS_MAPSPERCUP 5 +#define CUPS_MAX (NUMMAPS / CUPS_MAPSPERCUP) +#define CUPS_PAGES (CUPS_MAX / (CUPS_COLUMNS * CUPS_ROWS)) + +extern struct levellist_cupgrid_s { + UINT8 numcups; + SINT8 x, y; + SINT8 pageno; + tic_t previewanim; +} levellist_cupgrid; + +extern struct levellist_scroll_s { + SINT8 cupid; + SINT8 cursor; + UINT16 y; + UINT16 dest; +} levellist_scroll; + +boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt); +void M_LevelSelectInit(INT32 choice); + +void M_CupSelectHandler(INT32 choice); +void M_CupSelectTick(void); + +void M_LevelSelectHandler(INT32 choice); +void M_LevelSelectTick(void); + void M_EndModeAttackRun(void); void M_SetPlaybackMenuPointer(void); void M_PlaybackRewind(INT32 choice); @@ -298,6 +334,9 @@ void M_DrawImageDef(void); void M_DrawCharacterSelect(void); +void M_DrawCupSelect(void); +void M_DrawLevelSelect(void); + void M_DrawPlaybackMenu(void); // These defines make it a little easier to make menus @@ -322,7 +361,7 @@ void M_DrawPlaybackMenu(void); 0,\ source,\ 0, 0,\ - 10, 10,\ + 1, 10,\ M_DrawKartGamemodeMenu,\ NULL,\ NULL\ diff --git a/src/k_menudef.c b/src/k_menudef.c index 5dc155fd0..3030340ba 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -19,16 +19,16 @@ menuitem_t MainMenu[] = { {IT_STRING | IT_CALL, "Play", "Cut to the chase and start the race!", - NULL, M_CharacterSelectInit, 48, 0}, + NULL, M_CharacterSelectInit, 0, 0}, {IT_STRING, "Extra", "Check out some bonus features.", - NULL, NULL, 80, 0}, + NULL, NULL, 0, 0}, {IT_STRING, "Option", "Configure your controls, settings, and preferences.", - NULL, NULL, 112, 0}, + NULL, NULL, 0, 0}, {IT_STRING | IT_CALL, "Quit", "Exit SRB2Kart.", - NULL, M_QuitSRB2, 160, 0}, + NULL, M_QuitSRB2, 0, 0}, }; menu_t MainDef = KARTGAMEMODEMENU(MainMenu, NULL); @@ -57,45 +57,96 @@ menu_t PLAY_CharSelectDef = { menuitem_t PLAY_MainMenu[] = { {IT_STRING | IT_SUBMENU, "Local Play", "Play only on this computer.", - NULL, &PLAY_GamemodesDef, 64, 0}, + NULL, &PLAY_GamemodesDef, 0, 0}, {IT_STRING, "Online", "Connect to other computers.", - NULL, NULL, 96, 0}, + NULL, NULL, 0, 0}, - {IT_STRING | IT_CALL, "Back", NULL, NULL, M_GoBack, 160, 0}, + {IT_STRING | IT_CALL, "Back", NULL, NULL, M_GoBack, 0, 0}, }; menu_t PLAY_MainDef = KARTGAMEMODEMENU(PLAY_MainMenu, &PLAY_CharSelectDef); menuitem_t PLAY_GamemodesMenu[] = { - {IT_STRING | IT_SUBMENU, "Race", "A competition for the best time!", - NULL, &PLAY_RaceGamemodesDef, 64, 0}, + {IT_STRING | IT_SUBMENU, "Race", "A contest to see who's the fastest of them all!", + NULL, &PLAY_RaceGamemodesDef, 0, 0}, - {IT_STRING, "Battle", "Clash against other players in a survival match!", - NULL, NULL, 96, 0}, + {IT_STRING | IT_SUBMENU, "Battle", "Sharpen your item usage in these special Battle zones!", + NULL, &PLAY_BattleGamemodesDef, 0, 0}, - {IT_STRING | IT_CALL, "Back", NULL, NULL, M_GoBack, 160, 0}, + {IT_STRING | IT_CALL, "Back", NULL, NULL, M_GoBack, 0, 0}, }; menu_t PLAY_GamemodesDef = KARTGAMEMODEMENU(PLAY_GamemodesMenu, &PLAY_MainDef); +// RACE + menuitem_t PLAY_RaceGamemodesMenu[] = { - {IT_STRING, "Grand Prix", "Compete for the best rank over five races!", - NULL, NULL, 48, 0}, + {IT_STRING | IT_CALL, "Grand Prix", "Compete for the best rank over five races!", + NULL, M_LevelSelectInit, 0, 0}, - {IT_STRING, "Match Race", "Pick your own settings in a specialized single race.", - NULL, NULL, 80, 0}, + {IT_STRING | IT_CALL, "Match Race", "Play by your own rules in a specialized, single race!", + NULL, M_LevelSelectInit, 1, 0}, - {IT_STRING, "Time Attack", "Race against ghosts for the best time, no fluff.", - NULL, NULL, 112, 0}, + {IT_STRING | IT_CALL, "Time Attack", "Record your best time on any track!", + NULL, M_LevelSelectInit, 2, 0}, - {IT_STRING | IT_CALL, "Back", NULL, NULL, M_GoBack, 160, 0}, + {IT_STRING | IT_CALL, "Back", NULL, NULL, M_GoBack, 0, 0}, }; menu_t PLAY_RaceGamemodesDef = KARTGAMEMODEMENU(PLAY_RaceGamemodesMenu, &PLAY_GamemodesDef); +menuitem_t PLAY_CupSelect[] = +{ + {IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, M_CupSelectHandler, 0, 0}, +}; + +menu_t PLAY_CupSelectDef = { + sizeof(PLAY_CupSelect) / sizeof(menuitem_t), + &PLAY_RaceGamemodesDef, + 0, + PLAY_CupSelect, + 0, 0, + 2, 10, + M_DrawCupSelect, + M_CupSelectTick, + NULL +}; + +menuitem_t PLAY_LevelSelect[] = +{ + {IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, M_LevelSelectHandler, 0, 0}, +}; + +menu_t PLAY_LevelSelectDef = { + sizeof(PLAY_LevelSelect) / sizeof(menuitem_t), + &PLAY_CupSelectDef, + 0, + PLAY_LevelSelect, + 0, 0, + 2, 10, + M_DrawLevelSelect, + M_LevelSelectTick, + NULL +}; + +// BATTLE + +menuitem_t PLAY_BattleGamemodesMenu[] = +{ + {IT_STRING | IT_CALL, "Survival", "It's last hedgehog standing in this free-for-all!", + NULL, M_LevelSelectInit, 3, 0}, + + {IT_STRING | IT_CALL, "Time Attack", "Bust up all of the capsules in record time!", + NULL, M_LevelSelectInit, 4, 0}, + + {IT_STRING | IT_CALL, "Back", NULL, NULL, M_GoBack, 0, 0}, +}; + +menu_t PLAY_BattleGamemodesDef = KARTGAMEMODEMENU(PLAY_BattleGamemodesMenu, &PLAY_GamemodesDef); + // ------------------- // In-game/pause menus // ------------------- diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 8c433c5d5..b9f271615 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -135,7 +135,7 @@ void M_Drawer(void) if (menuwipe) { F_WipeEndScreen(); - F_RunWipe(wipedefs[wipe_menu_final], false, "FADEMAP0", true); + F_RunWipe(wipedefs[wipe_menu_final], false, "FADEMAP0", true, false); menuwipe = false; } @@ -380,6 +380,7 @@ void M_DrawGenericMenu(void) void M_DrawKartGamemodeMenu(void) { INT16 i, x = 170; + UINT8 n = currentMenu->numitems-1; M_DrawMenuTooltips(); M_DrawMenuPreviews(); @@ -389,10 +390,17 @@ void M_DrawKartGamemodeMenu(void) for (i = 0; i < currentMenu->numitems; i++) { + INT16 y; + + if (i == n) + y = 160; + else + y = 80 - (16 * (n-1)) + (32 * i); + switch (currentMenu->menuitems[i].status & IT_DISPLAY) { case IT_STRING: - V_DrawRightAlignedGamemodeString(x, currentMenu->menuitems[i].mvar1, 0, currentMenu->menuitems[i].text, + V_DrawRightAlignedGamemodeString(x, y, 0, currentMenu->menuitems[i].text, (i == itemOn) ? SKINCOLOR_PLAGUE : SKINCOLOR_PIGEON); break; } @@ -724,9 +732,8 @@ static void M_DrawCharSelectExplosions(void) #define IDLELEN 8 #define SELECTLEN (8 + IDLELEN + 7 + IDLELEN) -static void M_DrawCharSelectCursors(void) +static void M_DrawCharSelectCursor(UINT8 num) { - UINT8 i; static const char *idleframes[IDLELEN] = { "CHHOV1", "CHHOV1", "CHHOV1", "CHHOV2", "CHHOV1", "CHHOV3", "CHHOV1", "CHHOV2" }; @@ -743,40 +750,37 @@ static void M_DrawCharSelectCursors(void) NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; - for (i = 0; i < setup_numplayers; i++) + setup_player_t *p = &setup_player[num]; + char letter = 'A' + num; + UINT8 *colormap; + INT16 x, y; + INT16 quadx, quady; + + quadx = 4 * (p->gridx / 3); + quady = 4 * (p->gridy / 3); + + x = 82 + (p->gridx*16) + quadx - 13, + y = 22 + (p->gridy*16) + quady - 12, + + colormap = R_GetTranslationColormap(TC_DEFAULT, (p->color != SKINCOLOR_NONE ? p->color : SKINCOLOR_GREY), GTC_MENUCACHE); + + if (p->mdepth >= CSSTEP_READY) { - setup_player_t *p = &setup_player[i]; - char letter = 'A' + i; - UINT8 *colormap; - INT16 x, y; - INT16 quadx, quady; - - quadx = 4 * (p->gridx / 3); - quady = 4 * (p->gridy / 3); - - x = 82 + (p->gridx*16) + quadx - 13, - y = 22 + (p->gridy*16) + quady - 12, - - colormap = R_GetTranslationColormap(TC_DEFAULT, (p->color != SKINCOLOR_NONE ? p->color : SKINCOLOR_GREY), GTC_MENUCACHE); - - if (p->mdepth >= CSSTEP_READY) - { - V_DrawMappedPatch(x, y, 0, W_CachePatchName("CHCNFRM0", PU_CACHE), colormap); - } - else if (p->mdepth > CSSTEP_CHARS) - { - V_DrawMappedPatch(x, y, 0, W_CachePatchName(selectframesa[setup_animcounter % SELECTLEN], PU_CACHE), colormap); - if (selectframesb[(setup_animcounter-1) % SELECTLEN] != NULL) - V_DrawMappedPatch(x, y, V_TRANSLUCENT, W_CachePatchName(selectframesb[(setup_animcounter-1) % SELECTLEN], PU_CACHE), colormap); - } - else - { - V_DrawMappedPatch(x, y, 0, W_CachePatchName(idleframes[setup_animcounter % IDLELEN], PU_CACHE), colormap); - } - - if (p->mdepth < CSSTEP_READY) - V_DrawMappedPatch(x, y, 0, W_CachePatchName(va("CSELH%c", letter), PU_CACHE), colormap); + V_DrawMappedPatch(x, y, 0, W_CachePatchName("CHCNFRM0", PU_CACHE), colormap); } + else if (p->mdepth > CSSTEP_CHARS) + { + V_DrawMappedPatch(x, y, 0, W_CachePatchName(selectframesa[setup_animcounter % SELECTLEN], PU_CACHE), colormap); + if (selectframesb[(setup_animcounter-1) % SELECTLEN] != NULL) + V_DrawMappedPatch(x, y, V_TRANSLUCENT, W_CachePatchName(selectframesb[(setup_animcounter-1) % SELECTLEN], PU_CACHE), colormap); + } + else + { + V_DrawMappedPatch(x, y, 0, W_CachePatchName(idleframes[setup_animcounter % IDLELEN], PU_CACHE), colormap); + } + + if (p->mdepth < CSSTEP_READY) + V_DrawMappedPatch(x, y, 0, W_CachePatchName(va("CSELH%c", letter), PU_CACHE), colormap); } #undef IDLE @@ -786,6 +790,7 @@ static void M_DrawCharSelectCursors(void) void M_DrawCharacterSelect(void) { UINT8 i, j, k; + UINT8 priority = setup_animcounter % setup_numplayers; INT16 quadx, quady; SINT8 skin; @@ -851,12 +856,177 @@ void M_DrawCharacterSelect(void) // Explosions when you've made your final selection M_DrawCharSelectExplosions(); - // Draw the cursors - M_DrawCharSelectCursors(); - - // Draw a preview for each player for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + // Draw a preview for each player M_DrawCharSelectPreview(i); + + if (i >= setup_numplayers) + continue; + + // Draw the cursors + if (i != priority) + M_DrawCharSelectCursor(i); + } + + // Draw the priority player over the other ones + M_DrawCharSelectCursor(priority); +} + +// LEVEL SELECT + +static void M_DrawCupPreview(INT16 y, UINT8 cupnum) +{ + UINT8 i; + INT16 x = -(levellist_cupgrid.previewanim % 82); + + V_DrawFill(0, y, BASEVIDWIDTH, 54, 31); + + for (i = 0; i < 5; i++) + { + lumpnum_t lumpnum; + patch_t *PictureOfLevel; + UINT8 lvloff = (i + (levellist_cupgrid.previewanim / 82)) % 5; + + lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(1 + (cupnum * 5) + lvloff))); + if (lumpnum != LUMPERROR) + PictureOfLevel = W_CachePatchNum(lumpnum, PU_CACHE); + else + PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE); + + V_DrawSmallScaledPatch(x + 1 + (i*82), y+2, 0, PictureOfLevel); + } +} + +void M_DrawCupSelect(void) +{ + UINT8 i, j; + + for (i = 0; i < CUPS_COLUMNS; i++) + { + for (j = 0; j < CUPS_ROWS; j++) + { + V_DrawFill(14 + (i*42), 22 + (j*40) - (15*menutransition.tics), 39, 38, 31); + } + } + + V_DrawScaledPatch(14 + (levellist_cupgrid.x*42) - 4, + 22 + (levellist_cupgrid.y*40) - 4 - (12*menutransition.tics), + 0, W_CachePatchName("CUPCURS", PU_CACHE) + ); + + V_DrawScaledPatch(0, 120 - (12*menutransition.tics), 0, W_CachePatchName("MENUHINT", PU_CACHE)); + M_DrawCupPreview(146 + (12*menutransition.tics), levellist_cupgrid.x + (levellist_cupgrid.y * CUPS_COLUMNS)); + + V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, 126 - (12*menutransition.tics), 0, "SNEAKER CUP"); +} + +static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map) +{ + static char word1[21]; + static char word2[21]; + UINT8 word1len = 0; + UINT8 word2len = 0; + INT16 x2 = x; + UINT8 i; + + if (!mapheaderinfo[map]->lvlttl[0]) + return; + + if (mapheaderinfo[map]->zonttl[0]) + { + strcpy(word1, mapheaderinfo[map]->lvlttl); + strcpy(word2, mapheaderinfo[map]->zonttl); + } + else + { + boolean donewithone = false; + + for (i = 0; i < 21; i++) + { + if (!mapheaderinfo[map]->lvlttl[i]) + { + if (donewithone) + word2[word2len] = '\0'; + else + word1[word1len] = '\0'; + break; + } + + if (mapheaderinfo[map]->lvlttl[i] == ' ') + { + if (!donewithone) + { + word1[word1len] = '\0'; + donewithone = true; + continue; + } + } + + if (donewithone) + { + word2[word2len] = mapheaderinfo[map]->lvlttl[i]; + word2len++; + } + else + { + word1[word1len] = mapheaderinfo[map]->lvlttl[i]; + word1len++; + } + } + } + + for (i = 0; i < 2; i++) + { + INT32 c; + if (i >= word1len) + break; + c = toupper(word1[i]) - LT_FONTSTART; + x2 += SHORT(title_font_high[c]->width) - 4; + } + + if (word1len) + V_DrawLSTitleHighString(x, y, 0, word1); + if (word2len) + V_DrawLSTitleLowString(x2, y+28, 0, word2); +} + +void M_DrawLevelSelect(void) +{ + UINT8 i; + INT16 t = (32*menutransition.tics); + INT16 y = 80 - (12 * levellist_scroll.y); + + for (i = 0; i < 5; i++) + { + lumpnum_t lumpnum; + patch_t *PictureOfLevel; + UINT8 *colormap = NULL; + INT16 map = 1 + (levellist_scroll.cupid * 5) + i; + + lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(map))); + if (lumpnum != LUMPERROR) + PictureOfLevel = W_CachePatchNum(lumpnum, PU_CACHE); + else + PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE); + + if (i == levellist_scroll.cursor && ((skullAnimCounter / 4) & 1)) + V_DrawScaledPatch(3+t, y, 0, W_CachePatchName("LVLSEL2", PU_CACHE)); + else + V_DrawScaledPatch(3+t, y, 0, W_CachePatchName("LVLSEL", PU_CACHE)); + + if (i != levellist_scroll.cursor) + colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_MENUCACHE); + + V_DrawSmallMappedPatch(9+t, y+6, 0, PictureOfLevel, colormap); + + M_DrawHighLowLevelTitle(98+t, y+8, map-1); + + y += 72; + } + + V_DrawScaledPatch(0, 0, 0, W_CachePatchName("MENUHINT", PU_CACHE)); + V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, 6, 0, "SNEAKER CUP"); } // diff --git a/src/k_menufunc.c b/src/k_menufunc.c index e13d87b5c..74b563dc0 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -171,8 +171,8 @@ static consvar_t cv_dummystaff = {"dummystaff", "0", CV_HIDDEN|CV_CALL, dummysta // ========================================================================== // (there's only a couple anyway) -// Prototypes #if 0 +// Prototypes static INT32 M_FindFirstMap(INT32 gtype); static INT32 M_GetFirstLevelInList(void); #endif @@ -180,6 +180,7 @@ static INT32 M_GetFirstLevelInList(void); // Nextmap. Used for Time Attack. static void Nextmap_OnChange(void) { +#if 0 char *leveltitle; // Update the string in the consvar. @@ -187,7 +188,7 @@ static void Nextmap_OnChange(void) leveltitle = G_BuildMapTitle(cv_nextmap.value); cv_nextmap.string = cv_nextmap.zstring = leveltitle ? leveltitle : Z_StrDup(G_BuildMapName(cv_nextmap.value)); -#if 0 + if (currentMenu == &SP_TimeAttackDef) { // see also p_setup.c's P_LoadRecordGhosts @@ -272,6 +273,7 @@ static void Nextmap_OnChange(void) #endif } + static void Dummymenuplayer_OnChange(void) { if (cv_dummymenuplayer.value < 1) @@ -346,6 +348,7 @@ static void Newgametype_OnChange(void) #endif } + void Screenshot_option_Onchange(void) { #if 0 @@ -1174,10 +1177,11 @@ void M_SetupNextMenu(menu_t *menudef, boolean notransition) if (!notransition) { - if (currentMenu->transitionOutTics) + if (currentMenu->transitionID == menudef->transitionID + && currentMenu->transitionTics) { menutransition.tics = 0; - menutransition.dest = currentMenu->transitionOutTics; + menutransition.dest = currentMenu->transitionTics; menutransition.in = false; menutransition.newmenu = menudef; return; // Don't change menu yet, the transition will call this again @@ -1188,7 +1192,7 @@ void M_SetupNextMenu(menu_t *menudef, boolean notransition) F_WipeStartScreen(); V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); F_WipeEndScreen(); - F_RunWipe(wipedefs[wipe_menu_toblack], false, "FADEMAP0", false); + F_RunWipe(wipedefs[wipe_menu_toblack], false, "FADEMAP0", false, false); } } @@ -1243,6 +1247,8 @@ void M_GoBack(INT32 choice) } else M_ClearMenus(true); + + S_StartSound(NULL, sfx_s3k5b); } // @@ -1263,14 +1269,12 @@ void M_Ticker(void) // If dest is zero, we're mid-transition and want to end it if (menutransition.tics == menutransition.dest && menutransition.newmenu != NULL) { - M_SetupNextMenu(menutransition.newmenu, true); - - if (menutransition.newmenu->transitionInTics) + if (currentMenu->transitionID == menutransition.newmenu->transitionID + && menutransition.newmenu->transitionTics) { - menutransition.tics = currentMenu->transitionOutTics; + menutransition.tics = menutransition.newmenu->transitionTics; menutransition.dest = 0; menutransition.in = true; - menutransition.newmenu = NULL; } else if (gamestate == GS_MENU) { @@ -1280,8 +1284,11 @@ void M_Ticker(void) F_WipeStartScreen(); V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); F_WipeEndScreen(); - F_RunWipe(wipedefs[wipe_menu_toblack], false, "FADEMAP0", false); + F_RunWipe(wipedefs[wipe_menu_toblack], false, "FADEMAP0", false, false); } + + M_SetupNextMenu(menutransition.newmenu, true); + menutransition.newmenu = NULL; } } else @@ -1629,7 +1636,7 @@ setup_player_t setup_player[MAXSPLITSCREENPLAYERS]; struct setup_explosions_s setup_explosions[48]; UINT8 setup_numplayers = 0; -UINT16 setup_animcounter = 0; +tic_t setup_animcounter = 0; consvar_t *setup_playercvars[MAXSPLITSCREENPLAYERS][SPLITCV_MAX]; @@ -1781,7 +1788,7 @@ static void M_HandleCharacterGrid(INT32 choice, setup_player_t *p, UINT8 num) else p->mdepth = CSSTEP_ALTS; - S_StartSound(NULL, sfx_s3k5b); + S_StartSound(NULL, sfx_s3k63); } break; case KEY_ESCAPE: @@ -1824,7 +1831,7 @@ static void M_HandleCharRotate(INT32 choice, setup_player_t *p) break; case KEY_ENTER: p->mdepth = CSSTEP_COLORS; - S_StartSound(NULL, sfx_s3k5b); + S_StartSound(NULL, sfx_s3k63); break; case KEY_ESCAPE: p->mdepth = CSSTEP_CHARS; @@ -1996,6 +2003,241 @@ boolean M_CharacterSelectQuit(void) return true; } +// LEVEL SELECT + +#if 0 +// 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) +{ + // Does the map exist? + if (!mapheaderinfo[mapnum]) + return false; + + // Does the map have a name? + if (!mapheaderinfo[mapnum]->lvlttl[0]) + return false; + + if (M_MapLocked(mapnum+1)) + return false; // not unlocked + + // Should the map be hidden? + if (mapheaderinfo[mapnum]->menuflags & LF2_HIDEINMENU && mapnum+1 != gamemap) + return false; + + if (gt == GT_MATCH && (mapheaderinfo[mapnum]->typeoflevel & TOL_MATCH)) + return true; + + if (gt == GT_RACE && (mapheaderinfo[mapnum]->typeoflevel & TOL_RACE)) + { + if (levellist_cup != -1) + { + if (mapnum < (1 + ((levellist_cup-1) * 5)) + || mapnum > (6 + ((levellist_cup-1) * 5)) + return false; + } + + return true; + } + + // 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; +} +#endif + +struct levellist_cupgrid_s levellist_cupgrid; +struct levellist_scroll_s levellist_scroll; + +static void M_LevelSelectScrollDest(void) +{ + levellist_scroll.dest = (6*levellist_scroll.cursor); + + if (levellist_scroll.dest < 3) + levellist_scroll.dest = 3; + + if (levellist_scroll.dest > (6*4)-3) + levellist_scroll.dest = (6*4)-3; +} + +void M_LevelSelectInit(INT32 choice) +{ + UINT8 selecttype = currentMenu->menuitems[itemOn].mvar1; + + (void)choice; + + switch (selecttype) + { + case 2: + CV_StealthSetValue(&cv_newgametype, GT_RACE); + break; + default: + break; + } + + PLAY_CupSelectDef.prevMenu = currentMenu; + + if (cv_newgametype.value != GT_RACE) + { + PLAY_LevelSelectDef.prevMenu = currentMenu; + M_SetupNextMenu(&PLAY_LevelSelectDef, false); + } + else + { + PLAY_LevelSelectDef.prevMenu = &PLAY_CupSelectDef; + M_SetupNextMenu(&PLAY_CupSelectDef, false); + } +} + +void M_CupSelectHandler(INT32 choice) +{ + UINT8 selcup = levellist_cupgrid.x + (levellist_cupgrid.y * CUPS_COLUMNS); + + switch (choice) + { + case KEY_RIGHTARROW: + levellist_cupgrid.x++; + if (levellist_cupgrid.x >= CUPS_COLUMNS) + { + levellist_cupgrid.x = 0; + //levellist_cupgrid.pageno++; + } + S_StartSound(NULL, sfx_s3k5b); + break; + case KEY_LEFTARROW: + levellist_cupgrid.x--; + if (levellist_cupgrid.x < 0) + { + levellist_cupgrid.x = CUPS_COLUMNS-1; + //levellist_cupgrid.pageno--; + } + S_StartSound(NULL, sfx_s3k5b); + break; + case KEY_UPARROW: + levellist_cupgrid.y++; + if (levellist_cupgrid.y >= CUPS_ROWS) + { + levellist_cupgrid.y = 0; + //levellist_cupgrid.pageno++; + } + S_StartSound(NULL, sfx_s3k5b); + break; + case KEY_DOWNARROW: + levellist_cupgrid.y--; + if (levellist_cupgrid.y < 0) + { + levellist_cupgrid.y = CUPS_ROWS-1; + //levellist_cupgrid.pageno--; + } + S_StartSound(NULL, sfx_s3k5b); + break; + case KEY_ENTER: + if (levellist_scroll.cupid != selcup) // Keep cursor position if you select the same cup again + { + levellist_scroll.cursor = 0; + levellist_scroll.cupid = selcup; + } + + M_LevelSelectScrollDest(); + levellist_scroll.y = levellist_scroll.dest; + + M_SetupNextMenu(&PLAY_LevelSelectDef, false); + S_StartSound(NULL, sfx_s3k63); + break; + case KEY_ESCAPE: + if (currentMenu->prevMenu) + M_SetupNextMenu(currentMenu->prevMenu, false); + else + M_ClearMenus(true); + break; + default: + break; + } +} + +void M_CupSelectTick(void) +{ + levellist_cupgrid.previewanim++; +} + +void M_LevelSelectHandler(INT32 choice) +{ + if (levellist_scroll.y != levellist_scroll.dest) + return; + + switch (choice) + { + case KEY_UPARROW: + levellist_scroll.cursor--; + if (levellist_scroll.cursor < 0) + levellist_scroll.cursor = 4; + S_StartSound(NULL, sfx_s3k5b); + break; + case KEY_DOWNARROW: + levellist_scroll.cursor++; + if (levellist_scroll.cursor > 4) + levellist_scroll.cursor = 0; + S_StartSound(NULL, sfx_s3k5b); + break; + case KEY_ENTER: + //M_SetupNextMenu(&PLAY_TimeAttack, false); + S_StartSound(NULL, sfx_s3k63); + break; + case KEY_ESCAPE: + if (currentMenu->prevMenu) + M_SetupNextMenu(currentMenu->prevMenu, false); + else + M_ClearMenus(true); + break; + default: + break; + } + + M_LevelSelectScrollDest(); +} + +void M_LevelSelectTick(void) +{ + if (levellist_scroll.y > levellist_scroll.dest) + levellist_scroll.y--; + else if (levellist_scroll.y < levellist_scroll.dest) + levellist_scroll.y++; +} + // ===================== // PAUSE / IN-GAME MENUS // ===================== diff --git a/src/v_video.c b/src/v_video.c index f5c912296..42f503e32 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -1935,6 +1935,150 @@ void V_DrawRightAlignedFileString(INT32 x, INT32 y, INT32 option, const char *st x -= V_FileStringWidth(string, option); V_DrawFileString(x, y, option, string); } + +void V_DrawLSTitleHighString(INT32 x, INT32 y, INT32 option, const char *string) +{ + INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, left = 0; + INT32 spacewidth = 16; + const char *ch = string; + + option &= ~V_FLIP; + + if (option & V_NOSCALESTART) + { + dupx = vid.dupx; + dupy = vid.dupy; + scrwidth = vid.width; + } + else + { + dupx = dupy = 1; + scrwidth = vid.width/vid.dupx; + left = (scrwidth - BASEVIDWIDTH)/2; + } + + for (;;ch++) + { + if (!*ch) + break; + + if (*ch == '\n') + { + cx = x; + cy += 14*dupy; + + continue; + } + + c = toupper(*ch) - LT_FONTSTART; + + // character does not exist or is a space + if (c < 0 || c >= LT_FONTSIZE || !title_font_high[c]) + { + cx += spacewidth * dupx; + continue; + } + + w = (SHORT(title_font_high[c]->width) - 4) * dupx; + + if (cx > scrwidth) + break; + + if (cx+left + w < 0) //left boundary check + { + cx += w; + continue; + } + + V_DrawFixedPatch(cx<= LT_FONTSIZE || !title_font_low[c]) + { + cx += spacewidth * dupx; + continue; + } + + w = (SHORT(title_font_low[c]->width) - 4) * dupx; + + if (cx > scrwidth) + break; + + if (cx+left + w < 0) //left boundary check + { + cx += w; + continue; + } + + V_DrawFixedPatch(cx<= LT_FONTSIZE || !title_font_high[c]) + w += spacewidth; + else + w += SHORT(title_font_high[c]->width) - 4; + } + + return w; +} + +INT32 V_LSTitleLowStringWidth(const char *string, INT32 option) +{ + INT32 c, w = 0; + INT32 spacewidth = 16; + size_t i; + + (void)option; + + for (i = 0; i < strlen(string); i++) + { + c = string[i]; + c = toupper(c) - LT_FONTSTART; + + if (c < 0 || c >= LT_FONTSIZE || !title_font_low[c]) + w += spacewidth; + else + w += SHORT(title_font_low[c]->width) - 4; + } + + return w; +} + boolean *heatshifter = NULL; INT32 lastheight = 0; INT32 heatindex[MAXSPLITSCREENPLAYERS] = {0, 0, 0, 0}; diff --git a/src/v_video.h b/src/v_video.h index fe8e2b019..52dbc90b5 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -187,6 +187,12 @@ void V_DrawRightAlignedGamemodeString(INT32 x, INT32 y, INT32 option, const char void V_DrawFileString(INT32 x, INT32 y, INT32 option, const char *string); void V_DrawCenteredFileString(INT32 x, INT32 y, INT32 option, const char *string); void V_DrawRightAlignedFileString(INT32 x, INT32 y, INT32 option, const char *string); +void V_DrawLSTitleHighString(INT32 x, INT32 y, INT32 option, const char *string); +void V_DrawCenteredLSTitleHighString(INT32 x, INT32 y, INT32 option, const char *string); +void V_DrawRightAlignedLSTitleHighString(INT32 x, INT32 y, INT32 option, const char *string); +void V_DrawLSTitleLowString(INT32 x, INT32 y, INT32 option, const char *string); +void V_DrawCenteredLSTitleLowString(INT32 x, INT32 y, INT32 option, const char *string); +void V_DrawRightAlignedLSTitleLowString(INT32 x, INT32 y, INT32 option, const char *string); // draw a string using the hu_font, 0.5x scale void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string); @@ -224,6 +230,8 @@ INT32 V_ThinStringWidth(const char *string, INT32 option); // SRB2Kart INT32 V_GamemodeStringWidth(const char *string, INT32 option); INT32 V_FileStringWidth(const char *string, INT32 option); +INT32 V_LSTitleHighStringWidth(const char *string, INT32 option); +INT32 V_LSTitleLowStringWidth(const char *string, INT32 option); void V_DoPostProcessor(INT32 view, postimg_t type, INT32 param); From 94e8fddd46db138e5c94473fe0f251a2003cc48e Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Fri, 4 Oct 2019 13:27:09 -0400 Subject: [PATCH 007/379] Time Attack screen No functionality ported yet --- src/k_menu.h | 7 ++- src/k_menudef.c | 20 ++++++ src/k_menudraw.c | 154 ++++++++++++++++++++++++++++++++++++----------- src/k_menufunc.c | 33 +++++++--- 4 files changed, 169 insertions(+), 45 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 20b9e778d..8cb7001d1 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -157,6 +157,9 @@ extern menu_t PLAY_CupSelectDef; extern menuitem_t PLAY_LevelSelect[]; extern menu_t PLAY_LevelSelectDef; +extern menuitem_t PLAY_TimeAttack[]; +extern menu_t PLAY_TimeAttackDef; + extern menuitem_t PLAY_BattleGamemodesMenu[]; extern menu_t PLAY_BattleGamemodesDef; @@ -191,7 +194,8 @@ extern INT16 skullAnimCounter; // skull animation counter extern struct menutransition_s { INT16 tics; INT16 dest; - struct menu_s *newmenu; + struct menu_s *startmenu; + struct menu_s *endmenu; boolean in; } menutransition; @@ -336,6 +340,7 @@ void M_DrawCharacterSelect(void); void M_DrawCupSelect(void); void M_DrawLevelSelect(void); +void M_DrawTimeAttack(void); void M_DrawPlaybackMenu(void); diff --git a/src/k_menudef.c b/src/k_menudef.c index 3030340ba..2fd2b7264 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -132,6 +132,26 @@ menu_t PLAY_LevelSelectDef = { NULL }; +menuitem_t PLAY_TimeAttack[] = +{ + {IT_STRING, "Replay...", NULL, NULL, NULL, 0, 0}, + {IT_STRING, "Ghosts...", NULL, NULL, NULL, 0, 0}, + {IT_SPACE, NULL, NULL, NULL, NULL, 0, 0}, + {IT_STRING, "Start", NULL, NULL, NULL, 0, 0}, +}; + +menu_t PLAY_TimeAttackDef = { + sizeof(PLAY_TimeAttack) / sizeof(menuitem_t), + &PLAY_LevelSelectDef, + 0, + PLAY_TimeAttack, + 0, 0, + 2, 10, + M_DrawTimeAttack, + NULL, + NULL +}; + // BATTLE menuitem_t PLAY_BattleGamemodesMenu[] = diff --git a/src/k_menudraw.c b/src/k_menudraw.c index b9f271615..257ddac6a 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -923,8 +923,8 @@ void M_DrawCupSelect(void) static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map) { - static char word1[21]; - static char word2[21]; + char word1[22]; + char word2[22]; UINT8 word1len = 0; UINT8 word2len = 0; INT16 x2 = x; @@ -935,29 +935,44 @@ static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map) if (mapheaderinfo[map]->zonttl[0]) { - strcpy(word1, mapheaderinfo[map]->lvlttl); - strcpy(word2, mapheaderinfo[map]->zonttl); + boolean one = true; + boolean two = true; + + for (i = 0; i < 22; i++) + { + if (!one && !two) + break; + + if (mapheaderinfo[map]->lvlttl[i] && one) + { + word1[word1len] = mapheaderinfo[map]->lvlttl[i]; + word1len++; + } + else + one = false; + + if (mapheaderinfo[map]->zonttl[i] && two) + { + word2[word2len] = mapheaderinfo[map]->zonttl[i]; + word2len++; + } + else + two = false; + } } else { boolean donewithone = false; - for (i = 0; i < 21; i++) + for (i = 0; i < 22; i++) { if (!mapheaderinfo[map]->lvlttl[i]) - { - if (donewithone) - word2[word2len] = '\0'; - else - word1[word1len] = '\0'; break; - } if (mapheaderinfo[map]->lvlttl[i] == ' ') { if (!donewithone) { - word1[word1len] = '\0'; donewithone = true; continue; } @@ -976,6 +991,9 @@ static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map) } } + word1[word1len] = '\0'; + word2[word2len] = '\0'; + for (i = 0; i < 2; i++) { INT32 c; @@ -991,42 +1009,110 @@ static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map) V_DrawLSTitleLowString(x2, y+28, 0, word2); } +static void M_DrawLevelSelectBlock(INT16 x, INT16 y, INT16 map, boolean redblink, boolean greyscale) +{ + lumpnum_t lumpnum; + patch_t *PictureOfLevel; + UINT8 *colormap = NULL; + + if (greyscale) + colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_MENUCACHE); + + lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(map))); + if (lumpnum != LUMPERROR) + PictureOfLevel = W_CachePatchNum(lumpnum, PU_CACHE); + else + PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE); + + if (redblink) + V_DrawScaledPatch(3+x, y, 0, W_CachePatchName("LVLSEL2", PU_CACHE)); + else + V_DrawScaledPatch(3+x, y, 0, W_CachePatchName("LVLSEL", PU_CACHE)); + + V_DrawSmallMappedPatch(9+x, y+6, 0, PictureOfLevel, colormap); + M_DrawHighLowLevelTitle(98+x, y+8, map-1); +} + void M_DrawLevelSelect(void) { UINT8 i; - INT16 t = (32*menutransition.tics); + INT16 t = (32*menutransition.tics), tay = 0; INT16 y = 80 - (12 * levellist_scroll.y); + boolean tatransition = (menutransition.startmenu == &PLAY_TimeAttackDef || menutransition.endmenu == &PLAY_TimeAttackDef); + + if (tatransition) + { + t = -t; + tay = t/2; + } for (i = 0; i < 5; i++) { - lumpnum_t lumpnum; - patch_t *PictureOfLevel; - UINT8 *colormap = NULL; INT16 map = 1 + (levellist_scroll.cupid * 5) + i; + INT16 lvlx = t, lvly = y; - lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(map))); - if (lumpnum != LUMPERROR) - PictureOfLevel = W_CachePatchNum(lumpnum, PU_CACHE); - else - PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE); + if (i == levellist_scroll.cursor && tatransition) + { + lvlx = 0; + lvly = max(2, y+tay); + } - if (i == levellist_scroll.cursor && ((skullAnimCounter / 4) & 1)) - V_DrawScaledPatch(3+t, y, 0, W_CachePatchName("LVLSEL2", PU_CACHE)); - else - V_DrawScaledPatch(3+t, y, 0, W_CachePatchName("LVLSEL", PU_CACHE)); - - if (i != levellist_scroll.cursor) - colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_MENUCACHE); - - V_DrawSmallMappedPatch(9+t, y+6, 0, PictureOfLevel, colormap); - - M_DrawHighLowLevelTitle(98+t, y+8, map-1); + M_DrawLevelSelectBlock(lvlx, lvly, map, + (i == levellist_scroll.cursor && ((skullAnimCounter / 4) & 1)), + (i != levellist_scroll.cursor) + ); y += 72; } - V_DrawScaledPatch(0, 0, 0, W_CachePatchName("MENUHINT", PU_CACHE)); - V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, 6, 0, "SNEAKER CUP"); + V_DrawScaledPatch(0, tay, 0, W_CachePatchName("MENUHINT", PU_CACHE)); + V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, 6+tay, 0, "SNEAKER CUP"); +} + +void M_DrawTimeAttack(void) +{ + INT16 map = cv_nextmap.value; + INT16 t = (24*menutransition.tics); + INT16 leftedge = 149+t+16; + INT16 rightedge = 149+t+155; + INT16 opty = 152; + lumpnum_t lumpnum; + UINT8 i; + + M_DrawLevelSelectBlock(0, 2, map, false, false); + + //V_DrawFill(24-t, 82, 100, 100, 36); // size test + + lumpnum = W_CheckNumForName(va("%sR", G_BuildMapName(map))); + if (lumpnum != LUMPERROR) + V_DrawScaledPatch(24-t, 82, 0, W_CachePatchNum(lumpnum, PU_CACHE)); + + V_DrawScaledPatch(149+t, 70, 0, W_CachePatchName("BESTTIME", PU_CACHE)); + + V_DrawRightAlignedString(rightedge-12, 82, highlightflags, "BEST LAP:"); + K_drawKartTimestamp(0, 162+t, 88, 0, 2); + + V_DrawRightAlignedString(rightedge-12, 112, highlightflags, "BEST TIME:"); + K_drawKartTimestamp(0, 162+t, 118, map, 1); + + for (i = 0; i < currentMenu->numitems; i++) + { + UINT32 f = (i == itemOn) ? recommendedflags : highlightflags; + + switch (currentMenu->menuitems[i].status & IT_DISPLAY) + { + case IT_STRING: + if (i >= currentMenu->numitems-1) + V_DrawRightAlignedString(rightedge, opty, f, currentMenu->menuitems[i].text); + else + V_DrawString(leftedge, opty, f, currentMenu->menuitems[i].text); + opty += 10; + break; + case IT_SPACE: + opty += 4; + break; + } + } } // diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 74b563dc0..47d60e615 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1180,10 +1180,12 @@ void M_SetupNextMenu(menu_t *menudef, boolean notransition) if (currentMenu->transitionID == menudef->transitionID && currentMenu->transitionTics) { + menutransition.startmenu = currentMenu; + menutransition.endmenu = menudef; + menutransition.tics = 0; menutransition.dest = currentMenu->transitionTics; menutransition.in = false; - menutransition.newmenu = menudef; return; // Don't change menu yet, the transition will call this again } else if (gamestate == GS_MENU) @@ -1267,12 +1269,15 @@ void M_Ticker(void) // If dest is non-zero, we've started transition and want to switch menus // If dest is zero, we're mid-transition and want to end it - if (menutransition.tics == menutransition.dest && menutransition.newmenu != NULL) + if (menutransition.tics == menutransition.dest + && menutransition.endmenu != NULL + && currentMenu != menutransition.endmenu + ) { - if (currentMenu->transitionID == menutransition.newmenu->transitionID - && menutransition.newmenu->transitionTics) + if (menutransition.startmenu->transitionID == menutransition.endmenu->transitionID + && menutransition.endmenu->transitionTics) { - menutransition.tics = menutransition.newmenu->transitionTics; + menutransition.tics = menutransition.endmenu->transitionTics; menutransition.dest = 0; menutransition.in = true; } @@ -1287,14 +1292,21 @@ void M_Ticker(void) F_RunWipe(wipedefs[wipe_menu_toblack], false, "FADEMAP0", false, false); } - M_SetupNextMenu(menutransition.newmenu, true); - menutransition.newmenu = NULL; + M_SetupNextMenu(menutransition.endmenu, true); } } else { - // reset input trigger - noFurtherInput = false; + if (menuwipe) + { + // try not to let people input during the fadeout + noFurtherInput = true; + } + else + { + // reset input trigger + noFurtherInput = false; + } } if (currentMenu->tickroutine) @@ -2214,7 +2226,8 @@ void M_LevelSelectHandler(INT32 choice) S_StartSound(NULL, sfx_s3k5b); break; case KEY_ENTER: - //M_SetupNextMenu(&PLAY_TimeAttack, false); + CV_SetValue(&cv_nextmap, 1 + (levellist_scroll.cupid * 5) + levellist_scroll.cursor); + M_SetupNextMenu(&PLAY_TimeAttackDef, false); S_StartSound(NULL, sfx_s3k63); break; case KEY_ESCAPE: From 32bdd10dbf2da9ec3a1c7f83ee2fc89c9238e69b Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Fri, 4 Oct 2019 22:44:19 -0400 Subject: [PATCH 008/379] Cup definitions, updated menu to use them --- src/dehacked.c | 130 ++++++++++++++++++++++++++++++++++++++++ src/doomstat.h | 18 ++++++ src/g_game.c | 4 ++ src/k_menu.h | 13 +++- src/k_menudef.c | 10 ++-- src/k_menudraw.c | 46 +++++++++++---- src/k_menufunc.c | 151 +++++++++++++++++++++++++++++++++++------------ 7 files changed, 313 insertions(+), 59 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 724550443..1e7bf402d 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -1397,6 +1397,100 @@ static void readlevelheader(MYFILE *f, INT32 num) Z_Free(s); } +static void readcupheader(MYFILE *f, cupheader_t *cup) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word; + char *word2; + char *tmp; + INT32 i; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + // First remove trailing newline, if there is one + tmp = strchr(s, '\n'); + if (tmp) + *tmp = '\0'; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + // Set / reset word, because some things (Lua.) move it + word = s; + + // Get the part before the " = " + tmp = strchr(s, '='); + if (tmp) + *(tmp-1) = '\0'; + else + break; + strupr(word); + + // Now get the part after + word2 = tmp += 2; + i = atoi(word2); // used for numerical settings + strupr(word2); + + if (fastcmp(word, "ICON")) + { + deh_strlcpy(cup->icon, word2, + sizeof(cup->icon), va("%s Cup: icon", cup->name)); + } + else if (fastcmp(word, "LEVELLIST")) + { + cup->numlevels = 0; + + tmp = strtok(word2,","); + do { + INT32 map = atoi(tmp); + + if (tmp[0] >= 'A' && tmp[0] <= 'Z' && tmp[2] == '\0') + map = M_MapNumber(tmp[0], tmp[1]); + + if (!map) + break; + + if (cup->numlevels >= MAXLEVELLIST) + deh_warning("%s Cup: reached max levellist (%d)\n", cup->name, MAXLEVELLIST); + + cup->levellist[cup->numlevels] = map; + cup->numlevels++; + } while((tmp = strtok(NULL,",")) != NULL); + } + else if (fastcmp(word, "BONUSGAME")) + { + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') + i = M_MapNumber(word2[0], word2[1]); + cup->bonusgame = (INT16)i; + } + else if (fastcmp(word, "SPECIALSTAGE")) + { + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') + i = M_MapNumber(word2[0], word2[1]); + cup->specialstage = (INT16)i; + } + else if (fastcmp(word, "EMERALDNUM")) + { + cup->emeraldnum = (INT16)i; + } + else + deh_warning("%s Cup: unknown word '%s'", cup->name, word); + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} + static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum) { char *s = Z_Calloc(MAXLINELEN, PU_STATIC, NULL); @@ -3558,6 +3652,42 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad) } DEH_WriteUndoline(word, word2, UNDO_HEADER); } + else if (fastcmp(word, "CUP")) + { + cupheader_t *cup = kartcupheaders; + cupheader_t *prev = NULL; + + while (cup) + { + if (fastcmp(cup->name, word2)) + { + // mark as a major mod if it replaces an already-existing cup + G_SetGameModified(multiplayer, true); + break; + } + + prev = cup; + cup = cup->next; + } + + // Nothing found, add to the end. + if (!cup) + { + cup = Z_Calloc(sizeof (cupheader_t), PU_STATIC, NULL); + cup->id = numkartcupheaders; + deh_strlcpy(cup->name, word2, + sizeof(cup->name), va("Cup header %s: name", word2)); + if (prev != NULL) + prev->next = cup; + if (kartcupheaders == NULL) + kartcupheaders = cup; + numkartcupheaders++; + CONS_Printf("Added cup %d ('%s')\n", cup->id, cup->name); + } + + readcupheader(f, cup); + DEH_WriteUndoline(word, word2, UNDO_HEADER); + } else if (fastcmp(word, "CUTSCENE")) { if (i > 0 && i < 129) diff --git a/src/doomstat.h b/src/doomstat.h index 25899aff3..944bbfd5a 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -291,6 +291,24 @@ typedef struct extern mapheader_t* mapheaderinfo[NUMMAPS]; +#define MAXLEVELLIST 5 + +typedef struct cupheader_s +{ + UINT16 id; ///< Cup ID + char name[15]; ///< Cup title (14 chars) + char icon[9]; ///< Name of the icon patch + INT16 levellist[MAXLEVELLIST]; ///< List of levels that belong to this cup + UINT8 numlevels; ///< Number of levels defined in levellist + INT16 bonusgame; ///< Map number to use for bonus game + INT16 specialstage; ///< Map number to use for special stage + UINT8 emeraldnum; ///< ID of Emerald to use for special stage (1-7 for Chaos Emeralds, 8-14 for Super Emeralds, 0 for no emerald) + struct cupheader_s *next; ///< Next cup in linked list +} cupheader_t; + +extern cupheader_t *kartcupheaders; // Start of cup linked list +extern UINT16 numkartcupheaders; + enum TypeOfLevel { TOL_SP = 0x01, ///< Single Player diff --git a/src/g_game.c b/src/g_game.c index 783d2b072..e669ec3a3 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -153,6 +153,10 @@ struct quake quake; // Map Header Information mapheader_t* mapheaderinfo[NUMMAPS] = {NULL}; +// Kart cup definitions +cupheader_t *kartcupheaders = NULL; +UINT16 numkartcupheaders = 0; + static boolean exitgame = false; static boolean retrying = false; diff --git a/src/k_menu.h b/src/k_menu.h index 8cb7001d1..401620c1a 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -292,26 +292,33 @@ boolean M_CharacterSelectQuit(void); #define CUPS_MAX (NUMMAPS / CUPS_MAPSPERCUP) #define CUPS_PAGES (CUPS_MAX / (CUPS_COLUMNS * CUPS_ROWS)) +#define CUPID (levellist_cupgrid.x + (levellist_cupgrid.y * CUPS_COLUMNS)) + +extern cupheader_t *selectedcup; +extern INT16 selectedcupnum; + extern struct levellist_cupgrid_s { UINT8 numcups; SINT8 x, y; SINT8 pageno; tic_t previewanim; + boolean grandprix; // Setup grand prix server after picking } levellist_cupgrid; extern struct levellist_scroll_s { - SINT8 cupid; SINT8 cursor; UINT16 y; UINT16 dest; + boolean timeattack; // Setup time attack menu after picking } levellist_scroll; boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt); -void M_LevelSelectInit(INT32 choice); +INT32 M_CountLevelsToShowInList(INT32 gt); +INT32 M_GetFirstLevelInList(INT32 gt); +void M_LevelSelectInit(INT32 choice); void M_CupSelectHandler(INT32 choice); void M_CupSelectTick(void); - void M_LevelSelectHandler(INT32 choice); void M_LevelSelectTick(void); diff --git a/src/k_menudef.c b/src/k_menudef.c index 2fd2b7264..09cfa355b 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -85,13 +85,13 @@ menu_t PLAY_GamemodesDef = KARTGAMEMODEMENU(PLAY_GamemodesMenu, &PLAY_MainDef); menuitem_t PLAY_RaceGamemodesMenu[] = { {IT_STRING | IT_CALL, "Grand Prix", "Compete for the best rank over five races!", - NULL, M_LevelSelectInit, 0, 0}, + NULL, M_LevelSelectInit, 2, GT_RACE}, {IT_STRING | IT_CALL, "Match Race", "Play by your own rules in a specialized, single race!", - NULL, M_LevelSelectInit, 1, 0}, + NULL, M_LevelSelectInit, 0, GT_RACE}, {IT_STRING | IT_CALL, "Time Attack", "Record your best time on any track!", - NULL, M_LevelSelectInit, 2, 0}, + NULL, M_LevelSelectInit, 1, GT_RACE}, {IT_STRING | IT_CALL, "Back", NULL, NULL, M_GoBack, 0, 0}, }; @@ -157,10 +157,10 @@ menu_t PLAY_TimeAttackDef = { menuitem_t PLAY_BattleGamemodesMenu[] = { {IT_STRING | IT_CALL, "Survival", "It's last hedgehog standing in this free-for-all!", - NULL, M_LevelSelectInit, 3, 0}, + NULL, M_LevelSelectInit, 0, GT_MATCH}, {IT_STRING | IT_CALL, "Time Attack", "Bust up all of the capsules in record time!", - NULL, M_LevelSelectInit, 4, 0}, + NULL, M_LevelSelectInit, 1, GT_MATCH}, {IT_STRING | IT_CALL, "Back", NULL, NULL, M_GoBack, 0, 0}, }; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 257ddac6a..eed27ad28 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -875,20 +875,18 @@ void M_DrawCharacterSelect(void) // LEVEL SELECT -static void M_DrawCupPreview(INT16 y, UINT8 cupnum) +static void M_DrawCupPreview(INT16 y, cupheader_t *cup) { UINT8 i; INT16 x = -(levellist_cupgrid.previewanim % 82); - V_DrawFill(0, y, BASEVIDWIDTH, 54, 31); - - for (i = 0; i < 5; i++) + for (i = 0; i < cup->numlevels; i++) { lumpnum_t lumpnum; patch_t *PictureOfLevel; - UINT8 lvloff = (i + (levellist_cupgrid.previewanim / 82)) % 5; + UINT8 lvloff = (i + (levellist_cupgrid.previewanim / 82)) % cup->numlevels; - lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(1 + (cupnum * 5) + lvloff))); + lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cup->levellist[lvloff] ))); if (lumpnum != LUMPERROR) PictureOfLevel = W_CachePatchNum(lumpnum, PU_CACHE); else @@ -901,6 +899,14 @@ static void M_DrawCupPreview(INT16 y, UINT8 cupnum) void M_DrawCupSelect(void) { UINT8 i, j; + cupheader_t *cup = kartcupheaders; + + while (cup) + { + if (cup->id == CUPID) + break; + cup = cup->next; + } for (i = 0; i < CUPS_COLUMNS; i++) { @@ -915,10 +921,14 @@ void M_DrawCupSelect(void) 0, W_CachePatchName("CUPCURS", PU_CACHE) ); + V_DrawFill(0, 146 + (12*menutransition.tics), BASEVIDWIDTH, 54, 31); V_DrawScaledPatch(0, 120 - (12*menutransition.tics), 0, W_CachePatchName("MENUHINT", PU_CACHE)); - M_DrawCupPreview(146 + (12*menutransition.tics), levellist_cupgrid.x + (levellist_cupgrid.y * CUPS_COLUMNS)); - V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, 126 - (12*menutransition.tics), 0, "SNEAKER CUP"); + if (cup) + { + M_DrawCupPreview(146 + (12*menutransition.tics), cup); + V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, 126 - (12*menutransition.tics), 0, va("%s Cup", cup->name)); + } } static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map) @@ -930,7 +940,7 @@ static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map) INT16 x2 = x; UINT8 i; - if (!mapheaderinfo[map]->lvlttl[0]) + if (!mapheaderinfo[map] || !mapheaderinfo[map]->lvlttl[0]) return; if (mapheaderinfo[map]->zonttl[0]) @@ -1035,7 +1045,8 @@ static void M_DrawLevelSelectBlock(INT16 x, INT16 y, INT16 map, boolean redblink void M_DrawLevelSelect(void) { - UINT8 i; + INT16 i; + INT16 start = M_GetFirstLevelInList(cv_newgametype.value)-1; INT16 t = (32*menutransition.tics), tay = 0; INT16 y = 80 - (12 * levellist_scroll.y); boolean tatransition = (menutransition.startmenu == &PLAY_TimeAttackDef || menutransition.endmenu == &PLAY_TimeAttackDef); @@ -1046,10 +1057,16 @@ void M_DrawLevelSelect(void) tay = t/2; } - for (i = 0; i < 5; i++) + for (i = 0; i < M_CountLevelsToShowInList(cv_newgametype.value); i++) { - INT16 map = 1 + (levellist_scroll.cupid * 5) + i; INT16 lvlx = t, lvly = y; + INT16 map = start + i; + + while (!M_CanShowLevelInList(map, cv_newgametype.value) && map < NUMMAPS) + map++; + + if (map >= NUMMAPS) + break; if (i == levellist_scroll.cursor && tatransition) { @@ -1066,7 +1083,10 @@ void M_DrawLevelSelect(void) } V_DrawScaledPatch(0, tay, 0, W_CachePatchName("MENUHINT", PU_CACHE)); - V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, 6+tay, 0, "SNEAKER CUP"); + if (selectedcup) + V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, 6+tay, 0, va("%s Cup", selectedcup->name)); + else + V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, 6+tay, 0, va("%s Mode", Gametype_Names[cv_newgametype.value])); } void M_DrawTimeAttack(void) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 47d60e615..e3f1d0fa9 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2017,16 +2017,6 @@ boolean M_CharacterSelectQuit(void) // LEVEL SELECT -#if 0 -// 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 // @@ -2055,10 +2045,17 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) if (gt == GT_RACE && (mapheaderinfo[mapnum]->typeoflevel & TOL_RACE)) { - if (levellist_cup != -1) + if (selectedcup && selectedcup->numlevels) { - if (mapnum < (1 + ((levellist_cup-1) * 5)) - || mapnum > (6 + ((levellist_cup-1) * 5)) + UINT8 i; + + for (i = 0; i < selectedcup->numlevels; i++) + { + if (mapnum == selectedcup->levellist[i]) + break; + } + + if (i == selectedcup->numlevels) return false; } @@ -2069,75 +2066,95 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) return false; } -static INT32 M_CountLevelsToShowInList(void) +INT32 M_CountLevelsToShowInList(INT32 gt) { INT32 mapnum, count = 0; for (mapnum = 0; mapnum < NUMMAPS; mapnum++) - if (M_CanShowLevelInList(mapnum, -1)) + if (M_CanShowLevelInList(mapnum, gt)) count++; return count; } -static INT32 M_GetFirstLevelInList(void) +INT32 M_GetFirstLevelInList(INT32 gt) { INT32 mapnum; for (mapnum = 0; mapnum < NUMMAPS; mapnum++) - if (M_CanShowLevelInList(mapnum, -1)) + if (M_CanShowLevelInList(mapnum, gt)) return mapnum + 1; return 1; } -#endif +cupheader_t *selectedcup = NULL; struct levellist_cupgrid_s levellist_cupgrid; struct levellist_scroll_s levellist_scroll; static void M_LevelSelectScrollDest(void) { + UINT16 m = M_CountLevelsToShowInList(cv_newgametype.value)-1; + levellist_scroll.dest = (6*levellist_scroll.cursor); if (levellist_scroll.dest < 3) levellist_scroll.dest = 3; - if (levellist_scroll.dest > (6*4)-3) - levellist_scroll.dest = (6*4)-3; + if (levellist_scroll.dest > (6*m)-3) + levellist_scroll.dest = (6*m)-3; } void M_LevelSelectInit(INT32 choice) { - UINT8 selecttype = currentMenu->menuitems[itemOn].mvar1; - (void)choice; - switch (selecttype) + switch (currentMenu->menuitems[itemOn].mvar1) { + case 0: + levellist_cupgrid.grandprix = false; + levellist_scroll.timeattack = false; + break; + case 1: + levellist_cupgrid.grandprix = false; + levellist_scroll.timeattack = true; + break; case 2: - CV_StealthSetValue(&cv_newgametype, GT_RACE); + levellist_cupgrid.grandprix = true; + levellist_scroll.timeattack = false; break; default: - break; + CONS_Alert(CONS_WARNING, "Bad level select init\n"); + return; } + CV_StealthSetValue(&cv_newgametype, currentMenu->menuitems[itemOn].mvar2); PLAY_CupSelectDef.prevMenu = currentMenu; - if (cv_newgametype.value != GT_RACE) - { - PLAY_LevelSelectDef.prevMenu = currentMenu; - M_SetupNextMenu(&PLAY_LevelSelectDef, false); - } - else + if (cv_newgametype.value == GT_RACE) { PLAY_LevelSelectDef.prevMenu = &PLAY_CupSelectDef; M_SetupNextMenu(&PLAY_CupSelectDef, false); } + else + { + selectedcup = NULL; + PLAY_LevelSelectDef.prevMenu = currentMenu; + M_SetupNextMenu(&PLAY_LevelSelectDef, false); + } } void M_CupSelectHandler(INT32 choice) { - UINT8 selcup = levellist_cupgrid.x + (levellist_cupgrid.y * CUPS_COLUMNS); + cupheader_t *newcup = kartcupheaders; + + while (newcup) + { + CONS_Printf("%d == %d?\n", newcup->id, CUPID); + if (newcup->id == CUPID) + break; + newcup = newcup->next; + } switch (choice) { @@ -2178,10 +2195,13 @@ void M_CupSelectHandler(INT32 choice) S_StartSound(NULL, sfx_s3k5b); break; case KEY_ENTER: - if (levellist_scroll.cupid != selcup) // Keep cursor position if you select the same cup again + if (!newcup) + break; + + if (!selectedcup || newcup->id != selectedcup->id) // Keep cursor position if you select the same cup again { levellist_scroll.cursor = 0; - levellist_scroll.cupid = selcup; + selectedcup = newcup; } M_LevelSelectScrollDest(); @@ -2208,6 +2228,10 @@ void M_CupSelectTick(void) void M_LevelSelectHandler(INT32 choice) { + INT16 start = M_GetFirstLevelInList(cv_newgametype.value)-1; + INT16 maxlevels = M_CountLevelsToShowInList(cv_newgametype.value); + INT16 map = start; + if (levellist_scroll.y != levellist_scroll.dest) return; @@ -2216,18 +2240,69 @@ void M_LevelSelectHandler(INT32 choice) case KEY_UPARROW: levellist_scroll.cursor--; if (levellist_scroll.cursor < 0) - levellist_scroll.cursor = 4; + levellist_scroll.cursor = maxlevels-1; S_StartSound(NULL, sfx_s3k5b); break; case KEY_DOWNARROW: levellist_scroll.cursor++; - if (levellist_scroll.cursor > 4) + if (levellist_scroll.cursor >= maxlevels) levellist_scroll.cursor = 0; S_StartSound(NULL, sfx_s3k5b); break; case KEY_ENTER: - CV_SetValue(&cv_nextmap, 1 + (levellist_scroll.cupid * 5) + levellist_scroll.cursor); - M_SetupNextMenu(&PLAY_TimeAttackDef, false); + map = start + levellist_scroll.cursor; + + while (!M_CanShowLevelInList(map, cv_newgametype.value) && map < NUMMAPS) + map++; + + if (map >= NUMMAPS) + break; + + CV_SetValue(&cv_nextmap, map); + + if (levellist_scroll.timeattack) + M_SetupNextMenu(&PLAY_TimeAttackDef, false); + else + { + UINT8 ssplayers = cv_splitplayers.value-1; + + netgame = false; + multiplayer = true; + + strncpy(connectedservername, cv_servername.string, MAXSERVERNAME); + + // Still need to reset devmode + cv_debug = 0; + + if (strlen(cv_dummyjoinpassword.string) > 0) + D_SetJoinPassword(cv_dummyjoinpassword.string); + else + joinpasswordset = false; + + if (demo.playback) + G_StopDemo(); + if (metalrecording) + G_StopMetalDemo(); + + if (!cv_nextmap.value) + CV_SetValue(&cv_nextmap, G_RandMap(G_TOLFlag(cv_newgametype.value), -1, false, 0, false, NULL)+1); + + if (cv_maxplayers.value < ssplayers+1) + CV_SetValue(&cv_maxplayers, ssplayers+1); + + if (splitscreen != ssplayers) + { + splitscreen = ssplayers; + SplitScreen_OnChange(); + } + + paused = false; + SV_StartSinglePlayerServer(); + multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... + D_MapChange(cv_nextmap.value, cv_newgametype.value, (cv_kartencore.value == 1), 1, 1, false, false); + + M_ClearMenus(true); + } S_StartSound(NULL, sfx_s3k63); break; case KEY_ESCAPE: From cf8a77cfd6151420bad68346635620ecd4a6a3df Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Sat, 5 Oct 2019 23:11:31 -0400 Subject: [PATCH 009/379] Cup icon drawing --- src/dehacked.c | 5 ++++- src/k_menudraw.c | 36 ++++++++++++++++++++++++++++++++++-- src/k_menufunc.c | 1 - 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 1e7bf402d..2d88733df 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -1481,7 +1481,10 @@ static void readcupheader(MYFILE *f, cupheader_t *cup) } else if (fastcmp(word, "EMERALDNUM")) { - cup->emeraldnum = (INT16)i; + if (i >= 0 && i <= 14) + cup->emeraldnum = (UINT8)i; + else + deh_warning("%s Cup: invalid emerald number %d", cup->name, i); } else deh_warning("%s Cup: unknown word '%s'", cup->name, word); diff --git a/src/k_menudraw.c b/src/k_menudraw.c index eed27ad28..463c07c94 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -912,12 +912,44 @@ void M_DrawCupSelect(void) { for (j = 0; j < CUPS_ROWS; j++) { - V_DrawFill(14 + (i*42), 22 + (j*40) - (15*menutransition.tics), 39, 38, 31); + UINT8 id = (i + (j * CUPS_COLUMNS)); + cupheader_t *iconcup = kartcupheaders; + patch_t *patch = NULL; + INT16 x, y; + INT16 icony = 7; + + while (iconcup) + { + if (iconcup->id == id) + break; + iconcup = iconcup->next; + } + + if (!iconcup) + break; + + /*if (iconcup->emeraldnum == 0) + patch = W_CachePatchName("CUPMON3A", PU_CACHE); + else*/ if (iconcup->emeraldnum > 7) + { + patch = W_CachePatchName("CUPMON2A", PU_CACHE); + icony = 5; + } + else + patch = W_CachePatchName("CUPMON1A", PU_CACHE); + + x = 14 + (i*42); + y = 20 + (j*44) - (15*menutransition.tics); + + V_DrawScaledPatch(x, y, 0, patch); + + V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName(iconcup->icon, PU_CACHE)); + V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName("CUPBOX", PU_CACHE)); } } V_DrawScaledPatch(14 + (levellist_cupgrid.x*42) - 4, - 22 + (levellist_cupgrid.y*40) - 4 - (12*menutransition.tics), + 20 + (levellist_cupgrid.y*44) - 1 - (12*menutransition.tics), 0, W_CachePatchName("CUPCURS", PU_CACHE) ); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index e3f1d0fa9..f156d6ac7 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2150,7 +2150,6 @@ void M_CupSelectHandler(INT32 choice) while (newcup) { - CONS_Printf("%d == %d?\n", newcup->id, CUPID); if (newcup->id == CUPID) break; newcup = newcup->next; From 64a3d133b4d142d99bbd75e750f9376890ae2f19 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Tue, 15 Oct 2019 20:56:28 -0400 Subject: [PATCH 010/379] Cleanup & bug-fixin --- src/command.c | 41 +---- src/d_main.c | 5 +- src/d_netcmd.c | 17 +- src/dehacked.c | 16 +- src/doomstat.h | 3 + src/g_game.c | 4 +- src/hu_stuff.c | 2 +- src/k_menu.h | 35 ++-- src/k_menudraw.c | 159 ++++++++++++------ src/k_menufunc.c | 416 ++++++++++++++++++++++++----------------------- src/m_cheat.c | 21 +-- src/st_stuff.c | 2 +- src/y_inter.c | 26 +-- src/y_inter.h | 2 - 14 files changed, 383 insertions(+), 366 deletions(-) diff --git a/src/command.c b/src/command.c index e3fc0c292..c08933086 100644 --- a/src/command.c +++ b/src/command.c @@ -1352,8 +1352,7 @@ finish: // landing point for possiblevalue failures badinput: - if (var != &cv_nextmap) // Suppress errors for cv_nextmap - CONS_Printf(M_GetText("\"%s\" is not a possible value for \"%s\"\n"), valstr, var->name); + CONS_Printf(M_GetText("\"%s\" is not a possible value for \"%s\"\n"), valstr, var->name); // default value not valid... ?! if (var->defaultvalue == valstr) @@ -1628,44 +1627,6 @@ void CV_AddValue(consvar_t *var, INT32 increment) if (var->PossibleValue) { -#if 0 - if (var == &cv_nextmap) - { - // Special case for the nextmap variable, used only directly from the menu - INT32 oldvalue = var->value - 1, gt; - gt = cv_newgametype.value; - if (increment != 0) // Going up! - { - newvalue = var->value - 1; - do - { - if(increment > 0) // Going up! - { - if (++newvalue == NUMMAPS) - newvalue = -1; - } - else // Going down! - { - if (--newvalue == -2) - newvalue = NUMMAPS-1; - } - - if (newvalue == oldvalue) - break; // don't loop forever if there's none of a certain gametype - - if(!mapheaderinfo[newvalue]) - continue; // Don't allocate the header. That just makes memory usage skyrocket. - - } while (!M_CanShowLevelInList(newvalue, gt)); - - var->value = newvalue + 1; - var->func(); - return; - } - } - else -#endif - #define MINVAL 0 #define MAXVAL 1 if (var->PossibleValue[MINVAL].strvalue && !strcmp(var->PossibleValue[MINVAL].strvalue, "MIN")) diff --git a/src/d_main.c b/src/d_main.c index 1beff2292..27692bf4b 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -493,10 +493,7 @@ static void D_Display(void) if (lastdraw) { if (rendermode == render_soft) - { VID_BlitLinearScreen(screens[0], screens[1], vid.width*vid.bpp, vid.height, vid.width*vid.bpp, vid.rowbytes); - usebuffer = true; - } lastdraw = false; } @@ -1335,7 +1332,7 @@ void D_SRB2Main(void) if (M_CheckParm("-noupload")) COM_BufAddText("downloading 0\n"); - CONS_Printf("M_Init(): Init miscellaneous info.\n"); + CONS_Printf("M_Init(): Init menus.\n"); M_Init(); CONS_Printf("R_Init(): Init SRB2 refresh daemon.\n"); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index b49d1e539..81647f1c4 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -518,23 +518,17 @@ const char *netxcmdnames[MAXNETXCMD - 1] = void D_RegisterServerCommands(void) { INT32 i; + Forceskin_cons_t[0].value = -1; Forceskin_cons_t[0].strvalue = "Off"; - for (i = 0; i < NUMGAMETYPES; i++) - { - gametype_cons_t[i].value = i; - gametype_cons_t[i].strvalue = Gametype_Names[i]; - } - gametype_cons_t[NUMGAMETYPES].value = 0; - gametype_cons_t[NUMGAMETYPES].strvalue = NULL; - // Set the values to 0/NULL, it will be overwritten later when a skin is assigned to the slot. for (i = 1; i < MAXSKINS; i++) { Forceskin_cons_t[i].value = 0; Forceskin_cons_t[i].strvalue = NULL; } + RegisterNetXCmd(XD_NAMEANDCOLOR, Got_NameAndColor); RegisterNetXCmd(XD_WEAPONPREF, Got_WeaponPref); RegisterNetXCmd(XD_MAP, Got_Mapcmd); @@ -4786,6 +4780,7 @@ void D_GameTypeChanged(INT32 lastgametype) if (oldgt && newgt) CONS_Printf(M_GetText("Gametype was changed from %s to %s\n"), oldgt, newgt); } + // Only do the following as the server, not as remote admin. // There will always be a server, and this only needs to be done once. if (server && (multiplayer || netgame)) @@ -4859,9 +4854,10 @@ void D_GameTypeChanged(INT32 lastgametype) } } +#if 0 // When swapping to a gametype that supports spectators, // make everyone a spectator initially. - /*if (G_GametypeHasSpectators()) + if (G_GametypeHasSpectators()) { INT32 i; for (i = 0; i < MAXPLAYERS; i++) @@ -4870,7 +4866,8 @@ void D_GameTypeChanged(INT32 lastgametype) players[i].ctfteam = 0; players[i].spectator = true; } - }*/ + } +#endif // don't retain teams in other modes or between changes from ctf to team match. // also, stop any and all forms of team scrambling that might otherwise take place. diff --git a/src/dehacked.c b/src/dehacked.c index 2d88733df..7ba45f44b 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -1459,9 +1459,12 @@ static void readcupheader(MYFILE *f, cupheader_t *cup) break; if (cup->numlevels >= MAXLEVELLIST) + { deh_warning("%s Cup: reached max levellist (%d)\n", cup->name, MAXLEVELLIST); + break; + } - cup->levellist[cup->numlevels] = map; + cup->levellist[cup->numlevels] = map - 1; cup->numlevels++; } while((tmp = strtok(NULL,",")) != NULL); } @@ -1470,14 +1473,14 @@ static void readcupheader(MYFILE *f, cupheader_t *cup) // Convert to map number if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') i = M_MapNumber(word2[0], word2[1]); - cup->bonusgame = (INT16)i; + cup->bonusgame = (INT16)i - 1; } else if (fastcmp(word, "SPECIALSTAGE")) { // Convert to map number if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') i = M_MapNumber(word2[0], word2[1]); - cup->specialstage = (INT16)i; + cup->specialstage = (INT16)i - 1; } else if (fastcmp(word, "EMERALDNUM")) { @@ -1486,6 +1489,13 @@ static void readcupheader(MYFILE *f, cupheader_t *cup) else deh_warning("%s Cup: invalid emerald number %d", cup->name, i); } + else if (fastcmp(word, "UNLOCKABLE")) + { + if (i >= 0 && i <= MAXUNLOCKABLES) // 0 for no unlock required, anything else requires something + cup->unlockrequired = (SINT8)i - 1; + else + deh_warning("%s Cup: invalid unlockable number %d", cup->name, i); + } else deh_warning("%s Cup: unknown word '%s'", cup->name, word); } diff --git a/src/doomstat.h b/src/doomstat.h index 944bbfd5a..87b9ebc84 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -291,6 +291,8 @@ typedef struct extern mapheader_t* mapheaderinfo[NUMMAPS]; +// This could support more, but is that a good idea? +// Keep in mind that it may encourage people making overly long cups just because they "can", and would be a waste of memory. #define MAXLEVELLIST 5 typedef struct cupheader_s @@ -303,6 +305,7 @@ typedef struct cupheader_s INT16 bonusgame; ///< Map number to use for bonus game INT16 specialstage; ///< Map number to use for special stage UINT8 emeraldnum; ///< ID of Emerald to use for special stage (1-7 for Chaos Emeralds, 8-14 for Super Emeralds, 0 for no emerald) + SINT8 unlockrequired; ///< An unlockable is required to select this cup. -1 for no unlocking required. struct cupheader_s *next; ///< Next cup in linked list } cupheader_t; diff --git a/src/g_game.c b/src/g_game.c index e669ec3a3..a68290d76 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -808,9 +808,10 @@ const char *G_BuildMapName(INT32 map) { static char mapname[10] = "MAPXX"; // internal map name (wad resource name) - I_Assert(map >= 0); + I_Assert(map > 0); I_Assert(map <= NUMMAPS); +#if 0 if (map == 0) // hack??? { if (gamestate == GS_TITLESCREEN) @@ -821,6 +822,7 @@ const char *G_BuildMapName(INT32 map) map = prevmap; map = G_RandMap(G_TOLFlag(cv_newgametype.value), map, false, 0, false, NULL)+1; } +#endif if (map < 100) sprintf(&mapname[3], "%.2d", map); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 455d90394..5b5648c60 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -2978,7 +2978,7 @@ static void HU_DrawRankings(void) if (modeattacking) V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, "Record Attack"); else - V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, gametype_cons_t[gametype].strvalue); + V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, Gametype_Names[gametype]); if (G_GametypeHasTeams()) { diff --git a/src/k_menu.h b/src/k_menu.h index 401620c1a..7129719d6 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -202,8 +202,7 @@ extern struct menutransition_s { extern boolean menuwipe; extern consvar_t cv_showfocuslost; -extern consvar_t cv_newgametype, cv_nextmap, cv_chooseskin, cv_serversort; -extern CV_PossibleValue_t gametype_cons_t[]; +extern consvar_t cv_chooseskin, cv_serversort; void Moviemode_mode_Onchange(void); void Screenshot_option_Onchange(void); @@ -286,35 +285,31 @@ void M_CharacterSelectHandler(INT32 choice); void M_CharacterSelectTick(void); boolean M_CharacterSelectQuit(void); -#define CUPS_COLUMNS 7 -#define CUPS_ROWS 2 -#define CUPS_MAPSPERCUP 5 -#define CUPS_MAX (NUMMAPS / CUPS_MAPSPERCUP) -#define CUPS_PAGES (CUPS_MAX / (CUPS_COLUMNS * CUPS_ROWS)) +#define CUPMENU_COLUMNS 7 +#define CUPMENU_ROWS 2 +#define CUPMENU_CURSORID (cupgrid.x + (cupgrid.y * CUPMENU_COLUMNS) + (cupgrid.pageno * (CUPMENU_COLUMNS * CUPMENU_ROWS))) -#define CUPID (levellist_cupgrid.x + (levellist_cupgrid.y * CUPS_COLUMNS)) - -extern cupheader_t *selectedcup; -extern INT16 selectedcupnum; - -extern struct levellist_cupgrid_s { - UINT8 numcups; +extern struct cupgrid_s { SINT8 x, y; SINT8 pageno; + UINT8 numpages; tic_t previewanim; boolean grandprix; // Setup grand prix server after picking -} levellist_cupgrid; +} cupgrid; -extern struct levellist_scroll_s { +extern struct levellist_s { SINT8 cursor; UINT16 y; UINT16 dest; + cupheader_t *selectedcup; + INT16 choosemap; + UINT8 newgametype; boolean timeattack; // Setup time attack menu after picking -} levellist_scroll; +} levellist; -boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt); -INT32 M_CountLevelsToShowInList(INT32 gt); -INT32 M_GetFirstLevelInList(INT32 gt); +boolean M_CanShowLevelInList(INT16 mapnum, UINT8 gt); +INT16 M_CountLevelsToShowInList(UINT8 gt); +INT16 M_GetFirstLevelInList(UINT8 gt); void M_LevelSelectInit(INT32 choice); void M_CupSelectHandler(INT32 choice); diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 463c07c94..b07775695 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -102,13 +102,13 @@ void M_Drawer(void) if (gamestate == GS_MENU) // draw BG V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUBG", PU_CACHE), NULL); else if (!WipeInAction && currentMenu != &PAUSE_PlaybackMenuDef) - V_DrawFadeScreen(0xFF00, 16); // now that's more readable with a faded background (yeah like Quake...) + V_DrawCustomFadeScreen("FADEMAP0", 4); // now that's more readable with a faded background (yeah like Quake...) if (currentMenu->drawroutine) currentMenu->drawroutine(); // call current menu Draw routine // draw non-green resolution border - if ((gamestate == GS_MENU) && ((vid.width % BASEVIDWIDTH != 0) || (vid.height % BASEVIDHEIGHT != 0))) + if ((vid.width % BASEVIDWIDTH != 0) || (vid.height % BASEVIDHEIGHT != 0)) V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("WEIRDRES", PU_CACHE), NULL); // Draw version down in corner @@ -878,21 +878,62 @@ void M_DrawCharacterSelect(void) static void M_DrawCupPreview(INT16 y, cupheader_t *cup) { UINT8 i; - INT16 x = -(levellist_cupgrid.previewanim % 82); + INT16 x = -(cupgrid.previewanim % 82); - for (i = 0; i < cup->numlevels; i++) + V_DrawFill(0, y, BASEVIDWIDTH, 54, 31); + + if (cup && (cup->unlockrequired == -1 || unlockables[cup->unlockrequired].unlocked)) { - lumpnum_t lumpnum; - patch_t *PictureOfLevel; - UINT8 lvloff = (i + (levellist_cupgrid.previewanim / 82)) % cup->numlevels; + for (i = 0; i < cup->numlevels; i++) + { + lumpnum_t lumpnum; + patch_t *PictureOfLevel; + UINT8 lvloff = (i + (cupgrid.previewanim / 82)) % cup->numlevels; - lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cup->levellist[lvloff] ))); - if (lumpnum != LUMPERROR) - PictureOfLevel = W_CachePatchNum(lumpnum, PU_CACHE); - else - PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE); + lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cup->levellist[lvloff]+1))); + if (lumpnum != LUMPERROR) + PictureOfLevel = W_CachePatchNum(lumpnum, PU_CACHE); + else + PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE); - V_DrawSmallScaledPatch(x + 1 + (i*82), y+2, 0, PictureOfLevel); + V_DrawSmallScaledPatch(x + 1 + (i*82), y+2, 0, PictureOfLevel); + } + } + else + { + patch_t *st = W_CachePatchName(va("PREVST0%d", (cupgrid.previewanim % 4) + 1), PU_CACHE); + while (x < BASEVIDWIDTH) + { + V_DrawScaledPatch(x+1, y+2, 0, st); + x += 82; + } + } +} + +static void M_DrawCupTitle(INT16 y, cupheader_t *cup) +{ + V_DrawScaledPatch(0, y, 0, W_CachePatchName("MENUHINT", PU_CACHE)); + + if (cup) + { + boolean unlocked = (cup->unlockrequired == -1 || unlockables[cup->unlockrequired].unlocked); + UINT8 *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_MENUCACHE); + patch_t *icon = W_CachePatchName(cup->icon, PU_CACHE); + const char *str = (unlocked ? va("%s Cup", cup->name) : "???"); + INT16 offset = V_LSTitleLowStringWidth(str, 0) / 2; + + V_DrawLSTitleLowString(BASEVIDWIDTH/2 - offset, y+6, 0, str); + + if (unlocked) + { + V_DrawMappedPatch(BASEVIDWIDTH/2 - offset - 24, y+5, 0, icon, colormap); + V_DrawMappedPatch(BASEVIDWIDTH/2 + offset + 3, y+5, 0, icon, colormap); + } + } + else + { + if (currentMenu == &PLAY_LevelSelectDef) + V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, y+6, 0, va("%s Mode", Gametype_Names[levellist.newgametype])); } } @@ -903,16 +944,16 @@ void M_DrawCupSelect(void) while (cup) { - if (cup->id == CUPID) + if (cup->id == CUPMENU_CURSORID) break; cup = cup->next; } - for (i = 0; i < CUPS_COLUMNS; i++) + for (i = 0; i < CUPMENU_COLUMNS; i++) { - for (j = 0; j < CUPS_ROWS; j++) + for (j = 0; j < CUPMENU_ROWS; j++) { - UINT8 id = (i + (j * CUPS_COLUMNS)); + UINT8 id = (i + (j * CUPMENU_COLUMNS)) + (cupgrid.pageno * (CUPMENU_COLUMNS * CUPMENU_ROWS)); cupheader_t *iconcup = kartcupheaders; patch_t *patch = NULL; INT16 x, y; @@ -943,24 +984,26 @@ void M_DrawCupSelect(void) V_DrawScaledPatch(x, y, 0, patch); - V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName(iconcup->icon, PU_CACHE)); - V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName("CUPBOX", PU_CACHE)); + if (iconcup->unlockrequired != -1 && !unlockables[iconcup->unlockrequired].unlocked) + { + patch_t *st = W_CachePatchName(va("ICONST0%d", (cupgrid.previewanim % 4) + 1), PU_CACHE); + V_DrawScaledPatch(x + 8, y + icony, 0, st); + } + else + { + V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName(iconcup->icon, PU_CACHE)); + V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName("CUPBOX", PU_CACHE)); + } } } - V_DrawScaledPatch(14 + (levellist_cupgrid.x*42) - 4, - 20 + (levellist_cupgrid.y*44) - 1 - (12*menutransition.tics), + V_DrawScaledPatch(14 + (cupgrid.x*42) - 4, + 20 + (cupgrid.y*44) - 1 - (12*menutransition.tics), 0, W_CachePatchName("CUPCURS", PU_CACHE) ); - V_DrawFill(0, 146 + (12*menutransition.tics), BASEVIDWIDTH, 54, 31); - V_DrawScaledPatch(0, 120 - (12*menutransition.tics), 0, W_CachePatchName("MENUHINT", PU_CACHE)); - - if (cup) - { - M_DrawCupPreview(146 + (12*menutransition.tics), cup); - V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, 126 - (12*menutransition.tics), 0, va("%s Cup", cup->name)); - } + M_DrawCupPreview(146 + (12*menutransition.tics), cup); + M_DrawCupTitle(120 - (12*menutransition.tics), cup); } static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map) @@ -1033,16 +1076,37 @@ static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map) } } + if (mapheaderinfo[map]->actnum[0]) + { + word2[word2len] = ' '; + word2len++; + + for (i = 0; i < 3; i++) + { + if (!mapheaderinfo[map]->actnum[i]) + break; + + word2[word2len] = mapheaderinfo[map]->actnum[i]; + word2len++; + } + } + word1[word1len] = '\0'; word2[word2len] = '\0'; for (i = 0; i < 2; i++) { INT32 c; + if (i >= word1len) break; + c = toupper(word1[i]) - LT_FONTSTART; - x2 += SHORT(title_font_high[c]->width) - 4; + + if (!title_font_high[c]) + x2 += 16; + else + x2 += SHORT(title_font_high[c]->width) - 4; } if (word1len) @@ -1060,7 +1124,7 @@ static void M_DrawLevelSelectBlock(INT16 x, INT16 y, INT16 map, boolean redblink if (greyscale) colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_MENUCACHE); - lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(map))); + lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(map+1))); if (lumpnum != LUMPERROR) PictureOfLevel = W_CachePatchNum(lumpnum, PU_CACHE); else @@ -1072,16 +1136,17 @@ static void M_DrawLevelSelectBlock(INT16 x, INT16 y, INT16 map, boolean redblink V_DrawScaledPatch(3+x, y, 0, W_CachePatchName("LVLSEL", PU_CACHE)); V_DrawSmallMappedPatch(9+x, y+6, 0, PictureOfLevel, colormap); - M_DrawHighLowLevelTitle(98+x, y+8, map-1); + M_DrawHighLowLevelTitle(98+x, y+8, map); } void M_DrawLevelSelect(void) { INT16 i; - INT16 start = M_GetFirstLevelInList(cv_newgametype.value)-1; + INT16 start = M_GetFirstLevelInList(levellist.newgametype); + INT16 map = start; INT16 t = (32*menutransition.tics), tay = 0; - INT16 y = 80 - (12 * levellist_scroll.y); - boolean tatransition = (menutransition.startmenu == &PLAY_TimeAttackDef || menutransition.endmenu == &PLAY_TimeAttackDef); + INT16 y = 80 - (12 * levellist.y); + boolean tatransition = ((menutransition.startmenu == &PLAY_TimeAttackDef || menutransition.endmenu == &PLAY_TimeAttackDef) && menutransition.tics); if (tatransition) { @@ -1089,41 +1154,37 @@ void M_DrawLevelSelect(void) tay = t/2; } - for (i = 0; i < M_CountLevelsToShowInList(cv_newgametype.value); i++) + for (i = 0; i < M_CountLevelsToShowInList(levellist.newgametype); i++) { INT16 lvlx = t, lvly = y; - INT16 map = start + i; - while (!M_CanShowLevelInList(map, cv_newgametype.value) && map < NUMMAPS) + while (!M_CanShowLevelInList(map, levellist.newgametype) && map < NUMMAPS) map++; if (map >= NUMMAPS) break; - if (i == levellist_scroll.cursor && tatransition) + if (i == levellist.cursor && tatransition) { lvlx = 0; lvly = max(2, y+tay); } M_DrawLevelSelectBlock(lvlx, lvly, map, - (i == levellist_scroll.cursor && ((skullAnimCounter / 4) & 1)), - (i != levellist_scroll.cursor) + (i == levellist.cursor && (((skullAnimCounter / 4) & 1) || tatransition)), + (i != levellist.cursor) ); y += 72; + map++; } - V_DrawScaledPatch(0, tay, 0, W_CachePatchName("MENUHINT", PU_CACHE)); - if (selectedcup) - V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, 6+tay, 0, va("%s Cup", selectedcup->name)); - else - V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, 6+tay, 0, va("%s Mode", Gametype_Names[cv_newgametype.value])); + M_DrawCupTitle(tay, levellist.selectedcup); } void M_DrawTimeAttack(void) { - INT16 map = cv_nextmap.value; + INT16 map = levellist.choosemap; INT16 t = (24*menutransition.tics); INT16 leftedge = 149+t+16; INT16 rightedge = 149+t+155; @@ -1131,11 +1192,11 @@ void M_DrawTimeAttack(void) lumpnum_t lumpnum; UINT8 i; - M_DrawLevelSelectBlock(0, 2, map, false, false); + M_DrawLevelSelectBlock(0, 2, map, true, false); //V_DrawFill(24-t, 82, 100, 100, 36); // size test - lumpnum = W_CheckNumForName(va("%sR", G_BuildMapName(map))); + lumpnum = W_CheckNumForName(va("%sR", G_BuildMapName(map+1))); if (lumpnum != LUMPERROR) V_DrawScaledPatch(24-t, 82, 0, W_CachePatchNum(lumpnum, PU_CACHE)); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index f156d6ac7..4b7537cc0 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -83,7 +83,7 @@ menu_t *currentMenu = &MainDef; char dummystaffname[22]; INT16 itemOn = 0; // menu item skull is on, Hack by Tails 09-18-2002 -INT16 skullAnimCounter = 10; // skull animation counter +INT16 skullAnimCounter = 8; // skull animation counter struct menutransition_s menutransition; // Menu transition properties // finish wipes between screens @@ -98,10 +98,7 @@ static boolean noFurtherInput = false; // ========================================================================== // 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); consvar_t cv_showfocuslost = {"showfocuslost", "Yes", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL }; @@ -136,20 +133,8 @@ consvar_t cv_splitplayers = {"splitplayers", "One", CV_CALL, splitplayers_cons_t //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 CV_PossibleValue_t map_cons_t[] = { - {0,"MIN"}, - {NUMMAPS, "MAX"}, - {0, NULL} -}; -consvar_t cv_nextmap = {"nextmap", "1", CV_HIDDEN|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_HIDDEN|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 dehacked.c and doomstat.h! -CV_PossibleValue_t gametype_cons_t[NUMGAMETYPES+1]; -consvar_t cv_newgametype = {"newgametype", "Race", CV_HIDDEN|CV_CALL, gametype_cons_t, Newgametype_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_chooseskin = {"chooseskin", DEFAULTSKIN, CV_HIDDEN, skins_cons_t, NULL, 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 consvar_t cv_dummymenuplayer = {"dummymenuplayer", "P1", CV_HIDDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange, 0, NULL, NULL, 0, 0, NULL}; @@ -172,15 +157,9 @@ static consvar_t cv_dummystaff = {"dummystaff", "0", CV_HIDDEN|CV_CALL, dummysta // (there's only a couple anyway) #if 0 -// Prototypes -static INT32 M_FindFirstMap(INT32 gtype); -static INT32 M_GetFirstLevelInList(void); -#endif - // Nextmap. Used for Time Attack. static void Nextmap_OnChange(void) { -#if 0 char *leveltitle; // Update the string in the consvar. @@ -188,7 +167,6 @@ static void Nextmap_OnChange(void) 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 @@ -270,9 +248,8 @@ static void Nextmap_OnChange(void) free(gpath); } -#endif } - +#endif static void Dummymenuplayer_OnChange(void) { @@ -284,6 +261,7 @@ static void Dummymenuplayer_OnChange(void) static void Dummystaff_OnChange(void) { +#if 0 lumpnum_t l; dummystaffname[0] = '\0'; @@ -315,40 +293,9 @@ static void Dummystaff_OnChange(void) sprintf(temp, " - %d", cv_dummystaff.value); } -} - -// Newgametype. Used for gametype changes. -static void Newgametype_OnChange(void) -{ -#if 0 - 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)) - || (cv_newgametype.value == GT_MATCH && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_MATCH))) - { - INT32 value = 0; - - switch (cv_newgametype.value) - { - default: - case GT_RACE: - value = TOL_RACE; - break; - case GT_MATCH: - value = TOL_MATCH; - break; - } - - CV_SetValue(&cv_nextmap, M_FindFirstMap(value)); - } - } #endif } - void Screenshot_option_Onchange(void) { #if 0 @@ -427,32 +374,37 @@ static void M_ChangeCvar(INT32 choice) { consvar_t *cv = (consvar_t *)currentMenu->menuitems[itemOn].itemaction; + // Backspace sets values to default value if (choice == -1) { + // There's a default color technically, but it's not ideal. Use your skin's prefcolor instead! if (cv == &cv_playercolor) { SINT8 skinno = R_SkinAvailable(cv_chooseskin.string); + if (skinno != -1) - CV_SetValue(cv,skins[skinno].prefcolor); + CV_SetValue(cv, skins[skinno].prefcolor); + return; } - CV_Set(cv,cv->defaultvalue); + + 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)) + || ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_INVISSLIDER) + || ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_NOMOD)) { - CV_SetValue(cv,cv->value+choice); + 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); + sprintf(s, "%f", FIXED_TO_FLOAT(cv->value) + (choice) * (1.0f / 16.0f)); + CV_Set(cv, s); } else { @@ -464,7 +416,8 @@ static void M_ChangeCvar(INT32 choice) else if (cv == &cv_maxping) choice *= 50; #endif - CV_AddValue(cv,choice); + + CV_AddValue(cv, choice); } } @@ -579,15 +532,15 @@ boolean M_Responder(event_t *ev) switch (ch) { case KEY_MOUSE1: - //case KEY_JOY1: - //case KEY_JOY1 + 2: + //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_JOY1 + 3: // Brake can function as 'n' for message boxes now. + ch = 'n'; + break;*/ case KEY_MOUSE1 + 1: - //case KEY_JOY1 + 1: + //case KEY_JOY1 + 1: ch = KEY_BACKSPACE; break; case KEY_HAT1: @@ -744,8 +697,8 @@ boolean M_Responder(event_t *ev) M_QuitSRB2(0); return true; - case KEY_F11: // Gamma Level - CV_AddValue(&cv_usegamma, 1); + case KEY_F11: // Empty (used to be Gamma) + //CV_AddValue(&cv_usegamma, 1); return true; // Spymode on F12 handled in game logic @@ -948,8 +901,8 @@ boolean M_Responder(event_t *ev) if (cv == &cv_chooseskin || cv == &cv_dummystaff - || cv == &cv_nextmap - || cv == &cv_newgametype) + /*|| cv == &cv_nextmap + || cv == &cv_newgametype*/) return true; #if 0 @@ -1344,8 +1297,6 @@ void M_Init(void) { //COM_AddCommand("manual", Command_Manual_f); - CV_RegisterVar(&cv_nextmap); - CV_RegisterVar(&cv_newgametype); CV_RegisterVar(&cv_chooseskin); CV_RegisterVar(&cv_autorecord); @@ -2023,7 +1974,7 @@ boolean M_CharacterSelectQuit(void) // 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) +boolean M_CanShowLevelInList(INT16 mapnum, UINT8 gt) { // Does the map exist? if (!mapheaderinfo[mapnum]) @@ -2037,7 +1988,7 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) return false; // not unlocked // Should the map be hidden? - if (mapheaderinfo[mapnum]->menuflags & LF2_HIDEINMENU && mapnum+1 != gamemap) + if (mapheaderinfo[mapnum]->menuflags & LF2_HIDEINMENU /*&& mapnum+1 != gamemap*/) return false; if (gt == GT_MATCH && (mapheaderinfo[mapnum]->typeoflevel & TOL_MATCH)) @@ -2045,17 +1996,17 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) if (gt == GT_RACE && (mapheaderinfo[mapnum]->typeoflevel & TOL_RACE)) { - if (selectedcup && selectedcup->numlevels) + if (levellist.selectedcup && levellist.selectedcup->numlevels) { UINT8 i; - for (i = 0; i < selectedcup->numlevels; i++) + for (i = 0; i < levellist.selectedcup->numlevels; i++) { - if (mapnum == selectedcup->levellist[i]) + if (mapnum == levellist.selectedcup->levellist[i]) break; } - if (i == selectedcup->numlevels) + if (i == levellist.selectedcup->numlevels) return false; } @@ -2066,9 +2017,9 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) return false; } -INT32 M_CountLevelsToShowInList(INT32 gt) +INT16 M_CountLevelsToShowInList(UINT8 gt) { - INT32 mapnum, count = 0; + INT16 mapnum, count = 0; for (mapnum = 0; mapnum < NUMMAPS; mapnum++) if (M_CanShowLevelInList(mapnum, gt)) @@ -2077,32 +2028,31 @@ INT32 M_CountLevelsToShowInList(INT32 gt) return count; } -INT32 M_GetFirstLevelInList(INT32 gt) +INT16 M_GetFirstLevelInList(UINT8 gt) { - INT32 mapnum; + INT16 mapnum; for (mapnum = 0; mapnum < NUMMAPS; mapnum++) if (M_CanShowLevelInList(mapnum, gt)) - return mapnum + 1; + return mapnum; - return 1; + return 0; } -cupheader_t *selectedcup = NULL; -struct levellist_cupgrid_s levellist_cupgrid; -struct levellist_scroll_s levellist_scroll; +struct cupgrid_s cupgrid; +struct levellist_s levellist; static void M_LevelSelectScrollDest(void) { - UINT16 m = M_CountLevelsToShowInList(cv_newgametype.value)-1; + UINT16 m = M_CountLevelsToShowInList(levellist.newgametype)-1; - levellist_scroll.dest = (6*levellist_scroll.cursor); + levellist.dest = (6*levellist.cursor); - if (levellist_scroll.dest < 3) - levellist_scroll.dest = 3; + if (levellist.dest < 3) + levellist.dest = 3; - if (levellist_scroll.dest > (6*m)-3) - levellist_scroll.dest = (6*m)-3; + if (levellist.dest > (6*m)-3) + levellist.dest = (6*m)-3; } void M_LevelSelectInit(INT32 choice) @@ -2112,36 +2062,64 @@ void M_LevelSelectInit(INT32 choice) switch (currentMenu->menuitems[itemOn].mvar1) { case 0: - levellist_cupgrid.grandprix = false; - levellist_scroll.timeattack = false; + cupgrid.grandprix = false; + levellist.timeattack = false; break; case 1: - levellist_cupgrid.grandprix = false; - levellist_scroll.timeattack = true; - break; - case 2: - levellist_cupgrid.grandprix = true; - levellist_scroll.timeattack = false; + cupgrid.grandprix = false; + levellist.timeattack = true; break; + /*case 2: + cupgrid.grandprix = true; + levellist.timeattack = false; + break;*/ default: CONS_Alert(CONS_WARNING, "Bad level select init\n"); return; } - CV_StealthSetValue(&cv_newgametype, currentMenu->menuitems[itemOn].mvar2); + levellist.newgametype = currentMenu->menuitems[itemOn].mvar2; PLAY_CupSelectDef.prevMenu = currentMenu; - if (cv_newgametype.value == GT_RACE) + // Obviously go to Cup Select in gametypes that have cups. + // Use a really long level select in gametypes that don't use cups. + + if (levellist.newgametype == GT_RACE) { + cupheader_t *cup = kartcupheaders; + UINT8 highestid = 0; + + // Make sure there's valid cups before going to this menu. + if (cup == NULL) + I_Error("Can you really call this a racing game, I didn't recieve any Cups on my pillow or anything"); + + while (cup) + { + if (cup->unlockrequired == -1 || unlockables[cup->unlockrequired].unlocked) + highestid = cup->id; + cup = cup->next; + } + + cupgrid.numpages = (highestid / (CUPMENU_COLUMNS * CUPMENU_ROWS)) + 1; + PLAY_LevelSelectDef.prevMenu = &PLAY_CupSelectDef; M_SetupNextMenu(&PLAY_CupSelectDef, false); + + return; } - else + + // Reset position properly if you go back & forth between gametypes + if (levellist.selectedcup) { - selectedcup = NULL; - PLAY_LevelSelectDef.prevMenu = currentMenu; - M_SetupNextMenu(&PLAY_LevelSelectDef, false); + levellist.cursor = 0; + levellist.selectedcup = NULL; } + + M_LevelSelectScrollDest(); + levellist.y = levellist.dest; + + PLAY_LevelSelectDef.prevMenu = currentMenu; + M_SetupNextMenu(&PLAY_LevelSelectDef, false); } void M_CupSelectHandler(INT32 choice) @@ -2150,7 +2128,7 @@ void M_CupSelectHandler(INT32 choice) while (newcup) { - if (newcup->id == CUPID) + if (newcup->id == CUPMENU_CURSORID) break; newcup = newcup->next; } @@ -2158,53 +2136,55 @@ void M_CupSelectHandler(INT32 choice) switch (choice) { case KEY_RIGHTARROW: - levellist_cupgrid.x++; - if (levellist_cupgrid.x >= CUPS_COLUMNS) + cupgrid.x++; + if (cupgrid.x >= CUPMENU_COLUMNS) { - levellist_cupgrid.x = 0; - //levellist_cupgrid.pageno++; + cupgrid.x = 0; + cupgrid.pageno++; + if (cupgrid.pageno >= cupgrid.numpages) + cupgrid.pageno = 0; } S_StartSound(NULL, sfx_s3k5b); break; case KEY_LEFTARROW: - levellist_cupgrid.x--; - if (levellist_cupgrid.x < 0) + cupgrid.x--; + if (cupgrid.x < 0) { - levellist_cupgrid.x = CUPS_COLUMNS-1; - //levellist_cupgrid.pageno--; + cupgrid.x = CUPMENU_COLUMNS-1; + cupgrid.pageno--; + if (cupgrid.pageno < 0) + cupgrid.pageno = cupgrid.numpages-1; } S_StartSound(NULL, sfx_s3k5b); break; case KEY_UPARROW: - levellist_cupgrid.y++; - if (levellist_cupgrid.y >= CUPS_ROWS) - { - levellist_cupgrid.y = 0; - //levellist_cupgrid.pageno++; - } + cupgrid.y++; + if (cupgrid.y >= CUPMENU_ROWS) + cupgrid.y = 0; S_StartSound(NULL, sfx_s3k5b); break; case KEY_DOWNARROW: - levellist_cupgrid.y--; - if (levellist_cupgrid.y < 0) - { - levellist_cupgrid.y = CUPS_ROWS-1; - //levellist_cupgrid.pageno--; - } + cupgrid.y--; + if (cupgrid.y < 0) + cupgrid.y = CUPMENU_ROWS-1; S_StartSound(NULL, sfx_s3k5b); break; case KEY_ENTER: - if (!newcup) - break; - - if (!selectedcup || newcup->id != selectedcup->id) // Keep cursor position if you select the same cup again + if ((!newcup) || (newcup && newcup->unlockrequired != -1 && !unlockables[newcup->unlockrequired].unlocked)) { - levellist_scroll.cursor = 0; - selectedcup = newcup; + S_StartSound(NULL, sfx_s3kb2); + break; + } + + // Keep cursor position if you select the same cup again, reset if it's a different cup + if (!levellist.selectedcup || newcup->id != levellist.selectedcup->id) + { + levellist.cursor = 0; + levellist.selectedcup = newcup; } M_LevelSelectScrollDest(); - levellist_scroll.y = levellist_scroll.dest; + levellist.y = levellist.dest; M_SetupNextMenu(&PLAY_LevelSelectDef, false); S_StartSound(NULL, sfx_s3k63); @@ -2222,87 +2202,109 @@ void M_CupSelectHandler(INT32 choice) void M_CupSelectTick(void) { - levellist_cupgrid.previewanim++; + cupgrid.previewanim++; } void M_LevelSelectHandler(INT32 choice) { - INT16 start = M_GetFirstLevelInList(cv_newgametype.value)-1; - INT16 maxlevels = M_CountLevelsToShowInList(cv_newgametype.value); - INT16 map = start; + INT16 start = M_GetFirstLevelInList(levellist.newgametype); + INT16 maxlevels = M_CountLevelsToShowInList(levellist.newgametype); - if (levellist_scroll.y != levellist_scroll.dest) + if (levellist.y != levellist.dest) return; switch (choice) { case KEY_UPARROW: - levellist_scroll.cursor--; - if (levellist_scroll.cursor < 0) - levellist_scroll.cursor = maxlevels-1; + levellist.cursor--; + if (levellist.cursor < 0) + levellist.cursor = maxlevels-1; S_StartSound(NULL, sfx_s3k5b); break; case KEY_DOWNARROW: - levellist_scroll.cursor++; - if (levellist_scroll.cursor >= maxlevels) - levellist_scroll.cursor = 0; + levellist.cursor++; + if (levellist.cursor >= maxlevels) + levellist.cursor = 0; S_StartSound(NULL, sfx_s3k5b); break; case KEY_ENTER: - map = start + levellist_scroll.cursor; - - while (!M_CanShowLevelInList(map, cv_newgametype.value) && map < NUMMAPS) - map++; - - if (map >= NUMMAPS) - break; - - CV_SetValue(&cv_nextmap, map); - - if (levellist_scroll.timeattack) - M_SetupNextMenu(&PLAY_TimeAttackDef, false); - else { - UINT8 ssplayers = cv_splitplayers.value-1; + INT16 map = start; + INT16 add = levellist.cursor; - netgame = false; - multiplayer = true; - - strncpy(connectedservername, cv_servername.string, MAXSERVERNAME); - - // Still need to reset devmode - cv_debug = 0; - - if (strlen(cv_dummyjoinpassword.string) > 0) - D_SetJoinPassword(cv_dummyjoinpassword.string); - else - joinpasswordset = false; - - if (demo.playback) - G_StopDemo(); - if (metalrecording) - G_StopMetalDemo(); - - if (!cv_nextmap.value) - CV_SetValue(&cv_nextmap, G_RandMap(G_TOLFlag(cv_newgametype.value), -1, false, 0, false, NULL)+1); - - if (cv_maxplayers.value < ssplayers+1) - CV_SetValue(&cv_maxplayers, ssplayers+1); - - if (splitscreen != ssplayers) + while (add > 0) { - splitscreen = ssplayers; - SplitScreen_OnChange(); + map++; + + while (!M_CanShowLevelInList(map, levellist.newgametype) && map < NUMMAPS) + map++; + + if (map >= NUMMAPS) + break; + + add--; } - paused = false; - SV_StartSinglePlayerServer(); - multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... - D_MapChange(cv_nextmap.value, cv_newgametype.value, (cv_kartencore.value == 1), 1, 1, false, false); + if (map >= NUMMAPS) + break; - M_ClearMenus(true); + levellist.choosemap = map; + + if (levellist.timeattack) + { + M_SetupNextMenu(&PLAY_TimeAttackDef, false); + S_StartSound(NULL, sfx_s3k63); + } + else + { + UINT8 ssplayers = cv_splitplayers.value-1; + + netgame = false; + multiplayer = true; + + strncpy(connectedservername, cv_servername.string, MAXSERVERNAME); + + // Still need to reset devmode + cv_debug = 0; + + if (strlen(cv_dummyjoinpassword.string) > 0) + D_SetJoinPassword(cv_dummyjoinpassword.string); + else + joinpasswordset = false; + + if (demo.playback) + G_StopDemo(); + if (metalrecording) + G_StopMetalDemo(); + + /*if (levellist.choosemap == 0) + levellist.choosemap = G_RandMap(G_TOLFlag(levellist.newgametype), -1, false, 0, false, NULL);*/ + + if (cv_maxplayers.value < ssplayers+1) + CV_SetValue(&cv_maxplayers, ssplayers+1); + + if (splitscreen != ssplayers) + { + splitscreen = ssplayers; + SplitScreen_OnChange(); + } + + S_StartSound(NULL, sfx_s3k63); + + // Early fadeout to let the sound finish playing + F_WipeStartScreen(); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeEndScreen(); + F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); + + paused = false; + SV_StartSinglePlayerServer(); + multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... + D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false); + + M_ClearMenus(true); + } } - S_StartSound(NULL, sfx_s3k63); break; case KEY_ESCAPE: if (currentMenu->prevMenu) @@ -2319,10 +2321,20 @@ void M_LevelSelectHandler(INT32 choice) void M_LevelSelectTick(void) { - if (levellist_scroll.y > levellist_scroll.dest) - levellist_scroll.y--; - else if (levellist_scroll.y < levellist_scroll.dest) - levellist_scroll.y++; + UINT8 times = 1 + (abs(levellist.dest - levellist.y) / 21); + + while (times) // increase speed as you're farther away + { + if (levellist.y > levellist.dest) + levellist.y--; + else if (levellist.y < levellist.dest) + levellist.y++; + + if (levellist.y == levellist.dest) + break; + + times--; + } } // ===================== diff --git a/src/m_cheat.c b/src/m_cheat.c index 6bab3c7bd..dde197660 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -69,7 +69,7 @@ static UINT8 cheatf_warp(void) if (menuactive && currentMenu != &MainDef) return 0; // Only on the main menu! - // Temporarily unlock EVERYTHING. + // Unlock EVERYTHING. for (i = 0; i < MAXUNLOCKABLES; i++) { if (!unlockables[i].conditionset) @@ -120,30 +120,13 @@ static UINT8 cheatf_devmode(void) } #endif -/*static cheatseq_t cheat_ultimate = { - 0, cheatf_ultimate, - { SCRAMBLE('u'), SCRAMBLE('l'), SCRAMBLE('t'), SCRAMBLE('i'), SCRAMBLE('m'), SCRAMBLE('a'), SCRAMBLE('t'), SCRAMBLE('e'), 0xff } -};*/ - -/*static cheatseq_t cheat_ultimate_joy = { - 0, cheatf_ultimate, - { SCRAMBLE(KEY_UPARROW), SCRAMBLE(KEY_UPARROW), SCRAMBLE(KEY_DOWNARROW), SCRAMBLE(KEY_DOWNARROW), - SCRAMBLE(KEY_LEFTARROW), SCRAMBLE(KEY_RIGHTARROW), SCRAMBLE(KEY_LEFTARROW), SCRAMBLE(KEY_RIGHTARROW), - SCRAMBLE(KEY_ENTER), 0xff } -};*/ - static cheatseq_t cheat_warp = { 0, cheatf_warp, - //{ SCRAMBLE('r'), SCRAMBLE('e'), SCRAMBLE('d'), SCRAMBLE('x'), SCRAMBLE('v'), SCRAMBLE('i'), 0xff } { SCRAMBLE('b'), SCRAMBLE('a'), SCRAMBLE('n'), SCRAMBLE('a'), SCRAMBLE('n'), SCRAMBLE('a'), 0xff } }; static cheatseq_t cheat_warp_joy = { 0, cheatf_warp, - /*{ SCRAMBLE(KEY_LEFTARROW), SCRAMBLE(KEY_LEFTARROW), SCRAMBLE(KEY_UPARROW), - SCRAMBLE(KEY_RIGHTARROW), SCRAMBLE(KEY_RIGHTARROW), SCRAMBLE(KEY_UPARROW), - SCRAMBLE(KEY_LEFTARROW), SCRAMBLE(KEY_UPARROW), - SCRAMBLE(KEY_ENTER), 0xff }*/ { SCRAMBLE(KEY_LEFTARROW), SCRAMBLE(KEY_UPARROW), SCRAMBLE(KEY_RIGHTARROW), SCRAMBLE(KEY_RIGHTARROW), SCRAMBLE(KEY_UPARROW), SCRAMBLE(KEY_LEFTARROW), SCRAMBLE(KEY_DOWNARROW), SCRAMBLE(KEY_RIGHTARROW), @@ -239,8 +222,6 @@ boolean cht_Responder(event_t *ev) else ch = (UINT8)ev->data1; - //ret += cht_CheckCheat(&cheat_ultimate, (char)ch); - //ret += cht_CheckCheat(&cheat_ultimate_joy, (char)ch); ret += cht_CheckCheat(&cheat_warp, (char)ch); ret += cht_CheckCheat(&cheat_warp_joy, (char)ch); #ifdef DEVELOP diff --git a/src/st_stuff.c b/src/st_stuff.c index e1965e807..f78b6b5a0 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -803,7 +803,7 @@ static void ST_drawLevelTitle(void) if (subttl[0]) V_DrawRightAlignedString(sub + zonexpos - 8, bary+1, V_ALLOWLOWERCASE, subttl); //else - //V_DrawRightAlignedString(sub + zonexpos - 8, bary+1, V_ALLOWLOWERCASE, va("%s Mode", gametype_cons_t[gametype].strvalue)); + //V_DrawRightAlignedString(sub + zonexpos - 8, bary+1, V_ALLOWLOWERCASE, va("%s Mode", Gametype_Names[gametype])); } ttlnumxpos += sub; diff --git a/src/y_inter.c b/src/y_inter.c index 128fb8563..61db5aadd 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -347,10 +347,11 @@ void Y_IntermissionDrawer(void) #endif else { - if (widebgpatch && rendermode == render_soft && vid.width / vid.dupx == 400) - V_DrawScaledPatch(0, 0, V_SNAPTOLEFT, widebgpatch); - else - V_DrawScaledPatch(0, 0, 0, bgpatch); + V_DrawFixedPatch(0, 0, FRACUNIT, 0, bgpatch, NULL); + + // draw non-green resolution border + if (widebgpatch && ((vid.width % BASEVIDWIDTH != 0) || (vid.height % BASEVIDHEIGHT != 0))) + V_DrawFixedPatch(0, 0, FRACUNIT, 0, widebgpatch, NULL); } } else @@ -775,8 +776,8 @@ static void Y_UpdateRecordReplays(void) G_SaveGameData(false); // Update timeattack menu's replay availability. - CV_AddValue(&cv_nextmap, 1); - CV_AddValue(&cv_nextmap, -1); + //CV_AddValue(&cv_nextmap, 1); + //CV_AddValue(&cv_nextmap, -1); } // @@ -869,12 +870,11 @@ void Y_StartIntermission(void) break; } - //if (intertype == int_race || intertype == int_match) - { - //bgtile = W_CachePatchName("SRB2BACK", PU_STATIC); - usetile = useinterpic = false; - usebuffer = true; - } + + bgpatch = W_CachePatchName("MENUBG", PU_STATIC); + widebgpatch = W_CachePatchName("WEIRDRES", PU_STATIC); + + useinterpic = usetile = usebuffer = false; } // ====== @@ -1519,7 +1519,7 @@ void Y_StartVote(void) // set up the gtc and gts levelinfo[i].gtc = G_GetGametypeColor(votelevels[i][1]); if (i == 2 && votelevels[i][1] != votelevels[0][1]) - levelinfo[i].gts = gametype_cons_t[votelevels[i][1]].strvalue; + levelinfo[i].gts = Gametype_Names[votelevels[i][1]]; else levelinfo[i].gts = NULL; diff --git a/src/y_inter.h b/src/y_inter.h index 6162dc282..2964fb37a 100644 --- a/src/y_inter.h +++ b/src/y_inter.h @@ -9,8 +9,6 @@ /// \file y_inter.h /// \brief Tally screens, or "Intermissions" as they were formally called in Doom -extern boolean usebuffer; - void Y_IntermissionDrawer(void); void Y_Ticker(void); void Y_StartIntermission(void); From a7dac9d04bb0673973978303574abbb2a0fc2a4e Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Sun, 27 Oct 2019 15:50:22 -0400 Subject: [PATCH 011/379] Implement images --- src/k_menudef.c | 2 +- src/k_menudraw.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_menudef.c b/src/k_menudef.c index 09cfa355b..7dd279b98 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -157,7 +157,7 @@ menu_t PLAY_TimeAttackDef = { menuitem_t PLAY_BattleGamemodesMenu[] = { {IT_STRING | IT_CALL, "Survival", "It's last hedgehog standing in this free-for-all!", - NULL, M_LevelSelectInit, 0, GT_MATCH}, + "MENIMG00", M_LevelSelectInit, 0, GT_MATCH}, {IT_STRING | IT_CALL, "Time Attack", "Bust up all of the capsules in record time!", NULL, M_LevelSelectInit, 1, GT_MATCH}, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index b07775695..4ca28a367 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -175,7 +175,7 @@ static void M_DrawMenuPreviews(void) { V_DrawFixedPatch(172<menuitems[itemOn].patch != NULL) - V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName(currentMenu->menuitems[itemOn].patch, PU_CACHE), NULL); + V_DrawFixedPatch(181<menuitems[itemOn].patch, PU_CACHE), NULL); } // Converts a string into question marks. From 6520ae8d55b1e4a085b1ec67972962674ffdb1b3 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Fri, 15 Nov 2019 17:21:14 -0500 Subject: [PATCH 012/379] Static for placeholder menu images --- src/k_menudraw.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 4ca28a367..fb5cef736 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -162,8 +162,11 @@ void M_Drawer(void) static void M_DrawMenuTooltips(void) { V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL); + if (currentMenu->menuitems[itemOn].tooltip != NULL) + { V_DrawCenteredThinString(BASEVIDWIDTH/2, 12, V_ALLOWLOWERCASE|V_6WIDTHSPACE, currentMenu->menuitems[itemOn].tooltip); + } } // @@ -174,8 +177,16 @@ static void M_DrawMenuTooltips(void) static void M_DrawMenuPreviews(void) { V_DrawFixedPatch(172<menuitems[itemOn].patch != NULL) + + if (currentMenu->menuitems[itemOn].patch == NULL) + { + patch_t *st = W_CachePatchName(va("MIMGST0%d", (skullAnimCounter % 4) + 1), PU_CACHE); + V_DrawFixedPatch(181<menuitems[itemOn].patch, PU_CACHE), NULL); + } } // Converts a string into question marks. From 4f34de189e7d6c1e9683ed91cc08ee8cfd366137 Mon Sep 17 00:00:00 2001 From: James Date: Sat, 16 Nov 2019 17:27:35 -0500 Subject: [PATCH 013/379] Add match race image --- src/k_menudef.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_menudef.c b/src/k_menudef.c index 7dd279b98..faafeacb2 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -88,7 +88,7 @@ menuitem_t PLAY_RaceGamemodesMenu[] = NULL, M_LevelSelectInit, 2, GT_RACE}, {IT_STRING | IT_CALL, "Match Race", "Play by your own rules in a specialized, single race!", - NULL, M_LevelSelectInit, 0, GT_RACE}, + "MENIMG01", M_LevelSelectInit, 0, GT_RACE}, {IT_STRING | IT_CALL, "Time Attack", "Record your best time on any track!", NULL, M_LevelSelectInit, 1, GT_RACE}, From 735f3ca5134f483b0d56447a6b6ec57d0fc42b42 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 16 May 2020 04:39:27 -0400 Subject: [PATCH 014/379] Temporarily disable M_DemoResponder --- src/d_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index e64fc9612..3d26fee6a 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -249,9 +249,9 @@ void D_ProcessEvents(void) continue; // menu ate the event // Demo input: - if (demo.playback) - if (M_DemoResponder(ev)) - continue; // demo ate the event + //if (demo.playback) + //if (M_DemoResponder(ev)) + //continue; // demo ate the event // console input if (CON_Responder(ev)) From 15a243db09cc0740cc0a4d5b586612be9bb583b7 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 16 May 2020 04:58:52 -0400 Subject: [PATCH 015/379] Fix other merge errors --- src/d_main.c | 10 ++++++---- src/d_netcmd.c | 2 +- src/k_menudraw.c | 18 ++++++++---------- src/k_menufunc.c | 8 -------- 4 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 3d26fee6a..b36850486 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -249,9 +249,11 @@ void D_ProcessEvents(void) continue; // menu ate the event // Demo input: - //if (demo.playback) - //if (M_DemoResponder(ev)) - //continue; // demo ate the event + /* + if (demo.playback) + if (M_DemoResponder(ev)) + continue; // demo ate the event + */ // console input if (CON_Responder(ev)) @@ -332,7 +334,7 @@ static void D_Display(void) } else //dedicated servers { - F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK, "FADEMAP0", false, false); + F_RunWipe(wipedefs[wipedefindex], gamestate != GS_MENU, "FADEMAP0", false, false); wipegamestate = gamestate; } } diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 38b85ab6f..82e917b7b 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3190,7 +3190,7 @@ static void Command_Pause(void) } else if (modeattacking) // in time attack, pausing restarts the map { - M_ModeAttackRetry(0); // directly call from m_menu; + //M_ModeAttackRetry(0); // directly call from m_menu; return; } diff --git a/src/k_menudraw.c b/src/k_menudraw.c index fb5cef736..3ad0e17e0 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -500,17 +500,15 @@ void M_DrawMessageMenu(void) // You can even put multiple images in one menu! void M_DrawImageDef(void) { - // Grr. Need to autodetect for pic_ts. - pic_t *pictest = (pic_t *)W_CachePatchName(currentMenu->menuitems[itemOn].patch, PU_CACHE); - if (!pictest->zero) - V_DrawScaledPic(0,0,0,W_GetNumForName(currentMenu->menuitems[itemOn].patch)); + patch_t *patch = W_CachePatchName(currentMenu->menuitems[itemOn].text, PU_CACHE); + + if (patch->width <= BASEVIDWIDTH) + { + V_DrawScaledPatch(0, 0, 0, patch); + } else { - patch_t *patch = W_CachePatchName(currentMenu->menuitems[itemOn].patch, PU_CACHE); - if (patch->height <= BASEVIDHEIGHT) - V_DrawScaledPatch(0,0,0,patch); - else - V_DrawSmallScaledPatch(0,0,0,patch); + V_DrawSmallScaledPatch(0, 0, 0, patch); } if (currentMenu->menuitems[itemOn].mvar1) @@ -658,7 +656,7 @@ static void M_DrawCharSelectPreview(UINT8 num) if (skin != -1) { - statenum_t st = S_KART_WALK1; + statenum_t st = S_KART_FAST1; UINT32 flags = 0; UINT32 frame; diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 4b7537cc0..440df6666 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1323,9 +1323,6 @@ void M_Init(void) #ifndef NONET CV_RegisterVar(&cv_serversort); #endif - - //todo put this somewhere better... - CV_RegisterVar(&cv_allcaps); } // ================================================== @@ -2267,11 +2264,6 @@ void M_LevelSelectHandler(INT32 choice) // Still need to reset devmode cv_debug = 0; - if (strlen(cv_dummyjoinpassword.string) > 0) - D_SetJoinPassword(cv_dummyjoinpassword.string); - else - joinpasswordset = false; - if (demo.playback) G_StopDemo(); if (metalrecording) From 09bebbc558ff7179d5d1f8060a4660eed4627d5d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 8 Jun 2020 01:03:20 -0400 Subject: [PATCH 016/379] You can start GP from the new menu --- src/k_menufunc.c | 56 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 440df6666..9e706fbed 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -48,6 +48,7 @@ #include "k_kart.h" // SRB2kart #include "d_player.h" // KITEM_ constants #include "doomstat.h" // MAXSPLITSCREENPLAYERS +#include "k_grandprix.h" // MAXSPLITSCREENPLAYERS #include "i_joy.h" // for joystick menu controls @@ -2066,10 +2067,10 @@ void M_LevelSelectInit(INT32 choice) cupgrid.grandprix = false; levellist.timeattack = true; break; - /*case 2: + case 2: cupgrid.grandprix = true; levellist.timeattack = false; - break;*/ + break; default: CONS_Alert(CONS_WARNING, "Bad level select init\n"); return; @@ -2173,17 +2174,52 @@ void M_CupSelectHandler(INT32 choice) break; } - // Keep cursor position if you select the same cup again, reset if it's a different cup - if (!levellist.selectedcup || newcup->id != levellist.selectedcup->id) + if (cupgrid.grandprix == true) { - levellist.cursor = 0; - levellist.selectedcup = newcup; + // Early fadeout to let the sound finish playing + F_WipeStartScreen(); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeEndScreen(); + F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); + + M_ClearMenus(true); + + memset(&grandprixinfo, 0, sizeof(struct grandprixinfo)); + + // TODO: game settings screen + grandprixinfo.gamespeed = KARTSPEED_NORMAL; + grandprixinfo.masterbots = false; + grandprixinfo.encore = false; + + grandprixinfo.cup = newcup; + + grandprixinfo.gp = true; + grandprixinfo.roundnum = 1; + grandprixinfo.initalize = true; + + G_DeferedInitNew( + false, + G_BuildMapName(grandprixinfo.cup->levellist[0] + 1), + (UINT8)cv_skin.value, + (UINT8)(cv_splitplayers.value - 1), + false + ); + } + else + { + // Keep cursor position if you select the same cup again, reset if it's a different cup + if (!levellist.selectedcup || newcup->id != levellist.selectedcup->id) + { + levellist.cursor = 0; + levellist.selectedcup = newcup; + } + + M_LevelSelectScrollDest(); + levellist.y = levellist.dest; + + M_SetupNextMenu(&PLAY_LevelSelectDef, false); } - M_LevelSelectScrollDest(); - levellist.y = levellist.dest; - - M_SetupNextMenu(&PLAY_LevelSelectDef, false); S_StartSound(NULL, sfx_s3k63); break; case KEY_ESCAPE: From b88b8ffc2aa9f44f63a2319321d46905703f9c26 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 9 Jun 2020 16:39:49 -0400 Subject: [PATCH 017/379] You shouldn't be here... --- src/m_menu.c | 11208 ------------------------------------------------- 1 file changed, 11208 deletions(-) delete mode 100644 src/m_menu.c diff --git a/src/m_menu.c b/src/m_menu.c deleted file mode 100644 index 16cef2eb9..000000000 --- a/src/m_menu.c +++ /dev/null @@ -1,11208 +0,0 @@ -// 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 "k_pwrlv.h" -#include "d_player.h" // KITEM_ constants -#include "k_color.h" -#include "k_grandprix.h" - -#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 - -#if defined (NONET) || defined (TESTERS) -#define NOMENUHOST -#endif - -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[MAXSKINS]; - -//static char *char_notes = NULL; -//static fixed_t char_scroll = 0; - -boolean menuactive = false; -boolean fromlevelselect = false; - -typedef enum -{ - LLM_CREATESERVER, - LLM_LEVELSELECT, - LLM_TIMEATTACK, - LLM_BREAKTHECAPSULES -} 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 -static void M_SinglePlayerMenu(INT32 choice); -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_GrandPrixTemp(INT32 choice); -static void M_StartGrandPrix(INT32 choice); -static void M_TimeAttack(INT32 choice); -static boolean M_QuitTimeAttackMenu(void); -static void M_BreakTheCapsules(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_ModeAttackEndGame(INT32 choice); -static void M_SetGuestReplay(INT32 choice); -//static void M_ChoosePlayer(INT32 choice); -menu_t SP_LevelStatsDef; -static menu_t SP_GrandPrixTempDef; -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 -#ifndef TESTERS -static void M_StartServerMenu(INT32 choice); -#endif -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 -#ifndef TESTERS -static void M_StartOfflineServerMenu(INT32 choice); -#endif -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_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 -menu_t MISC_ReplayHutDef; -menu_t MISC_ReplayOptionsDef; -static void M_HandleReplayHutList(INT32 choice); -static void M_DrawReplayHut(void); -static void M_DrawReplayStartMenu(void); -static boolean M_QuitReplayHut(void); -static void M_HutStartReplay(INT32 choice); - -static void M_DrawPlaybackMenu(void); -static void M_PlaybackRewind(INT32 choice); -static void M_PlaybackPause(INT32 choice); -static void M_PlaybackFastForward(INT32 choice); -static void M_PlaybackAdvance(INT32 choice); -static void M_PlaybackSetViews(INT32 choice); -static void M_PlaybackAdjustView(INT32 choice); -static void M_PlaybackToggleFreecam(INT32 choice); -static void M_PlaybackQuit(INT32 choice); - -static UINT8 playback_enterheld = 0; // horrid hack to prevent holding the button from being extremely fucked - -// 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_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); -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 dehacked.c and doomstat.h! -CV_PossibleValue_t gametype_cons_t[NUMGAMETYPES+1]; - -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}; - -static CV_PossibleValue_t dummygpdifficulty_cons_t[] = {{0, "Easy"}, {1, "Normal"}, {2, "Hard"}, {3, "Master"}, {0, NULL}}; -static CV_PossibleValue_t dummygpcup_cons_t[50] = {{1, "TEMP"}}; // A REALLY BIG NUMBER, SINCE THIS IS TEMP UNTIL NEW MENUS - -static consvar_t cv_dummygpdifficulty = {"dummygpdifficulty", "Normal", CV_HIDEN, dummygpdifficulty_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -static consvar_t cv_dummygpencore = {"dummygpencore", "Off", CV_HIDEN, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -static consvar_t cv_dummygpcup = {"dummygpcup", "TEMP", CV_HIDEN, dummygpcup_cons_t, NULL, 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}, -#ifdef TESTERS - {IT_GRAYEDOUT, NULL, "1 Player", NULL, 84}, -#else - {IT_CALL |IT_STRING, NULL, "1 Player", M_SinglePlayerMenu, 84}, -#endif - {IT_SUBMENU|IT_STRING, NULL, "Multiplayer", &MP_MainDef, 92}, - {IT_CALL |IT_STRING, NULL, "Options", M_Options, 100}, - /* I don't think is useful at all... */ - {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_KEYHANDLER|IT_NOTHING, NULL, "", M_HandleReplayHutList, 0}, // Dummy menuitem for the replay list - {IT_NOTHING, NULL, "", NULL, 0}, // Dummy for handling wrapping to the top of the menu.. -}; - -static menuitem_t MISC_ReplayStartMenu[] = -{ - {IT_CALL |IT_STRING, NULL, "Load Addons and Watch", M_HutStartReplay, 0}, - {IT_CALL |IT_STRING, NULL, "Watch Without Addons", M_HutStartReplay, 10}, - {IT_CALL |IT_STRING, NULL, "Watch Replay", M_HutStartReplay, 10}, - {IT_SUBMENU |IT_STRING, NULL, "Back", &MISC_ReplayHutDef, 30}, -}; - -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}, -}; - -static tic_t playback_last_menu_interaction_leveltime = 0; -static menuitem_t PlaybackMenu[] = -{ - {IT_CALL | IT_STRING, "M_PHIDE", "Hide Menu (Esc)", M_SelectableClearMenus, 0}, - - {IT_CALL | IT_STRING, "M_PREW", "Rewind ([)", M_PlaybackRewind, 20}, - {IT_CALL | IT_STRING, "M_PPAUSE", "Pause (\\)", M_PlaybackPause, 36}, - {IT_CALL | IT_STRING, "M_PFFWD", "Fast-Forward (])", M_PlaybackFastForward, 52}, - {IT_CALL | IT_STRING, "M_PSTEPB", "Backup Frame ([)", M_PlaybackRewind, 20}, - {IT_CALL | IT_STRING, "M_PRESUM", "Resume", M_PlaybackPause, 36}, - {IT_CALL | IT_STRING, "M_PFADV", "Advance Frame (])", M_PlaybackAdvance, 52}, - - {IT_ARROWS | IT_STRING, "M_PVIEWS", "View Count (- and =)", M_PlaybackSetViews, 72}, - {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint (1)", M_PlaybackAdjustView, 88}, - {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint 2 (2)", M_PlaybackAdjustView, 104}, - {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint 3 (3)", M_PlaybackAdjustView, 120}, - {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint 4 (4)", M_PlaybackAdjustView, 136}, - - {IT_CALL | IT_STRING, "M_PVIEWS", "Toggle Free Camera (')", M_PlaybackToggleFreecam, 156}, - {IT_CALL | IT_STRING, "M_PEXIT", "Stop Playback", M_PlaybackQuit, 172}, -}; -typedef enum -{ - playback_hide, - playback_rewind, - playback_pause, - playback_fastforward, - playback_backframe, - playback_resume, - playback_advanceframe, - playback_viewcount, - playback_view1, - playback_view2, - playback_view3, - playback_view4, - playback_freecamera, - //playback_moreoptions, - playback_quit -} playback_e; - -// --------------------------------- -// 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, "Addons...", 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[] = -{ -#ifndef TESTERS - {IT_STRING|IT_SUBMENU, NULL, "Unlockables", &SR_UnlockChecklistDef, 100}, -#endif - {IT_CALL|IT_STRING|IT_CALL_NOTMODIFIED, NULL, "Statistics", M_Statistics, 108}, - {IT_CALL|IT_STRING, NULL, "Replay Hut", M_ReplayHut, 116}, - {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_STRING|IT_CALL, NULL, "Grand Prix", M_GrandPrixTemp, 92}, - {IT_SECRET, NULL, "Time Attack", M_TimeAttack, 100}, - {IT_SECRET, NULL, "Break the Capsules", M_BreakTheCapsules, 108}, -}; - -enum -{ - spgrandprix, - sptimeattack, - spbreakthecapsules -}; - -// Single Player Load Game -static menuitem_t SP_GrandPrixPlaceholderMenu[] = -{ - {IT_STRING|IT_CVAR, NULL, "Character", &cv_chooseskin, 10}, - {IT_STRING|IT_CVAR, NULL, "Color", &cv_playercolor, 20}, - - {IT_STRING|IT_CVAR, NULL, "Difficulty", &cv_dummygpdifficulty, 40}, - {IT_STRING|IT_CVAR, NULL, "Encore Mode", &cv_dummygpencore, 50}, - - {IT_STRING|IT_CVAR, NULL, "Cup", &cv_dummygpcup, 70}, - {IT_STRING|IT_CALL, NULL, "Start", M_StartGrandPrix, 80}, -}; - -// 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 NOMENUHOST - {IT_STRING|IT_CALL, NULL, "Internet/LAN...", M_StartServerMenu, 110-24}, -#else - {IT_GRAYEDOUT, NULL, "Internet/LAN...", NULL, 110-24}, -#endif -#ifdef TESTERS - {IT_GRAYEDOUT, NULL, "Offline...", NULL, 118-24}, -#else - {IT_STRING|IT_CALL, NULL, "Offline...", M_StartOfflineServerMenu, 118-24}, -#endif - - {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, 10}, - {IT_STRING|IT_CALL, NULL, "Room...", M_RoomMenu, 20}, - {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Server Name", &cv_servername, 30}, - - {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_SUBMENU|IT_STRING, NULL, "Gameplay Options...", &OP_GameOptionsDef, 70}, - {IT_SUBMENU|IT_STRING, NULL, "Server Options...", &OP_ServerOptionsDef, 80}, - - {IT_SUBMENU|IT_STRING, NULL, "Data Options...", &OP_DataOptionsDef, 100}, - - {IT_CALL|IT_STRING, NULL, "Tricks & Secrets (F1)", M_Manual, 120}, - {IT_CALL|IT_STRING, NULL, "Play Credits", M_Credits, 130}, -}; - -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_globalgamma, 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, "Field of View", &cv_fov, 75}, - - {IT_STRING | IT_CVAR, NULL, "Show FPS", &cv_ticrate, 90}, - {IT_STRING | IT_CVAR, NULL, "Vertical Sync", &cv_vidwait, 100}, - -#ifdef HWRENDER - {IT_SUBMENU|IT_STRING, NULL, "OpenGL Options...", &OP_OpenGLOptionsDef, 120}, -#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_fov, - op_video_fps, - op_video_vsync, -#ifdef HWRENDER - 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_STRING | IT_CVAR, NULL, "3D Models", &cv_grmdls, 10}, - {IT_STRING | IT_CVAR, NULL, "Fallback Player 3D Model", &cv_grfallbackplayermodel, 20}, - {IT_STRING|IT_CVAR, NULL, "Shaders", &cv_grshaders, 30}, - - {IT_STRING|IT_CVAR, NULL, "Texture Quality", &cv_scr_depth, 50}, - {IT_STRING|IT_CVAR, NULL, "Texture Filter", &cv_grfiltermode, 60}, - {IT_STRING|IT_CVAR, NULL, "Anisotropic", &cv_granisotropicmode, 70}, - - {IT_STRING|IT_CVAR, NULL, "Wall Contrast Style", &cv_grfakecontrast, 90}, - {IT_STRING|IT_CVAR, NULL, "Sprite Billboarding", &cv_grspritebillboarding, 100}, - {IT_STRING|IT_CVAR, NULL, "Software Perspective", &cv_grshearing, 110}, - - {IT_SUBMENU|IT_STRING, NULL, "Gamma...", &OP_OpenGLColorDef, 130}, -}; - -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_CALL, NULL, "Addon Options...", M_AddonsOptions, 20}, - {IT_STRING | IT_SUBMENU, NULL, "Replay Options...", &MISC_ReplayOptionsDef, 30}, - - {IT_STRING | IT_SUBMENU, NULL, "Erase Data...", &OP_EraseDataDef, 50}, -}; - -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 addons 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, "Track Power Levels", &cv_kartusepwrlv, 140}, -}; - -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 Addon 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, "Super Rings", M_HandleMonitorToggles, KITEM_SUPERRING}, - {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), - &OP_DataOptionsDef, - 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, - 0, - M_QuitReplayHut -}; - -menu_t MISC_ReplayOptionsDef = -{ - "M_REPOPT", - sizeof (MISC_ReplayOptionsMenu)/sizeof (menuitem_t), - &OP_DataOptionsDef, - MISC_ReplayOptionsMenu, - M_DrawGenericMenu, - 27, 40, - 0, - NULL -}; - -menu_t MISC_ReplayStartDef = -{ - NULL, - sizeof (MISC_ReplayStartMenu)/sizeof (menuitem_t), - &MISC_ReplayHutDef, - MISC_ReplayStartMenu, - M_DrawReplayStartMenu, - 30, 90, - 0, - NULL -}; - -menu_t PlaybackMenuDef = { - NULL, - sizeof (PlaybackMenu)/sizeof (menuitem_t), - NULL, - PlaybackMenu, - M_DrawPlaybackMenu, - //BASEVIDWIDTH/2 - 94, 2, - BASEVIDWIDTH/2 - 88, 2, - 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 = CENTERMENUSTYLE(NULL, SR_MainMenu, &MainDef, 72); - -//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_GrandPrixTempDef = DEFAULTMENUSTYLE(NULL, SP_GrandPrixPlaceholderMenu, &MainDef, 60, 30); - -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 -}; - -/*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); -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_DataOptionsDef, 30, 30); -menu_t OP_AddonsOptionsDef = DEFAULTMENUSTYLE("M_ADDONS", OP_AddonsOptionsMenu, &OP_DataOptionsDef, 30, 30); -menu_t OP_EraseDataDef = DEFAULTMENUSTYLE("M_DATA", OP_EraseDataMenu, &OP_DataOptionsDef, 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("media")+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1; - char *gpath = malloc(glen); - INT32 i; - - if (!gpath) - return; - - sprintf(gpath,"%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value)); - - CV_StealthSetValue(&cv_dummystaff, 0); - - active = 0; - 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 (levellistmode != LLM_BREAKTHECAPSULES) { - 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; - else if (cv == &cv_maxping) - choice *= 50; -#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 - - 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 - - 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-1) * cv_deadzone.value) >> FRACBITS; - 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; - } - } - } - - 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_globalgamma, 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) - { - - if (shiftdown && ch >= 32 && ch <= 127) - ch = shiftxform[ch]; - if (M_ChangeStringCvar(ch)) - return true; - else - routine = NULL; - } - else - routine = M_ChangeCvar; - } - - if (currentMenu == &PlaybackMenuDef && !con_destlines) - { - playback_last_menu_interaction_leveltime = leveltime; - // Flip left/right with up/down for the playback menu, since it's a horizontal icon row. - switch (ch) - { - case KEY_LEFTARROW: ch = KEY_UPARROW; break; - case KEY_UPARROW: ch = KEY_RIGHTARROW; break; - case KEY_RIGHTARROW: ch = KEY_DOWNARROW; break; - case KEY_DOWNARROW: ch = KEY_LEFTARROW; break; - - // arbitrary keyboard shortcuts because fuck you - - case '\'': // toggle freecam - M_PlaybackToggleFreecam(0); - break; - - case ']': // ffw / advance frame (depends on if paused or not) - if (paused) - M_PlaybackAdvance(0); - else - M_PlaybackFastForward(0); - break; - - case '[': // rewind /backupframe, uses the same function - M_PlaybackRewind(0); - break; - - case '\\': // pause - M_PlaybackPause(0); - break; - - // viewpoints, an annoyance (tm) - case '-': // viewpoint minus - M_PlaybackSetViews(-1); // yeah lol. - break; - - case '=': // viewpoint plus - M_PlaybackSetViews(1); // yeah lol. - break; - - // switch viewpoints: - case '1': // viewpoint for p1 (also f12) - // maximum laziness: - if (!demo.freecam) - G_AdjustView(1, 1, true); - break; - case '2': // viewpoint for p2 - if (!demo.freecam) - G_AdjustView(2, 1, true); - break; - case '3': // viewpoint for p3 - if (!demo.freecam) - G_AdjustView(3, 1, true); - break; - case '4': // viewpoint for p4 - if (!demo.freecam) - G_AdjustView(4, 1, true); - break; - - default: break; - } - } - - // 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 (currentMenu == &PlaybackMenuDef) - { - boolean held = (boolean)playback_enterheld; - if (held) - return true; - playback_enterheld = 3; - } - - 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 addons\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; -} - -// special responder for demos -boolean M_DemoResponder(event_t *ev) -{ - - INT32 ch = -1; // cur event data - boolean eatinput = false; // :omnom: - - //should be accounted for beforehand but just to be safe... - if (!demo.playback || demo.title) - 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 && !con_destlines) // not while the console is on please - { - ch = ev->data1; - // since this is ONLY for demos, there isn't MUCH for us to do. - // mirrored from m_responder - - switch (ch) - { - // arbitrary keyboard shortcuts because fuck you - - case '\'': // toggle freecam - M_PlaybackToggleFreecam(0); - eatinput = true; - break; - - case ']': // ffw / advance frame (depends on if paused or not) - if (paused) - M_PlaybackAdvance(0); - else - M_PlaybackFastForward(0); - eatinput = true; - break; - - case '[': // rewind /backupframe, uses the same function - M_PlaybackRewind(0); - break; - - case '\\': // pause - M_PlaybackPause(0); - eatinput = true; - break; - - // viewpoints, an annoyance (tm) - case '-': // viewpoint minus - M_PlaybackSetViews(-1); // yeah lol. - eatinput = true; - break; - - case '=': // viewpoint plus - M_PlaybackSetViews(1); // yeah lol. - eatinput = true; - break; - - // switch viewpoints: - case '1': // viewpoint for p1 (also f12) - // maximum laziness: - if (!demo.freecam) - G_AdjustView(1, 1, true); - break; - case '2': // viewpoint for p2 - if (!demo.freecam) - G_AdjustView(2, 1, true); - break; - case '3': // viewpoint for p3 - if (!demo.freecam) - G_AdjustView(3, 1, true); - break; - case '4': // viewpoint for p4 - if (!demo.freecam) - G_AdjustView(4, 1, true); - break; - - default: break; - } - - } - return eatinput; -} - - -// -// 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 && currentMenu != &PlaybackMenuDef) // Replay playback has its own background - 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) -{ - // intro might call this repeatedly - if (menuactive) - { - CON_ToggleOff(); // move away console - return; - } - - menuactive = true; - - if (demo.playback) - { - currentMenu = &PlaybackMenuDef; - playback_last_menu_interaction_leveltime = leveltime; - } - else if (!Playing()) - { - // Secret menu! - //MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED); - - currentMenu = &MainDef; -#ifdef TESTERS - itemOn = multiplr; -#else - itemOn = singleplr; -#endif - } - 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; - - if (currentMenu == &PlaybackMenuDef) - { - if (playback_enterheld > 0) - playback_enterheld--; - } - else - playback_enterheld = 0; - - //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); - - CV_RegisterVar(&cv_dummygpdifficulty); - CV_RegisterVar(&cv_dummygpencore); - CV_RegisterVar(&cv_dummygpcup); - - 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 = IT_DISABLED; -#endif - -#ifndef NONET - CV_RegisterVar(&cv_serversort); -#endif -} - -void M_InitCharacterTables(void) -{ - UINT8 i; - - // Setup PlayerMenu table - for (i = 0; i < MAXSKINS; i++) - { - PlayerMenu[i].status = (i < 4 ? IT_CALL : IT_DISABLED); - PlayerMenu[i].patch = PlayerMenu[i].text = NULL; - PlayerMenu[i].itemaction = M_ChoosePlayer; - PlayerMenu[i].alphaKey = 0; - } - - // Setup description table - for (i = 0; i < MAXSKINS; i++) - { - if (i == 0) - { - strcpy(description[i].notes, "\x82Sonic\x80 is the fastest of the three, but also the hardest to control. Beginners beware, but experts will find Sonic very powerful.\n\n\x82""Ability:\x80 Speed Thok\nDouble jump to zoom forward with a huge burst of speed.\n\n\x82Tip:\x80 Simply letting go of forward does not slow down in SRB2. To slow down, hold the opposite direction."); - strcpy(description[i].picname, ""); - strcpy(description[i].skinname, "sonic"); - } - else if (i == 1) - { - strcpy(description[i].notes, "\x82Tails\x80 is the most mobile of the three, but has the slowest speed. Because of his mobility, he's well-\nsuited to beginners.\n\n\x82""Ability:\x80 Fly\nDouble jump to start flying for a limited time. Repetitively hit the jump button to ascend.\n\n\x82Tip:\x80 To quickly descend while flying, hit the spin button."); - strcpy(description[i].picname, ""); - strcpy(description[i].skinname, "tails"); - } - else if (i == 2) - { - strcpy(description[i].notes, "\x82Knuckles\x80 is well-\nrounded and can destroy breakable walls simply by touching them, but he can't jump as high as the other two.\n\n\x82""Ability:\x80 Glide & Climb\nDouble jump to glide in the air as long as jump is held. Glide into a wall to climb it.\n\n\x82Tip:\x80 Press spin while climbing to jump off the wall; press jump instead to jump off\nand face away from\nthe wall."); - strcpy(description[i].picname, ""); - strcpy(description[i].skinname, "knuckles"); - } - else if (i == 3) - { - strcpy(description[i].notes, "\x82Sonic & Tails\x80 team up to take on Dr. Eggman!\nControl Sonic while Tails desperately struggles to keep up.\n\nPlayer 2 can control Tails directly by setting the controls in the options menu.\nTails's directional controls are relative to Player 1's camera.\n\nTails can pick up Sonic while flying and carry him around."); - strcpy(description[i].picname, "CHRS&T"); - strcpy(description[i].skinname, "sonic&tails"); - } - else - { - strcpy(description[i].notes, "???"); - strcpy(description[i].picname, ""); - strcpy(description[i].skinname, ""); - } - } -} - -// ========================================================================== -// 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, 159); - //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; - 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_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; - case IT_TRANSTEXT: - if (currentMenu->menuitems[i].alphaKey) - y = currentMenu->y+currentMenu->menuitems[i].alphaKey; - /* FALLTHRU */ - case IT_TRANSTEXT2: - V_DrawCenteredString(x, y, V_TRANSLUCENT, 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(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; -} - -// -// M_PrepareCupList -// -static void M_PrepareCupList(void) -{ - cupheader_t *cup = kartcupheaders; - INT32 i = 0; - - memset(dummygpcup_cons_t, 0, sizeof (dummygpcup_cons_t)); - - while (cup != NULL) - { - dummygpcup_cons_t[i].strvalue = cup->name; - dummygpcup_cons_t[i].value = i+1; - // this will probably crash or do something stupid at over 50 cups, - // but this is all behavior that gets completely overwritten in new-menus, so I'm not worried - i++; - cup = cup->next; - } - - for (; i < 50; i++) - { - dummygpcup_cons_t[i].strvalue = NULL; - dummygpcup_cons_t[i].value = 0; - } - - CV_SetValue(&cv_dummygpcup, 1); // This causes crash sometimes?! -} - -// 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_TIMEATTACK: - case LLM_BREAKTHECAPSULES: - /*if (!(mapheaderinfo[mapnum]->menuflags & LF2_RECORDATTACK)) - return false;*/ - - if ((levellistmode == LLM_TIMEATTACK && !(mapheaderinfo[mapnum]->typeoflevel & TOL_RACE)) - || (levellistmode == LLM_BREAKTHECAPSULES && !(mapheaderinfo[mapnum]->typeoflevel & TOL_MATCH))) - 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; - 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 = ((demo.playback) ? &PlaybackMenuDef : &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) -{ - patch_t *patch = W_CachePatchName(currentMenu->menuitems[itemOn].text,PU_CACHE); - if (patch->width <= BASEVIDWIDTH) - 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 addons!" -#define LOCATIONSTRING2 "Visit \x88SRB2.ORG/MODS\x80 to get & make addons!" - -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, 0); - V_DrawFill(x + width, vpadding, 1, h, 0); - V_DrawFill(x - 1, vpadding-1, width+2, 1, 0); - V_DrawFill(x - 1, vpadding+h, width+2, 1, 0); - - // 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 addons reached.\nA file could not be loaded.\nIf you wish to play with this addon, 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 addons.\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 - { - y = FixedDiv(((ssize_t)(numwadfiles) - (ssize_t)(mainwads+1))< 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)[0]; - - 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, 159); - - // 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 replayScrollTitle = 0; -static SINT8 replayScrollDelay = TICRATE, replayScrollDir = 1; - -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_UP) - { - demolist[i].type = MD_SUBDIR; - sprintf(demolist[i].title, "UP"); - } - else 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, "....."); - } - } -} - -void M_ReplayHut(INT32 choice) -{ - (void)choice; - - if (!demo.inreplayhut) - { - snprintf(menupath, 1024, "%s"PATHSEP"media"PATHSEP"replay"PATHSEP"online"PATHSEP, srb2home); - menupathindex[(menudepthleft = menudepth-1)] = strlen(menupath); - } - if (!preparefilemenu(false, true)) - { - M_StartMessage("No replays found.\n\n(Press a key)\n", NULL, MM_NOTHING); - return; - } - else if (!demo.inreplayhut) - dir_on[menudepthleft] = 0; - demo.inreplayhut = true; - - replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; - - PrepReplayList(); - - menuactive = true; - M_SetupNextMenu(&MISC_ReplayHutDef); - G_SetGamestate(GS_TIMEATTACK); - - demo.rewinding = false; - CL_ClearRewinds(); - - S_ChangeMusicInternal("replst", true); -} - -static void M_HandleReplayHutList(INT32 choice) -{ - switch (choice) - { - case KEY_UPARROW: - if (dir_on[menudepthleft]) - dir_on[menudepthleft]--; - else - return; - //M_PrevOpt(); - - S_StartSound(NULL, sfx_menu1); - replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; - break; - - case KEY_DOWNARROW: - if (dir_on[menudepthleft] < sizedirmenu-1) - dir_on[menudepthleft]++; - else - return; - //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: - 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, true)) - { - 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, true)) - { - M_QuitReplayHut(); - return; - } - } - else - { - S_StartSound(NULL, sfx_menu1); - dir_on[menudepthleft] = 1; - PrepReplayList(); - } - } - 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, true)) - { - M_QuitReplayHut(); - return; - } - PrepReplayList(); - break; - default: - // We can't just use M_SetupNextMenu because that'll run ReplayDef's quitroutine and boot us back to the title screen! - currentMenu->lastOn = itemOn; - currentMenu = &MISC_ReplayStartDef; - - replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; - - switch (demolist[dir_on[menudepthleft]].addonstatus) - { - case DFILE_ERROR_CANNOTLOAD: - // Only show "Watch Replay Without Addons" - MISC_ReplayStartMenu[0].status = IT_DISABLED; - MISC_ReplayStartMenu[1].status = IT_CALL|IT_STRING; - //MISC_ReplayStartMenu[1].alphaKey = 0; - MISC_ReplayStartMenu[2].status = IT_DISABLED; - itemOn = 1; - break; - - case DFILE_ERROR_NOTLOADED: - case DFILE_ERROR_INCOMPLETEOUTOFORDER: - // Show "Load Addons and Watch Replay" and "Watch Replay Without Addons" - MISC_ReplayStartMenu[0].status = IT_CALL|IT_STRING; - MISC_ReplayStartMenu[1].status = IT_CALL|IT_STRING; - //MISC_ReplayStartMenu[1].alphaKey = 10; - MISC_ReplayStartMenu[2].status = IT_DISABLED; - itemOn = 0; - break; - - case DFILE_ERROR_EXTRAFILES: - case DFILE_ERROR_OUTOFORDER: - default: - // Show "Watch Replay" - MISC_ReplayStartMenu[0].status = IT_DISABLED; - MISC_ReplayStartMenu[1].status = IT_DISABLED; - MISC_ReplayStartMenu[2].status = IT_CALL|IT_STRING; - //MISC_ReplayStartMenu[2].alphaKey = 0; - itemOn = 2; - break; - } - } - - break; - } -} - -#define SCALEDVIEWWIDTH (vid.width/vid.dupx) -#define SCALEDVIEWHEIGHT (vid.height/vid.dupy) -static void DrawReplayHutReplayInfo(void) -{ - lumpnum_t lumpnum; - patch_t *patch; - UINT8 *colormap; - INT32 x, y, w, h; - - switch (demolist[dir_on[menudepthleft]].type) - { - case MD_NOTLOADED: - V_DrawCenteredString(160, 40, V_SNAPTOTOP, "Loading replay information..."); - break; - - case MD_INVALID: - V_DrawCenteredString(160, 40, V_SNAPTOTOP|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, 64, V_SNAPTOTOP|V_ALLOWLOWERCASE|V_TRANSLUCENT|highlightflags, "Recorded on an outdated version."); - /*fallthru*/ - default: - // Draw level stuff - x = 15; y = 15; - - // A 160x100 image of the level as entry MAPxxP - //CONS_Printf("%d %s\n", demolist[dir_on[menudepthleft]].map, G_BuildMapName(demolist[dir_on[menudepthleft]].map)); - lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(demolist[dir_on[menudepthleft]].map))); - if (lumpnum != LUMPERROR) - patch = W_CachePatchNum(lumpnum, PU_CACHE); - else - patch = W_CachePatchName("M_NOLVL", PU_CACHE); - - if (!(demolist[dir_on[menudepthleft]].kartspeed & DF_ENCORE)) - V_DrawSmallScaledPatch(x, y, V_SNAPTOTOP, patch); - else - { - w = SHORT(patch->width); - h = SHORT(patch->height); - V_DrawSmallScaledPatch(x+(w>>1), y, V_SNAPTOTOP|V_FLIP, patch); - - { - static angle_t rubyfloattime = 0; - const fixed_t rubyheight = FINESINE(rubyfloattime>>ANGLETOFINESHIFT); - V_DrawFixedPatch((x+(w>>2))<>2))<width), y+20, V_SNAPTOTOP, patch, colormap); - - 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)); - - if (cv_vhseffect.value) - V_DrawVhsEffect(false); - - // Draw menu choices - x = currentMenu->x; - y = currentMenu->y; - - if (itemOn > replaylistitem) - { - itemOn = replaylistitem; - dir_on[menudepthleft] = sizedirmenu-1; - replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; - } - else if (itemOn < replaylistitem) - { - dir_on[menudepthleft] = 0; - replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; - } - - if (itemOn == replaylistitem) - { - INT32 maxy; - // Scroll menu items if needed - cursory = y + currentMenu->menuitems[replaylistitem].alphaKey + dir_on[menudepthleft]*10; - maxy = y + currentMenu->menuitems[replaylistitem].alphaKey + sizedirmenu*10; - - if (cursory > maxy - 20) - cursory = maxy - 20; - - if (cursory - replayhutmenuy > SCALEDVIEWHEIGHT-50) - replayhutmenuy += (cursory-SCALEDVIEWHEIGHT-replayhutmenuy + 51)/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++) - { - INT32 localy = y + currentMenu->menuitems[i].alphaKey; - - if (localy < 65) - continue; - - if (i == itemOn) - cursory = localy; - - if ((currentMenu->menuitems[i].status & IT_DISPLAY)==IT_STRING) - V_DrawString(x, localy, V_SNAPTOTOP|V_SNAPTOLEFT, currentMenu->menuitems[i].text); - else - V_DrawString(x, localy, V_SNAPTOTOP|V_SNAPTOLEFT|highlightflags, currentMenu->menuitems[i].text); - } - - y += currentMenu->menuitems[replaylistitem].alphaKey; - - for (i = 0; i < (INT16)sizedirmenu; i++) - { - INT32 localy = y+i*10; - INT32 localx = x; - - if (localy < 65) - continue; - if (localy >= SCALEDVIEWHEIGHT) - break; - - if (demolist[i].type == MD_NOTLOADED && !processed_one_this_frame) - { - processed_one_this_frame = true; - G_LoadDemoInfo(&demolist[i]); - } - - if (demolist[i].type == MD_SUBDIR) - { - localx += 8; - V_DrawScaledPatch(x - 4, localy, V_SNAPTOTOP|V_SNAPTOLEFT, W_CachePatchName(dirmenu[i][DIR_TYPE] == EXT_UP ? "M_RBACK" : "M_RFLDR", PU_CACHE)); - } - - if (itemOn == replaylistitem && i == (INT16)dir_on[menudepthleft]) - { - cursory = localy; - - if (replayScrollDelay) - replayScrollDelay--; - else if (replayScrollDir > 0) - { - if (replayScrollTitle < (V_StringWidth(demolist[i].title, 0) - (SCALEDVIEWWIDTH - (x<<1)))<<1) - replayScrollTitle++; - else - { - replayScrollDelay = TICRATE; - replayScrollDir = -1; - } - } - else - { - if (replayScrollTitle > 0) - replayScrollTitle--; - else - { - replayScrollDelay = TICRATE; - replayScrollDir = 1; - } - } - - V_DrawString(localx - (replayScrollTitle>>1), localy, V_SNAPTOTOP|V_SNAPTOLEFT|highlightflags|V_ALLOWLOWERCASE, demolist[i].title); - } - else - V_DrawString(localx, localy, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, demolist[i].title); - } - - // Draw scrollbar - y = sizedirmenu*10 + currentMenu->menuitems[replaylistitem].alphaKey + 30; - if (y > SCALEDVIEWHEIGHT-80) - { - V_DrawFill(BASEVIDWIDTH-4, 75, 4, SCALEDVIEWHEIGHT-80, V_SNAPTOTOP|V_SNAPTORIGHT|159); - V_DrawFill(BASEVIDWIDTH-3, 76 + (SCALEDVIEWHEIGHT-80) * replayhutmenuy / y, 2, (((SCALEDVIEWHEIGHT-80) * (SCALEDVIEWHEIGHT-80))-1) / y - 1, V_SNAPTOTOP|V_SNAPTORIGHT|149); - } - - // Draw the cursor - V_DrawScaledPatch(currentMenu->x - 24, cursory, V_SNAPTOTOP|V_SNAPTOLEFT, - W_CachePatchName("M_CURSOR", PU_CACHE)); - V_DrawString(currentMenu->x, cursory, V_SNAPTOTOP|V_SNAPTOLEFT|highlightflags, currentMenu->menuitems[itemOn].text); - - // Now draw some replay info! - V_DrawFill(10, 10, 300, 60, V_SNAPTOTOP|159); - - if (itemOn == replaylistitem) - { - DrawReplayHutReplayInfo(); - } -} - -static void M_DrawReplayStartMenu(void) -{ - const char *warning; - UINT8 i; - - M_DrawGenericBackgroundMenu(); - -#define STARTY 62-(replayScrollTitle>>1) - // Draw rankings beyond first - for (i = 1; i < MAXPLAYERS && demolist[dir_on[menudepthleft]].standings[i].ranking; i++) - { - patch_t *patch; - UINT8 *colormap; - - V_DrawRightAlignedString(BASEVIDWIDTH-100, STARTY + i*20, V_SNAPTOTOP|highlightflags, va("%2d", demolist[dir_on[menudepthleft]].standings[i].ranking)); - V_DrawThinString(BASEVIDWIDTH-96, STARTY + i*20, V_SNAPTOTOP|V_ALLOWLOWERCASE, demolist[dir_on[menudepthleft]].standings[i].name); - - if (demolist[dir_on[menudepthleft]].standings[i].timeorscore == UINT32_MAX-1) - V_DrawThinString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, "NO CONTEST"); - else if (demolist[dir_on[menudepthleft]].gametype == GT_RACE) - V_DrawRightAlignedString(BASEVIDWIDTH-40, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d'%02d\"%02d", - G_TicsToMinutes(demolist[dir_on[menudepthleft]].standings[i].timeorscore, true), - G_TicsToSeconds(demolist[dir_on[menudepthleft]].standings[i].timeorscore), - G_TicsToCentiseconds(demolist[dir_on[menudepthleft]].standings[i].timeorscore) - )); - else - V_DrawString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d", demolist[dir_on[menudepthleft]].standings[i].timeorscore)); - - // Character face! - if (W_CheckNumForName(skins[demolist[dir_on[menudepthleft]].standings[i].skin].facerank) != LUMPERROR) - { - patch = facerankprefix[demolist[dir_on[menudepthleft]].standings[i].skin]; - colormap = R_GetTranslationColormap( - demolist[dir_on[menudepthleft]].standings[i].skin, - demolist[dir_on[menudepthleft]].standings[i].color, - GTC_MENUCACHE); - } - else - { - patch = W_CachePatchName("M_NORANK", PU_CACHE); - colormap = R_GetTranslationColormap( - TC_RAINBOW, - demolist[dir_on[menudepthleft]].standings[i].color, - GTC_MENUCACHE); - } - - V_DrawMappedPatch(BASEVIDWIDTH-5 - SHORT(patch->width), STARTY + i*20, V_SNAPTOTOP, patch, colormap); - } -#undef STARTY - - // Handle scrolling rankings - if (replayScrollDelay) - replayScrollDelay--; - else if (replayScrollDir > 0) - { - if (replayScrollTitle < (i*20 - SCALEDVIEWHEIGHT + 100)<<1) - replayScrollTitle++; - else - { - replayScrollDelay = TICRATE; - replayScrollDir = -1; - } - } - else - { - if (replayScrollTitle > 0) - replayScrollTitle--; - else - { - replayScrollDelay = TICRATE; - replayScrollDir = 1; - } - } - - V_DrawFill(10, 10, 300, 60, V_SNAPTOTOP|159); - DrawReplayHutReplayInfo(); - - V_DrawString(10, 72, V_SNAPTOTOP|highlightflags|V_ALLOWLOWERCASE, demolist[dir_on[menudepthleft]].title); - - // Draw a warning prompt if needed - switch (demolist[dir_on[menudepthleft]].addonstatus) - { - case DFILE_ERROR_CANNOTLOAD: - warning = "Some addons in this replay cannot be loaded.\nYou can watch anyway, but desyncs may occur."; - break; - - case DFILE_ERROR_NOTLOADED: - case DFILE_ERROR_INCOMPLETEOUTOFORDER: - warning = "Loading addons will mark your game as modified, and Record Attack may be unavailable.\nYou can watch without loading addons, but desyncs may occur."; - break; - - case DFILE_ERROR_EXTRAFILES: - warning = "You have addons loaded that were not present in this replay.\nYou can watch anyway, but desyncs may occur."; - break; - - case DFILE_ERROR_OUTOFORDER: - warning = "You have this replay's addons loaded, but they are out of order.\nYou can watch anyway, but desyncs may occur."; - break; - - default: - return; - } - - V_DrawSmallString(4, BASEVIDHEIGHT-14, V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, warning); -} - -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; - - demo.inreplayhut = false; - - return true; -} - -static void M_HutStartReplay(INT32 choice) -{ - (void)choice; - - M_ClearMenus(false); - demo.loadfiles = (itemOn == 0); - demo.ignorefiles = (itemOn != 0); - - G_DoPlayDemo(demolist[dir_on[menudepthleft]].filepath); -} - -void M_SetPlaybackMenuPointer(void) -{ - itemOn = playback_pause; -} - -static void M_DrawPlaybackMenu(void) -{ - INT16 i; - patch_t *icon; - UINT8 *activemap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GOLD, GTC_MENUCACHE); - UINT32 transmap = max(0, (INT32)(leveltime - playback_last_menu_interaction_leveltime - 4*TICRATE)) / 5; - transmap = min(8, transmap) << V_ALPHASHIFT; - - if (leveltime - playback_last_menu_interaction_leveltime >= 6*TICRATE) - playback_last_menu_interaction_leveltime = leveltime - 6*TICRATE; - - // Toggle items - if (paused && !demo.rewinding) - { - PlaybackMenu[playback_pause].status = PlaybackMenu[playback_fastforward].status = PlaybackMenu[playback_rewind].status = IT_DISABLED; - PlaybackMenu[playback_resume].status = PlaybackMenu[playback_advanceframe].status = PlaybackMenu[playback_backframe].status = IT_CALL|IT_STRING; - - if (itemOn >= playback_rewind && itemOn <= playback_fastforward) - itemOn += playback_backframe - playback_rewind; - } - else - { - PlaybackMenu[playback_pause].status = PlaybackMenu[playback_fastforward].status = PlaybackMenu[playback_rewind].status = IT_CALL|IT_STRING; - PlaybackMenu[playback_resume].status = PlaybackMenu[playback_advanceframe].status = PlaybackMenu[playback_backframe].status = IT_DISABLED; - - if (itemOn >= playback_backframe && itemOn <= playback_advanceframe) - itemOn -= playback_backframe - playback_rewind; - } - - if (modeattacking) - { - for (i = playback_viewcount; i <= playback_view4; i++) - PlaybackMenu[i].status = IT_DISABLED; - PlaybackMenu[playback_freecamera].alphaKey = 72; - PlaybackMenu[playback_quit].alphaKey = 88; - - currentMenu->x = BASEVIDWIDTH/2 - 52; - } - else - { - PlaybackMenu[playback_viewcount].status = IT_ARROWS|IT_STRING; - - for (i = 0; i <= r_splitscreen; i++) - PlaybackMenu[playback_view1+i].status = IT_ARROWS|IT_STRING; - for (i = r_splitscreen+1; i < 4; i++) - PlaybackMenu[playback_view1+i].status = IT_DISABLED; - - PlaybackMenu[playback_freecamera].alphaKey = 156; - PlaybackMenu[playback_quit].alphaKey = 172; - currentMenu->x = BASEVIDWIDTH/2 - 88; - } - - // wip - //M_DrawTextBox(currentMenu->x-68, currentMenu->y-7, 15, 15); - //M_DrawCenteredMenu(); - - for (i = 0; i < currentMenu->numitems; i++) - { - UINT8 *inactivemap = NULL; - - if (i >= playback_view1 && i <= playback_view4) - { - if (modeattacking) continue; - - if (r_splitscreen >= i - playback_view1) - { - INT32 ply = displayplayers[i - playback_view1]; - - icon = facerankprefix[players[ply].skin]; - if (i != itemOn) - inactivemap = R_GetTranslationColormap(players[ply].skin, players[ply].skincolor, GTC_MENUCACHE); - } - else if (currentMenu->menuitems[i].patch && W_CheckNumForName(currentMenu->menuitems[i].patch) != LUMPERROR) - icon = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE); - else - icon = W_CachePatchName("PLAYRANK", PU_CACHE); // temp - } - else if (currentMenu->menuitems[i].status == IT_DISABLED) - continue; - else if (currentMenu->menuitems[i].patch && W_CheckNumForName(currentMenu->menuitems[i].patch) != LUMPERROR) - icon = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE); - else - icon = W_CachePatchName("PLAYRANK", PU_CACHE); // temp - - if ((i == playback_fastforward && cv_playbackspeed.value > 1) || (i == playback_rewind && demo.rewinding)) - V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].alphaKey, currentMenu->y, transmap|V_SNAPTOTOP, icon, R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_JAWZ, GTC_MENUCACHE)); - else - V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].alphaKey, currentMenu->y, transmap|V_SNAPTOTOP, icon, (i == itemOn) ? activemap : inactivemap); - - if (i == itemOn) - { - V_DrawCharacter(currentMenu->x + currentMenu->menuitems[i].alphaKey + 4, currentMenu->y + 14, - '\x1A' | transmap|V_SNAPTOTOP|highlightflags, false); - - V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y + 18, transmap|V_SNAPTOTOP|V_ALLOWLOWERCASE, currentMenu->menuitems[i].text); - - if ((currentMenu->menuitems[i].status & IT_TYPE) == IT_ARROWS) - { - char *str; - - if (!(i == playback_viewcount && r_splitscreen == 3)) - V_DrawCharacter(BASEVIDWIDTH/2 - 4, currentMenu->y + 28 - (skullAnimCounter/5), - '\x1A' | transmap|V_SNAPTOTOP|highlightflags, false); // up arrow - - if (!(i == playback_viewcount && r_splitscreen == 0)) - V_DrawCharacter(BASEVIDWIDTH/2 - 4, currentMenu->y + 48 + (skullAnimCounter/5), - '\x1B' | transmap|V_SNAPTOTOP|highlightflags, false); // down arrow - - switch (i) - { - case playback_viewcount: - str = va("%d", r_splitscreen+1); - break; - - case playback_view1: - case playback_view2: - case playback_view3: - case playback_view4: - str = player_names[displayplayers[i - playback_view1]]; // 0 to 3 - break; - - default: // shouldn't ever be reached but whatever - continue; - } - - V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y + 38, transmap|V_SNAPTOTOP|V_ALLOWLOWERCASE|highlightflags, str); - } - } - } -} - -static void M_PlaybackRewind(INT32 choice) -{ - static tic_t lastconfirmtime; - - (void)choice; - - if (!demo.rewinding) - { - if (paused) - { - G_ConfirmRewind(leveltime-1); - paused = true; - S_PauseAudio(); - } - else - demo.rewinding = paused = true; - } - else if (lastconfirmtime + TICRATE/2 < I_GetTime()) - { - lastconfirmtime = I_GetTime(); - G_ConfirmRewind(leveltime); - } - - CV_SetValue(&cv_playbackspeed, 1); -} - -static void M_PlaybackPause(INT32 choice) -{ - (void)choice; - - paused = !paused; - - if (demo.rewinding) - { - G_ConfirmRewind(leveltime); - paused = true; - S_PauseAudio(); - } - else if (paused) - S_PauseAudio(); - else - S_ResumeAudio(); - - CV_SetValue(&cv_playbackspeed, 1); -} - -static void M_PlaybackFastForward(INT32 choice) -{ - (void)choice; - - if (demo.rewinding) - { - G_ConfirmRewind(leveltime); - paused = false; - S_ResumeAudio(); - } - CV_SetValue(&cv_playbackspeed, cv_playbackspeed.value == 1 ? 4 : 1); -} - -static void M_PlaybackAdvance(INT32 choice) -{ - (void)choice; - - paused = false; - TryRunTics(1); - paused = true; -} - - -static void M_PlaybackSetViews(INT32 choice) -{ - - if (demo.freecam) - return; // not here. - - if (choice > 0) - { - if (r_splitscreen < 3) - G_AdjustView(r_splitscreen + 2, 0, true); - } - else if (r_splitscreen) - { - r_splitscreen--; - R_ExecuteSetViewSize(); - } -} - -static void M_PlaybackAdjustView(INT32 choice) -{ - G_AdjustView(itemOn - playback_viewcount, (choice > 0) ? 1 : -1, true); -} - -// this one's rather tricky -static void M_PlaybackToggleFreecam(INT32 choice) -{ - (void)choice; - M_ClearMenus(true); - - // remove splitscreen: - splitscreen = 0; - R_ExecuteSetViewSize(); - - P_InitCameraCmd(); // init camera controls - if (!demo.freecam) // toggle on - { - demo.freecam = true; - democam.cam = &camera[0]; // this is rather useful - } - else // toggle off - { - demo.freecam = false; - // reset democam vars: - democam.cam = NULL; - democam.turnheld = false; - democam.keyboardlook = false; // reset only these. localangle / aiming gets set before the cam does anything anyway - } -} - - -static void M_PlaybackQuit(INT32 choice) -{ - (void)choice; - G_StopDemo(); - - if (demo.inreplayhut) - M_ReplayHut(choice); - else if (modeattacking) - { - M_EndModeAttackRun(); - S_ChangeMusicInternal("racent", true); - } - else - D_StartTitle(); -} - -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[4].status = OP_MainMenu[5].status = (Playing() && !(server || IsPlayerAdmin(consoleplayer))) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); - - OP_MainMenu[8].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL); // Play credits - OP_DataOptionsMenu[3].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); // Erase data - - 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_POWERLEVEL: - return va("Reach power level %d in %s", cond.requirement, (cond.extrainfo1 == PWRLV_BATTLE ? "Battle" : "Race")); - 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 -// ================== - -static void M_SinglePlayerMenu(INT32 choice) -{ - (void)choice; - - SP_MainMenu[spgrandprix].status = IT_CALL|IT_STRING; - SP_MainMenu[sptimeattack].status = - (M_SecretUnlocked(SECRET_TIMEATTACK)) ? IT_CALL|IT_STRING : IT_SECRET; - SP_MainMenu[spbreakthecapsules].status = - (M_SecretUnlocked(SECRET_BREAKTHECAPSULES)) ? IT_CALL|IT_STRING : IT_SECRET; - - M_SetupNextMenu(&SP_MainDef); -} - -/*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 = 88, 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)); - - V_DrawString(20, 52, highlightflags, "Online Power Level:"); - V_DrawRightAlignedString(BASEVIDWIDTH-16, 52, 0, va("Race: %i", vspowerlevel[PWRLV_RACE])); - V_DrawRightAlignedString(BASEVIDWIDTH-16, 60, 0, va("Battle: %i", vspowerlevel[PWRLV_BATTLE])); - - 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, 70, 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, 70, (mapsunfinished ? warningflags : 0), beststr); - - if (mapsunfinished) - V_DrawRightAlignedString(BASEVIDWIDTH-16, 78, warningflags, va("(%d unfinished)", mapsunfinished)); - else - V_DrawRightAlignedString(BASEVIDWIDTH-16, 78, recommendedflags, "(complete)"); - - V_DrawString(32, 78, V_ALLOWLOWERCASE, va("x %d/%d", M_CountEmblems(), numemblems+numextraemblems)); - V_DrawSmallScaledPatch(20, 78, 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); - } -} - -static void M_GrandPrixTemp(INT32 choice) -{ - (void)choice; - M_PatchSkinNameTable(); - M_PrepareCupList(); - M_SetupNextMenu(&SP_GrandPrixTempDef); -} - -// Start Grand Prix! -static void M_StartGrandPrix(INT32 choice) -{ - cupheader_t *gpcup = kartcupheaders; - - (void)choice; - - if (gpcup == NULL) - { - // welp - I_Error("No cup definitions for GP\n"); - return; - } - - M_ClearMenus(true); - - memset(&grandprixinfo, 0, sizeof(struct grandprixinfo)); - - switch (cv_dummygpdifficulty.value) - { - case 0: - grandprixinfo.gamespeed = KARTSPEED_EASY; - break; - case 1: - default: - grandprixinfo.gamespeed = KARTSPEED_NORMAL; - break; - case 2: - grandprixinfo.gamespeed = KARTSPEED_HARD; - break; - case 3: - grandprixinfo.gamespeed = KARTSPEED_HARD; - grandprixinfo.masterbots = true; - break; - - } - - grandprixinfo.encore = (boolean)(cv_dummygpencore.value); - - while (gpcup != NULL && gpcup->id != cv_dummygpcup.value-1) - { - gpcup = gpcup->next; - } - - if (gpcup == NULL) - { - gpcup = kartcupheaders; - } - - grandprixinfo.cup = gpcup; - - grandprixinfo.gp = true; - grandprixinfo.roundnum = 1; - grandprixinfo.wonround = false; - - grandprixinfo.initalize = true; - - G_DeferedInitNew( - false, - G_BuildMapName(grandprixinfo.cup->levellist[0] + 1), - (UINT8)(cv_chooseskin.value - 1), - (UINT8)(cv_splitplayers.value - 1), - false - ); -} - -// =========== -// 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, 159); - - if (levellistmode != LLM_BREAKTHECAPSULES) - { - 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_TIMEATTACK; // Don't be dependent on cv_newgametype - - if (M_CountLevelsToShowInList() == 0) - { - M_StartMessage(M_GetText("No levels found for Time Attack.\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); -} - -// Same as above, but sets a different levellistmode. Should probably be merged... -static void M_BreakTheCapsules(INT32 choice) -{ - (void)choice; - - memset(skins_cons_t, 0, sizeof (skins_cons_t)); - - levellistmode = LLM_BREAKTHECAPSULES; // Don't be dependent on cv_newgametype - - if (M_CountLevelsToShowInList() == 0) - { - M_StartMessage(M_GetText("No levels found for Break the Capsules.\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_BreakTheCapsules(INT32 choice) -{ - (void)choice; - - memset(skins_cons_t, 0, sizeof (skins_cons_t)); - - levellistmode = LLM_BREAKTHECAPSULES; // 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_CAPSULES; - - 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("media")+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1; - char nameofdemo[256]; - (void)choice; - emeralds = 0; - M_ClearMenus(true); - modeattacking = (levellistmode == LLM_BREAKTHECAPSULES ? ATTACKING_CAPSULES : ATTACKING_RECORD); - - gpath = va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s", - srb2home, timeattackfolder); - M_MkdirEach(gpath, M_PathParts(gpath) - 3, 0755); - - if ((gpath = malloc(glen)) == NULL) - I_Error("Out of memory for replay filepath\n"); - - sprintf(gpath,"media"PATHSEP"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 = (levellistmode == LLM_BREAKTHECAPSULES ? ATTACKING_CAPSULES : 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 = (levellistmode == LLM_BREAKTHECAPSULES ? ATTACKING_CAPSULES : 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); -} - -void M_ModeAttackRetry(INT32 choice) -{ - (void)choice; - G_CheckDemoStatus(); // Cancel recording - if (modeattacking) - M_ChooseTimeAttack(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(); - - if (modeattacking) - currentMenu = &SP_TimeAttackDef; - - 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; - const char *gt = "Unknown"; - const char *spd = ""; - const char *pwr = "----"; - 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); -} - -#ifndef TESTERS -static void M_StartOfflineServerMenu(INT32 choice) -{ - (void)choice; - levellistmode = LLM_CREATESERVER; - M_PrepareLevelSelect(); - M_SetupNextMenu(&MP_OfflineServerDef); -} -#endif - -#ifndef NONET -#ifndef TESTERS -static void M_StartServerMenu(INT32 choice) -{ - (void)choice; - levellistmode = LLM_CREATESERVER; - M_PrepareLevelSelect(); - ms_RoomId = -1; - M_SetupNextMenu(&MP_ServerDef); - -} -#endif - -// ============== -// 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 NOMENUHOST -#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 - -#ifndef TESTERS - V_DrawRightAlignedString(BASEVIDWIDTH-x, y+MP_MainMenu[5].alphaKey, - ((itemOn == 5) ? highlightflags : 0), - "(2-4 players)" - ); -#endif - -#ifndef NONET - y += MP_MainMenu[8].alphaKey; - - V_DrawFill(x+5, y+4+5, /*16*8 + 6,*/ BASEVIDWIDTH - 2*(x+5), 8+6, 159); - - // 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, 159); - - // 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[g_localplayers[1]]; - 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(g_localplayers[1])) - 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[g_localplayers[2]]; - 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(g_localplayers[2])) - 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[g_localplayers[3]]; - 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(g_localplayers[3])) - 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) -{ - UINT8 i; - - 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; - for (i = 0; i < PWRLV_NUMTYPES; i++) - vspowerlevel[i] = PWRLVRECORD_START; - 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 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 = KartItemCVars[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 = KartItemCVars[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 (KartItemCVars[i]->value == v) - CV_AddValue(KartItemCVars[i], 1); - } - } - else - { - S_StartSound(NULL, sfx_s1ba); - CV_AddValue(KartItemCVars[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 -// ===================================================================== - -// ===================== -// 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"); -} -#endif From 6c0b30ad40572ec81046087fdb1a238739c69b89 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 9 Jun 2020 16:49:39 -0400 Subject: [PATCH 018/379] Use SV_StartSinglePlayerServer for GP instead of G_DeferedInitNew multiplayer should always = true in kart anyway (should make a branch to remove the variable tbh) --- src/k_menufunc.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 9e706fbed..e534c5bca 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2176,14 +2176,14 @@ void M_CupSelectHandler(INT32 choice) if (cupgrid.grandprix == true) { + S_StartSound(NULL, sfx_s3k63); + // Early fadeout to let the sound finish playing F_WipeStartScreen(); V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); F_WipeEndScreen(); F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); - M_ClearMenus(true); - memset(&grandprixinfo, 0, sizeof(struct grandprixinfo)); // TODO: game settings screen @@ -2197,13 +2197,20 @@ void M_CupSelectHandler(INT32 choice) grandprixinfo.roundnum = 1; grandprixinfo.initalize = true; - G_DeferedInitNew( + paused = false; + SV_StartSinglePlayerServer(); + multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... + D_MapChange( + grandprixinfo.cup->levellist[0] + 1, + GT_RACE, + grandprixinfo.encore, + true, + 1, false, - G_BuildMapName(grandprixinfo.cup->levellist[0] + 1), - (UINT8)cv_skin.value, - (UINT8)(cv_splitplayers.value - 1), false ); + + M_ClearMenus(true); } else { @@ -2218,9 +2225,8 @@ void M_CupSelectHandler(INT32 choice) levellist.y = levellist.dest; M_SetupNextMenu(&PLAY_LevelSelectDef, false); + S_StartSound(NULL, sfx_s3k63); } - - S_StartSound(NULL, sfx_s3k63); break; case KEY_ESCAPE: if (currentMenu->prevMenu) From ef9e6081c981d0b6fa3f6a13d7b3062805f695fa Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 9 Jun 2020 16:50:18 -0400 Subject: [PATCH 019/379] Log the server connection messages rather than print Immersion breaking when you transition from menu to gameplay! --- src/d_clisrv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index b32f19b76..0a2c87c4d 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2373,12 +2373,12 @@ static void CL_ConnectToServer(boolean viams) strncpy(connectedservername, serverlist[i].info.servername, MAXSERVERNAME); - CONS_Printf(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername); + CON_LogMessage(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername); if (num < NUMGAMETYPES) gametypestr = Gametype_Names[num]; if (gametypestr) - CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr); - CONS_Printf(M_GetText("Version: %d.%d\n"), + CON_LogMessage(M_GetText("Gametype: %s\n"), gametypestr); + CON_LogMessage(M_GetText("Version: %d.%d\n"), serverlist[i].info.version, serverlist[i].info.subversion); } SL_ClearServerList(servernode); @@ -3814,7 +3814,7 @@ boolean SV_SpawnServer(void) if (!serverrunning) { - CONS_Printf(M_GetText("Starting Server....\n")); + CON_LogMessage(M_GetText("Starting Server....\n")); serverrunning = true; SV_ResetServer(); SV_GenContext(); From 844f24203cfb4204941017ae52fd3caaf85ed148 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 9 Jun 2020 16:51:38 -0400 Subject: [PATCH 020/379] Whoops, use va to put these together! --- src/d_clisrv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 0a2c87c4d..fd02c4570 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2373,13 +2373,13 @@ static void CL_ConnectToServer(boolean viams) strncpy(connectedservername, serverlist[i].info.servername, MAXSERVERNAME); - CON_LogMessage(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername); + CON_LogMessage(va(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername)); if (num < NUMGAMETYPES) gametypestr = Gametype_Names[num]; if (gametypestr) - CON_LogMessage(M_GetText("Gametype: %s\n"), gametypestr); - CON_LogMessage(M_GetText("Version: %d.%d\n"), - serverlist[i].info.version, serverlist[i].info.subversion); + CON_LogMessage(va(M_GetText("Gametype: %s\n"), gametypestr)); + CON_LogMessage(va(M_GetText("Version: %d.%d\n"), + serverlist[i].info.version, serverlist[i].info.subversion)); } SL_ClearServerList(servernode); #endif From 2af75a2bd15a6046d1a122696c437c037044bb18 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 11 Jan 2021 00:06:34 -0500 Subject: [PATCH 021/379] Fix merge conflicts --- src/command.c | 6 +- src/d_clisrv.c | 9 +- src/d_main.c | 31 +-- src/d_netcmd.c | 2 + src/dehacked.c | 551 +-------------------------------------------- src/discord.c | 4 +- src/f_finale.c | 53 +---- src/f_finale.h | 3 - src/f_wipe.c | 4 +- src/g_demo.c | 2 +- src/g_game.c | 5 - src/http-mserv.c | 2 +- src/k_menu.h | 40 +++- src/k_menudef.c | 32 +-- src/k_menudraw.c | 191 ++++++++-------- src/k_menufunc.c | 411 ++++++++++++++++++++++++++------- src/lua_baselib.c | 3 +- src/mserv.c | 4 +- src/r_things.c | 2 +- src/sdl/i_system.c | 2 +- src/sdl/i_video.c | 2 +- src/v_video.c | 139 ------------ src/v_video.h | 6 +- src/y_inter.c | 148 +----------- src/y_inter.h | 3 - 25 files changed, 523 insertions(+), 1132 deletions(-) diff --git a/src/command.c b/src/command.c index c4a0bfbdf..6e551d24c 100644 --- a/src/command.c +++ b/src/command.c @@ -35,6 +35,7 @@ #include "lua_script.h" #include "d_netfil.h" // findfile #include "r_data.h" // Color_cons_t +#include "r_skins.h" //======== // protos. @@ -1955,6 +1956,7 @@ void CV_AddValue(consvar_t *var, INT32 increment) if (var->PossibleValue) { + /* if (var == &cv_nextmap) { // Special case for the nextmap variable, used only directly from the menu @@ -1988,9 +1990,11 @@ void CV_AddValue(consvar_t *var, INT32 increment) return; } } + else + */ #define MINVAL 0 #define MAXVAL 1 - else if (var->PossibleValue[MINVAL].strvalue && !strcmp(var->PossibleValue[MINVAL].strvalue, "MIN")) + if (var->PossibleValue[MINVAL].strvalue && !strcmp(var->PossibleValue[MINVAL].strvalue, "MIN")) { #ifdef PARANOIA if (!var->PossibleValue[MAXVAL].strvalue) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index fde3997d6..1cc781306 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2603,17 +2603,16 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic { if (cl_mode != CL_DOWNLOADFILES && cl_mode != CL_DOWNLOADSAVEGAME) { - F_MenuPresTicker(true); // title sky F_TitleScreenTicker(true); F_TitleScreenDrawer(); } CL_DrawConnectionStatus(); #ifdef HAVE_THREADS - I_lock_mutex(&m_menu_mutex); + I_lock_mutex(&k_menu_mutex); #endif M_Drawer(); //Needed for drawing messageboxes on the connection screen #ifdef HAVE_THREADS - I_unlock_mutex(m_menu_mutex); + I_unlock_mutex(k_menu_mutex); #endif I_UpdateNoVsync(); // page flip or blit buffer if (moviemode) @@ -6315,11 +6314,11 @@ void NetUpdate(void) { resptime = nowtime; #ifdef HAVE_THREADS - I_lock_mutex(&m_menu_mutex); + I_lock_mutex(&k_menu_mutex); #endif M_Ticker(); #ifdef HAVE_THREADS - I_unlock_mutex(m_menu_mutex); + I_unlock_mutex(k_menu_mutex); #endif CON_Ticker(); } diff --git a/src/d_main.c b/src/d_main.c index a7f918d2e..0ac753e23 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -204,13 +204,13 @@ void D_ProcessEvents(void) // Menu input #ifdef HAVE_THREADS - I_lock_mutex(&m_menu_mutex); + I_lock_mutex(&k_menu_mutex); #endif { eaten = M_Responder(ev); } #ifdef HAVE_THREADS - I_unlock_mutex(m_menu_mutex); + I_unlock_mutex(k_menu_mutex); #endif if (eaten) @@ -300,11 +300,6 @@ static void D_Display(void) if (vid.recalc || setrenderstillneeded) { SCR_Recalc(); // NOTE! setsizeneeded is set by SCR_Recalc() -#ifdef HWRENDER - // Shoot! The screen texture was flushed! - if ((rendermode == render_opengl) && (gamestate == GS_INTERMISSION)) - usebuffer = false; -#endif } if (rendermode == render_soft) @@ -554,9 +549,8 @@ static void D_Display(void) if (rendermode == render_soft) { VID_BlitLinearScreen(screens[0], screens[1], vid.width*vid.bpp, vid.height, vid.width*vid.bpp, vid.rowbytes); - Y_ConsiderScreenBuffer(); - usebuffer = true; } + lastdraw = false; } @@ -608,11 +602,11 @@ static void D_Display(void) vid.recalc = 0; #ifdef HAVE_THREADS - I_lock_mutex(&m_menu_mutex); + I_lock_mutex(&k_menu_mutex); #endif M_Drawer(); // menu is drawn even on top of everything #ifdef HAVE_THREADS - I_unlock_mutex(m_menu_mutex); + I_unlock_mutex(k_menu_mutex); #endif // focus lost moved to M_Drawer @@ -947,7 +941,6 @@ void D_StartTitle(void) G_SetGametype(GT_RACE); // SRB2kart paused = false; advancedemo = false; - F_InitMenuPresValues(); F_StartTitleScreen(); currentMenu = &MainDef; // reset the current menu ID @@ -1355,13 +1348,6 @@ void D_SRB2Main(void) // adapt tables to SRB2's needs, including extra slots for dehacked file support P_PatchInfoTables(); - // initiate menu metadata before SOCcing them - M_InitMenuPresTables(); - - // init title screen display params - if (M_GetUrlProtocolArg() || M_CheckParm("-connect")) - F_InitMenuPresValues(); - //---------------------------------------------------- READY TIME // we need to check for dedicated before initialization of some subsystems @@ -1372,12 +1358,6 @@ void D_SRB2Main(void) // Make backups of some SOCcable tables. P_BackupTables(); - // Setup character tables - // Have to be done here before files are loaded -#ifdef USEPLAYERMENU - M_InitCharacterTables(); -#endif - // load wad, including the main wad file CONS_Printf("W_InitMultipleFiles(): Adding IWAD and main PWADs.\n"); W_InitMultipleFiles(startupiwads, false); @@ -1824,7 +1804,6 @@ void D_SRB2Main(void) } else if (M_CheckParm("-skipintro")) { - F_InitMenuPresValues(); F_StartTitleScreen(); } else diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 712854c05..f65a796c6 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2325,10 +2325,12 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r // The supplied data are assumed to be good. I_Assert(delay >= 0 && delay <= 2); + /* if (mapnum != -1) { CV_SetValue(&cv_nextmap, mapnum); } + */ CONS_Debug(DBG_GAMELOGIC, "Map change: mapnum=%d gametype=%d pencoremode=%d resetplayers=%d delay=%d skipprecutscene=%d\n", mapnum, newgametype, pencoremode, resetplayers, delay, skipprecutscene); diff --git a/src/dehacked.c b/src/dehacked.c index fc921b2e9..6b040787e 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -76,7 +76,6 @@ static sfxenum_t get_sfx(const char *word); static UINT16 get_mus(const char *word, UINT8 dehacked_mode); #endif static hudnum_t get_huditem(const char *word); -static menutype_t get_menutype(const char *word); //static INT16 get_gametype(const char *word); //static powertype_t get_power(const char *word); skincolornum_t get_skincolor(const char *word); @@ -306,201 +305,6 @@ static void clear_levels(void) P_AllocMapHeader(gamemap-1); } -static boolean findFreeSlot(INT32 *num) -{ - // Send the character select entry to a free slot. - while (*num < MAXSKINS && (description[*num].used)) - *num = *num+1; - - // No more free slots. :( - if (*num >= MAXSKINS) - return false; - - // Redesign your logo. (See M_DrawSetupChoosePlayerMenu in m_menu.c...) - description[*num].picname[0] = '\0'; - description[*num].nametag[0] = '\0'; - description[*num].displayname[0] = '\0'; - description[*num].oppositecolor = SKINCOLOR_NONE; - description[*num].tagtextcolor = SKINCOLOR_NONE; - description[*num].tagoutlinecolor = SKINCOLOR_NONE; - - // Found one! ^_^ - return (description[*num].used = true); -} - -// Reads a player. -// For modifying the character select screen -static void readPlayer(MYFILE *f, INT32 num) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word; - char *word2; - char *displayname = ZZ_Alloc(MAXLINELEN+1); - INT32 i; - boolean slotfound = false; - - #define SLOTFOUND \ - if (!slotfound && (slotfound = findFreeSlot(&num)) == false) \ - goto done; - - displayname[MAXLINELEN] = '\0'; - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - for (i = 0; i < MAXLINELEN-3; i++) - { - char *tmp; - if (s[i] == '=') - { - tmp = &s[i+2]; - strncpy(displayname, tmp, SKINNAMESIZE); - break; - } - } - - word = strtok(s, " "); - if (word) - strupr(word); - else - break; - - if (fastcmp(word, "PLAYERTEXT")) - { - char *playertext = NULL; - - SLOTFOUND - - for (i = 0; i < MAXLINELEN-3; i++) - { - if (s[i] == '=') - { - playertext = &s[i+2]; - break; - } - } - if (playertext) - { - strcpy(description[num].notes, playertext); - strcat(description[num].notes, myhashfgets(playertext, sizeof (description[num].notes), f)); - } - else - strcpy(description[num].notes, ""); - - // For some reason, cutting the string did not work above. Most likely due to strcpy or strcat... - // It works down here, though. - { - INT32 numline = 0; - for (i = 0; (size_t)i < sizeof(description[num].notes)-1; i++) - { - if (numline < 20 && description[num].notes[i] == '\n') - numline++; - - if (numline >= 20 || description[num].notes[i] == '\0' || description[num].notes[i] == '#') - break; - } - } - description[num].notes[strlen(description[num].notes)-1] = '\0'; - description[num].notes[i] = '\0'; - continue; - } - - word2 = strtok(NULL, " = "); - if (word2) - strupr(word2); - else - break; - - if (word2[strlen(word2)-1] == '\n') - word2[strlen(word2)-1] = '\0'; - i = atoi(word2); - - if (fastcmp(word, "PICNAME")) - { - SLOTFOUND - strncpy(description[num].picname, word2, 8); - } - // new character select - else if (fastcmp(word, "DISPLAYNAME")) - { - SLOTFOUND - // replace '#' with line breaks - // (also remove any '\n') - { - char *cur = NULL; - - // remove '\n' - cur = strchr(displayname, '\n'); - if (cur) - *cur = '\0'; - - // turn '#' into '\n' - cur = strchr(displayname, '#'); - while (cur) - { - *cur = '\n'; - cur = strchr(cur, '#'); - } - } - // copy final string - strncpy(description[num].displayname, displayname, SKINNAMESIZE); - } - else if (fastcmp(word, "OPPOSITECOLOR") || fastcmp(word, "OPPOSITECOLOUR")) - { - SLOTFOUND - description[num].oppositecolor = (UINT16)get_number(word2); - } - else if (fastcmp(word, "NAMETAG") || fastcmp(word, "TAGNAME")) - { - SLOTFOUND - strncpy(description[num].nametag, word2, 8); - } - else if (fastcmp(word, "TAGTEXTCOLOR") || fastcmp(word, "TAGTEXTCOLOUR")) - { - SLOTFOUND - description[num].tagtextcolor = (UINT16)get_number(word2); - } - else if (fastcmp(word, "TAGOUTLINECOLOR") || fastcmp(word, "TAGOUTLINECOLOUR")) - { - SLOTFOUND - description[num].tagoutlinecolor = (UINT16)get_number(word2); - } - else if (fastcmp(word, "STATUS")) - { - /* - You MAY disable previous entries if you so desire... - But try to enable something that's already enabled and you will be sent to a free slot. - - Because of this, you are allowed to edit any previous entries you like, but only if you - signal that you are purposely doing so by disabling and then reenabling the slot. - */ - if (i && !slotfound && (slotfound = findFreeSlot(&num)) == false) - goto done; - - description[num].used = (!!i); - } - else if (fastcmp(word, "SKINNAME")) - { - // Send to free slot. - SLOTFOUND - strlcpy(description[num].skinname, word2, sizeof description[num].skinname); - strlwr(description[num].skinname); - } - else - deh_warning("readPlayer %d: unknown word '%s'", num, word); - } - } while (!myfeof(f)); // finish when the line is empty - #undef SLOTFOUND -done: - Z_Free(displayname); - Z_Free(s); -} -#endif - static int freeslotusage[2][2] = {{0, 0}, {0, 0}}; // [S_, MT_][max, previous .wad's max] void DEH_UpdateMaxFreeslots(void) @@ -2795,206 +2599,6 @@ static void readtextprompt(MYFILE *f, INT32 num) Z_Free(s); } -static void readmenu(MYFILE *f, INT32 num) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word = s; - char *word2; - char *tmp; - INT32 value; - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - // First remove trailing newline, if there is one - tmp = strchr(s, '\n'); - if (tmp) - *tmp = '\0'; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - // Get the part before the " = " - tmp = strchr(s, '='); - if (tmp) - *(tmp-1) = '\0'; - else - break; - strupr(word); - - // Now get the part after - word2 = (tmp += 2); - strupr(word2); - - value = atoi(word2); // used for numerical settings - - if (fastcmp(word, "BACKGROUNDNAME")) - { - strncpy(menupres[num].bgname, word2, 8); - titlechanged = true; - } - else if (fastcmp(word, "HIDEBACKGROUND")) - { - menupres[num].bghide = (boolean)(value || word2[0] == 'T' || word2[0] == 'Y'); - titlechanged = true; - } - else if (fastcmp(word, "BACKGROUNDCOLOR")) - { - menupres[num].bgcolor = get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "HIDETITLEPICS") || fastcmp(word, "HIDEPICS") || fastcmp(word, "TITLEPICSHIDE")) - { - // true by default, except MM_MAIN - menupres[num].hidetitlepics = (boolean)(value || word2[0] == 'T' || word2[0] == 'Y'); - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSMODE")) - { - if (fastcmp(word2, "USER")) - menupres[num].ttmode = TTMODE_USER; - else if (fastcmp(word2, "HIDE") || fastcmp(word2, "HIDDEN") || fastcmp(word2, "NONE")) - { - menupres[num].ttmode = TTMODE_USER; - menupres[num].ttname[0] = 0; - menupres[num].hidetitlepics = true; - } - else // if (fastcmp(word2, "OLD") || fastcmp(word2, "SSNTAILS")) - menupres[num].ttmode = TTMODE_OLD; - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSSCALE")) - { - // Don't handle Alacroix special case here; see Maincfg section. - menupres[num].ttscale = max(1, min(8, (UINT8)get_number(word2))); - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSNAME")) - { - strncpy(menupres[num].ttname, word2, 9); - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSX")) - { - menupres[num].ttx = (INT16)get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSY")) - { - menupres[num].tty = (INT16)get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSLOOP")) - { - menupres[num].ttloop = (INT16)get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSTICS")) - { - menupres[num].tttics = (UINT16)get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "TITLESCROLLSPEED") || fastcmp(word, "TITLESCROLLXSPEED") - || fastcmp(word, "SCROLLSPEED") || fastcmp(word, "SCROLLXSPEED")) - { - menupres[num].titlescrollxspeed = get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "TITLESCROLLYSPEED") || fastcmp(word, "SCROLLYSPEED")) - { - menupres[num].titlescrollyspeed = get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "MUSIC")) - { - strncpy(menupres[num].musname, word2, 7); - menupres[num].musname[6] = 0; - titlechanged = true; - } -#ifdef MUSICSLOT_COMPATIBILITY - else if (fastcmp(word, "MUSICSLOT")) - { - value = get_mus(word2, true); - if (value && value <= 1035) - snprintf(menupres[num].musname, 7, "%sM", G_BuildMapName(value)); - else if (value && value <= 1050) - strncpy(menupres[num].musname, compat_special_music_slots[value - 1036], 7); - else - menupres[num].musname[0] = 0; // becomes empty string - menupres[num].musname[6] = 0; - titlechanged = true; - } -#endif - else if (fastcmp(word, "MUSICTRACK")) - { - menupres[num].mustrack = ((UINT16)value - 1); - titlechanged = true; - } - else if (fastcmp(word, "MUSICLOOP")) - { - // true by default except MM_MAIN - menupres[num].muslooping = (value || word2[0] == 'T' || word2[0] == 'Y'); - titlechanged = true; - } - else if (fastcmp(word, "NOMUSIC")) - { - menupres[num].musstop = (value || word2[0] == 'T' || word2[0] == 'Y'); - titlechanged = true; - } - else if (fastcmp(word, "IGNOREMUSIC")) - { - menupres[num].musignore = (value || word2[0] == 'T' || word2[0] == 'Y'); - titlechanged = true; - } - else if (fastcmp(word, "FADESTRENGTH")) - { - // one-based, <= 0 means use default value. 1-32 - menupres[num].fadestrength = get_number(word2)-1; - titlechanged = true; - } - else if (fastcmp(word, "NOENTERBUBBLE")) - { - menupres[num].enterbubble = !(value || word2[0] == 'T' || word2[0] == 'Y'); - titlechanged = true; - } - else if (fastcmp(word, "NOEXITBUBBLE")) - { - menupres[num].exitbubble = !(value || word2[0] == 'T' || word2[0] == 'Y'); - titlechanged = true; - } - else if (fastcmp(word, "ENTERTAG")) - { - menupres[num].entertag = get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "EXITTAG")) - { - menupres[num].exittag = get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "ENTERWIPE")) - { - menupres[num].enterwipe = get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "EXITWIPE")) - { - menupres[num].exitwipe = get_number(word2); - titlechanged = true; - } - } - } while (!myfeof(f)); // finish when the line is empty - - Z_Free(s); -} - static void readhuditem(MYFILE *f, INT32 num) { char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); @@ -4710,6 +4314,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) // This is not a major mod either. continue; // continue so that we don't error. } + word2 = strtok(NULL, " "); if (word2) { strupr(word2); @@ -4719,20 +4324,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) } else i = 0; -#ifdef USEPLAYERMENU - if (fastcmp(word, "CHARACTER")) - { - if (i >= 0 && i < 32) - readPlayer(f, i); - else - { - deh_warning("Character %d out of range (0 - 31)", i); - ignorelines(f); - } - continue; - } - else -#endif + if (fastcmp(word, "EMBLEM")) { if (!mainfile && !gamedataadded) @@ -4785,7 +4377,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) } continue; } -#endif + if (word2) { if (fastcmp(word, "THING") || fastcmp(word, "MOBJ") || fastcmp(word, "OBJECT")) @@ -5007,19 +4599,6 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) ignorelines(f); } } - else if (fastcmp(word, "MENU")) - { - if (i == 0 && word2[0] != '0') // If word2 isn't a number - i = get_menutype(word2); // find a huditem by name - if (i >= 1 && i < NUMMENUTYPES) - readmenu(f, i); - else - { - // zero-based, but let's start at 1 - deh_warning("Menu number %d out of range (1 - %d)", i, NUMMENUTYPES-1); - ignorelines(f); - } - } else if (fastcmp(word, "UNLOCKABLE")) { if (!mainfile && !gamedataadded) @@ -11036,101 +10615,6 @@ static const char *const HUDITEMS_LIST[] = { "POWERUPS" }; -static const char *const MENUTYPES_LIST[] = { - "NONE", - - "MAIN", - - // Single Player - "SP_MAIN", - - "SP_LOAD", - "SP_PLAYER", - - "SP_LEVELSELECT", - "SP_LEVELSTATS", - - "SP_TIMEATTACK", - "SP_TIMEATTACK_LEVELSELECT", - "SP_GUESTREPLAY", - "SP_REPLAY", - "SP_GHOST", - - "SP_NIGHTSATTACK", - "SP_NIGHTS_LEVELSELECT", - "SP_NIGHTS_GUESTREPLAY", - "SP_NIGHTS_REPLAY", - "SP_NIGHTS_GHOST", - - // Multiplayer - "MP_MAIN", - "MP_SPLITSCREEN", // SplitServer - "MP_SERVER", - "MP_CONNECT", - "MP_ROOM", - "MP_PLAYERSETUP", // MP_PlayerSetupDef shared with SPLITSCREEN if #defined NONET - "MP_SERVER_OPTIONS", - - // Options - "OP_MAIN", - - "OP_P1CONTROLS", - "OP_CHANGECONTROLS", // OP_ChangeControlsDef shared with P2 - "OP_P1MOUSE", - "OP_P1JOYSTICK", - "OP_JOYSTICKSET", // OP_JoystickSetDef shared with P2 - "OP_P1CAMERA", - - "OP_P2CONTROLS", - "OP_P2MOUSE", - "OP_P2JOYSTICK", - "OP_P2CAMERA", - - "OP_PLAYSTYLE", - - "OP_VIDEO", - "OP_VIDEOMODE", - "OP_COLOR", - "OP_OPENGL", - "OP_OPENGL_LIGHTING", - - "OP_SOUND", - - "OP_SERVER", - "OP_MONITORTOGGLE", - - "OP_DATA", - "OP_ADDONS", - "OP_SCREENSHOTS", - "OP_ERASEDATA", - - // Extras - "SR_MAIN", - "SR_PANDORA", - "SR_LEVELSELECT", - "SR_UNLOCKCHECKLIST", - "SR_EMBLEMHINT", - "SR_PLAYER", - "SR_SOUNDTEST", - - // Addons (Part of MISC, but let's make it our own) - "AD_MAIN", - - // MISC - // "MESSAGE", - // "SPAUSE", - - // "MPAUSE", - // "SCRAMBLETEAM", - // "CHANGETEAM", - // "CHANGELEVEL", - - // "MAPAUSE", - // "HELP", - - "SPECIAL" -}; - struct { const char *n; // has to be able to hold both fixed_t and angle_t, so drastic measure!! @@ -11926,20 +11410,6 @@ static hudnum_t get_huditem(const char *word) return HUD_LIVES; } -static menutype_t get_menutype(const char *word) -{ // Returns the value of MN_ enumerations - menutype_t i; - if (*word >= '0' && *word <= '9') - return atoi(word); - if (fastncmp("MN_",word,3)) - word += 3; // take off the MN_ - for (i = 0; i < NUMMENUTYPES; i++) - if (fastcmp(word, MENUTYPES_LIST[i])) - return i; - deh_warning("Couldn't find menutype named 'MN_%s'",word); - return MN_NONE; -} - /* static INT16 get_gametype(const char *word) { // Returns the value of GT_ enumerations @@ -12157,11 +11627,6 @@ static fixed_t find_const(const char **rword) free(word); return r; } - else if (fastncmp("MN_",word,3)) { - r = get_menutype(word); - free(word); - return r; - } else if (fastncmp("GT_",word,3)) { r = get_gametype(word); free(word); @@ -12771,16 +12236,6 @@ static inline int lib_getenum(lua_State *L) if (mathlib) return luaL_error(L, "NiGHTS grade '%s' could not be found.\n", word); return 0; } - else if (fastncmp("MN_",word,3)) { - p = word+3; - for (i = 0; i < NUMMENUTYPES; i++) - if (fastcmp(p, MENUTYPES_LIST[i])) { - lua_pushinteger(L, i); - return 1; - } - if (mathlib) return luaL_error(L, "menutype '%s' could not be found.\n", word); - return 0; - } else if (!mathlib && fastncmp("A_",word,2)) { char *caps; // Try to get a Lua action first. diff --git a/src/discord.c b/src/discord.c index 652303f65..7b3b0bec8 100644 --- a/src/discord.c +++ b/src/discord.c @@ -20,7 +20,7 @@ #include "i_net.h" #include "g_game.h" #include "p_tick.h" -#include "m_menu.h" // gametype_cons_t +#include "k_menu.h" // gametype_cons_t #include "r_things.h" // skins #include "mserv.h" // cv_advertise #include "z_zone.h" @@ -264,7 +264,7 @@ static void DRPC_HandleJoinRequest(const DiscordUser *requestUser) else { discordRequestList = newRequest; - M_RefreshPauseMenu(); + //M_RefreshPauseMenu(); } // Made it to the end, request was valid, so play the request sound :) diff --git a/src/f_finale.c b/src/f_finale.c index f0a0c7aed..2fbbea881 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -54,7 +54,6 @@ static INT32 timetonext; // Delay between screen changes static tic_t animtimer; // Used for some animation timings static tic_t credbgtimer; // Credits background -static INT16 skullAnimCounter; // Prompts: Chevron animation static tic_t stoptimer; @@ -406,11 +405,11 @@ void F_IntroDrawer(void) I_OsPolling(); I_UpdateNoBlit(); #ifdef HAVE_THREADS - I_lock_mutex(&m_menu_mutex); + I_lock_mutex(&k_menu_mutex); #endif M_Drawer(); // menu is drawn even on top of wipes #ifdef HAVE_THREADS - I_unlock_mutex(m_menu_mutex); + I_unlock_mutex(k_menu_mutex); #endif I_FinishUpdate(); // Update the screen with the image Tails 06-19-2001 @@ -1680,35 +1679,6 @@ void F_GameEndTicker(void) // TITLE SCREEN // ============== -void F_InitMenuPresValues(void) -{ - menuanimtimer = 0; - prevMenuId = 0; - activeMenuId = MainDef.menuid; - - // Set defaults for presentation values - strncpy(curbgname, "TITLESKY", 9); - curfadevalue = 16; - curbgcolor = 31; - curbgxspeed = (gamestate == GS_TIMEATTACK) ? 0 : titlescrollxspeed; - curbgyspeed = (gamestate == GS_TIMEATTACK) ? 22 : titlescrollyspeed; - curbghide = (gamestate == GS_TIMEATTACK) ? false : true; - - curhidepics = hidetitlepics; - curttmode = ttmode; - curttscale = ttscale; - strncpy(curttname, ttname, 9); - curttx = ttx; - curtty = tty; - curttloop = ttloop; - curtttics = tttics; - - // Find current presentation values - //M_SetMenuCurBackground((gamestate == GS_TIMEATTACK) ? "RECATTBG" : "TITLESKY"); - //M_SetMenuCurFadeValue(16); - //M_SetMenuCurTitlePics(); -} - // // F_SkyScroll // @@ -1825,7 +1795,7 @@ void F_StartTitleScreen(void) { ttuser_count = 0; finalecount = 0; - wipetypepost = menupres[MN_MAIN].enterwipe; + wipetypepost = 0; } else wipegamestate = GS_TITLESCREEN; @@ -1877,10 +1847,6 @@ void F_StartTitleScreen(void) camera[0].chase = true; camera[0].height = 0; - // Run enter linedef exec for MN_MAIN, since this is where we start - if (menupres[MN_MAIN].entertag) - P_LinedefExecute(menupres[MN_MAIN].entertag, players[displayplayers[0]].mo, NULL); - wipegamestate = prevwipegamestate; } else @@ -2004,14 +1970,6 @@ luahook: LUAh_TitleHUD(); } -// separate animation timer for backgrounds, since we also count -// during GS_TIMEATTACK -void F_MenuPresTicker(boolean run) -{ - if (run) - menuanimtimer++; -} - // (no longer) De-Demo'd Title Screen void F_TitleScreenTicker(boolean run) { @@ -2026,10 +1984,7 @@ void F_TitleScreenTicker(boolean run) else if (finalecount == 50) { // Now start the music - if (menupres[MN_MAIN].musname[0]) - S_ChangeMusic(menupres[MN_MAIN].musname, menupres[MN_MAIN].mustrack, menupres[MN_MAIN].muslooping); - else - S_ChangeMusicInternal("_title", looptitle); + S_ChangeMusicInternal("_title", looptitle); S_StartSound(NULL, sfx_s23c); } } diff --git a/src/f_finale.h b/src/f_finale.h index 6fb2990ff..f2f53ae2b 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -129,9 +129,6 @@ extern UINT16 curtttics; #define TITLEBACKGROUNDACTIVE (curfadevalue >= 0 || curbgname[0]) -void F_InitMenuPresValues(void); -void F_MenuPresTicker(boolean run); - // // WIPE // diff --git a/src/f_wipe.c b/src/f_wipe.c index b0544a9d4..df6a55de3 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -500,11 +500,11 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu, const char *colormap, boolean r if (drawMenu) { #ifdef HAVE_THREADS - I_lock_mutex(&m_menu_mutex); + I_lock_mutex(&k_menu_mutex); #endif M_Drawer(); // menu is drawn even on top of wipes #ifdef HAVE_THREADS - I_unlock_mutex(m_menu_mutex); + I_unlock_mutex(k_menu_mutex); #endif } diff --git a/src/g_demo.c b/src/g_demo.c index 693a4f095..4ad01781e 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -25,7 +25,7 @@ #include "g_game.h" #include "g_demo.h" #include "m_misc.h" -#include "m_menu.h" +#include "k_menu.h" #include "m_argv.h" #include "hu_stuff.h" #include "z_zone.h" diff --git a/src/g_game.c b/src/g_game.c index 2029eee44..aa78e9f0e 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1892,7 +1892,6 @@ void G_Ticker(boolean run) break; case GS_MENU: - F_MenuPresTicker(run); break; case GS_INTRO: @@ -3590,8 +3589,6 @@ demointermission: // See also F_EndCutscene, the only other place which handles intra-map/ending transitions void G_AfterIntermission(void) { - Y_CleanupScreenBuffer(); - if (modeattacking) { M_EndModeAttackRun(); @@ -4379,8 +4376,6 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, bool (void)FLS; - Y_CleanupScreenBuffer(); - if (paused) { paused = false; diff --git a/src/http-mserv.c b/src/http-mserv.c index 68851483a..04428452d 100644 --- a/src/http-mserv.c +++ b/src/http-mserv.c @@ -22,7 +22,7 @@ Documentation available here. #include "d_clisrv.h" #include "command.h" #include "m_argv.h" -#include "m_menu.h" +#include "k_menu.h" #include "mserv.h" #include "i_tcp.h"/* for current_port */ #include "i_threads.h" diff --git a/src/k_menu.h b/src/k_menu.h index 7129719d6..461dbccee 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -76,6 +76,10 @@ #define MAXSTRINGLENGTH 32 +#ifdef HAVE_THREADS +extern I_mutex k_menu_mutex; +#endif + typedef union { struct menu_s *submenu; // IT_SUBMENU @@ -83,9 +87,21 @@ typedef union void (*routine)(INT32 choice); // IT_CALL, IT_KEYHANDLER, IT_ARROWS } itemaction_t; +// Player Setup menu colors linked list +typedef struct menucolor_s { + struct menucolor_s *next; + struct menucolor_s *prev; + UINT16 color; +} menucolor_t; + +extern menucolor_t *menucolorhead, *menucolortail; + +extern CV_PossibleValue_t gametype_cons_t[]; + // // MENU TYPEDEFS // + typedef struct menuitem_s { UINT16 status; // show IT_xxx @@ -97,19 +113,23 @@ typedef struct menuitem_s void *itemaction; // FIXME: should be itemaction_t // extra variables - UINT8 mvar1; - UINT8 mvar2; + INT32 mvar1; + INT32 mvar2; } menuitem_t; typedef struct menu_s { INT16 numitems; // # of menu items struct menu_s *prevMenu; // previous menu + INT16 lastOn; // last item user was on in menu menuitem_t *menuitems; // menu items + INT16 x, y; // x, y of menu + INT16 transitionID; // only transition if IDs match INT16 transitionTics; // tics for transitions out + void (*drawroutine)(void); // draw routine void (*tickroutine)(void); // ticker routine boolean (*quitroutine)(void); // called before quit a menu return true if we can @@ -185,6 +205,9 @@ typedef enum // K_MENUFUNC.C +// Moviemode menu updating +void Moviemode_option_Onchange(void); + extern menu_t *currentMenu; extern char dummystaffname[22]; @@ -228,6 +251,14 @@ void M_HandleImageDef(INT32 choice); void M_QuitResponse(INT32 ch); void M_QuitSRB2(INT32 choice); +void M_AddMenuColor(UINT16 color); +void M_MoveColorBefore(UINT16 color, UINT16 targ); +void M_MoveColorAfter(UINT16 color, UINT16 targ); +UINT16 M_GetColorBefore(UINT16 color); +UINT16 M_GetColorAfter(UINT16 color); +void M_InitPlayerSetupColors(void); +void M_FreePlayerSetupColors(void); + // If you want to waste a bunch of memory for a limit no one will hit, feel free to boost this to MAXSKINS :P // I figure this will be enough clone characters to fit onto the character select. // (If someone runs into it after release I'll probably boost it, though.) @@ -317,6 +348,8 @@ void M_CupSelectTick(void); void M_LevelSelectHandler(INT32 choice); void M_LevelSelectTick(void); +extern tic_t playback_last_menu_interaction_leveltime; + void M_EndModeAttackRun(void); void M_SetPlaybackMenuPointer(void); void M_PlaybackRewind(INT32 choice); @@ -325,12 +358,15 @@ void M_PlaybackFastForward(INT32 choice); void M_PlaybackAdvance(INT32 choice); void M_PlaybackSetViews(INT32 choice); void M_PlaybackAdjustView(INT32 choice); +void M_PlaybackToggleFreecam(INT32 choice); void M_PlaybackQuit(INT32 choice); void M_ReplayHut(INT32 choice); // M_MENUDRAW.C +void M_DrawMenuBackground(void); +void M_DrawMenuForeground(void); void M_Drawer(void); void M_DrawGenericMenu(void); void M_DrawKartGamemodeMenu(void); diff --git a/src/k_menudef.c b/src/k_menudef.c index faafeacb2..ea95e9b6a 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -157,10 +157,10 @@ menu_t PLAY_TimeAttackDef = { menuitem_t PLAY_BattleGamemodesMenu[] = { {IT_STRING | IT_CALL, "Survival", "It's last hedgehog standing in this free-for-all!", - "MENIMG00", M_LevelSelectInit, 0, GT_MATCH}, + "MENIMG00", M_LevelSelectInit, 0, GT_BATTLE}, {IT_STRING | IT_CALL, "Time Attack", "Bust up all of the capsules in record time!", - NULL, M_LevelSelectInit, 1, GT_MATCH}, + NULL, M_LevelSelectInit, 1, GT_BATTLE}, {IT_STRING | IT_CALL, "Back", NULL, NULL, M_GoBack, 0, 0}, }; @@ -173,23 +173,23 @@ menu_t PLAY_BattleGamemodesDef = KARTGAMEMODEMENU(PLAY_BattleGamemodesMenu, &PLA menuitem_t PAUSE_PlaybackMenu[] = { - {IT_CALL | IT_STRING, "Hide Menu", NULL, "M_PHIDE", M_SelectableClearMenus, 0, 0}, + {IT_CALL | IT_STRING, "Hide Menu (Esc)", NULL, "M_PHIDE", M_SelectableClearMenus, 0, 0}, - {IT_CALL | IT_STRING, "Rewind", NULL, "M_PREW", M_PlaybackRewind, 20, 0}, - {IT_CALL | IT_STRING, "Pause", NULL, "M_PPAUSE", M_PlaybackPause, 36, 0}, - {IT_CALL | IT_STRING, "Fast-Forward", NULL, "M_PFFWD", M_PlaybackFastForward, 52, 0}, + {IT_CALL | IT_STRING, "Rewind ([)", NULL, "M_PREW", M_PlaybackRewind, 20, 0}, + {IT_CALL | IT_STRING, "Pause (\\)", NULL, "M_PPAUSE", M_PlaybackPause, 36, 0}, + {IT_CALL | IT_STRING, "Fast-Forward (])", NULL, "M_PFFWD", M_PlaybackFastForward, 52, 0}, + {IT_CALL | IT_STRING, "Backup Frame ([)", NULL, "M_PSTEPB", M_PlaybackRewind, 20, 0}, + {IT_CALL | IT_STRING, "Resume", NULL, "M_PRESUM", M_PlaybackPause, 36, 0}, + {IT_CALL | IT_STRING, "Advance Frame (])", NULL, "M_PFADV", M_PlaybackAdvance, 52, 0}, - {IT_CALL | IT_STRING, "Backup Frame", NULL, "M_PSTEPB", M_PlaybackRewind, 20, 0}, - {IT_CALL | IT_STRING, "Resume", NULL, "M_PRESUM", M_PlaybackPause, 36, 0}, - {IT_CALL | IT_STRING, "Advance Frame", NULL, "M_PFADV", M_PlaybackAdvance, 52, 0}, + {IT_ARROWS | IT_STRING, "View Count (- and =)", NULL, "M_PVIEWS", M_PlaybackSetViews, 72, 0}, + {IT_ARROWS | IT_STRING, "Viewpoint (1)", NULL, "M_PNVIEW", M_PlaybackAdjustView, 88, 0}, + {IT_ARROWS | IT_STRING, "Viewpoint 2 (2)", NULL, "M_PNVIEW", M_PlaybackAdjustView, 104, 0}, + {IT_ARROWS | IT_STRING, "Viewpoint 3 (3)", NULL, "M_PNVIEW", M_PlaybackAdjustView, 120, 0}, + {IT_ARROWS | IT_STRING, "Viewpoint 4 (4)", NULL, "M_PNVIEW", M_PlaybackAdjustView, 136, 0}, - {IT_ARROWS | IT_STRING, "View Count", NULL, "M_PVIEWS", M_PlaybackSetViews, 72, 0}, - {IT_ARROWS | IT_STRING, "Viewpoint", NULL, "M_PNVIEW", M_PlaybackAdjustView, 88, 0}, - {IT_ARROWS | IT_STRING, "Viewpoint 2", NULL, "M_PNVIEW", M_PlaybackAdjustView, 104, 0}, - {IT_ARROWS | IT_STRING, "Viewpoint 3", NULL, "M_PNVIEW", M_PlaybackAdjustView, 120, 0}, - {IT_ARROWS | IT_STRING, "Viewpoint 4", NULL, "M_PNVIEW", M_PlaybackAdjustView, 136, 0}, - - {IT_CALL | IT_STRING, "Stop Playback", NULL, "M_PEXIT", M_PlaybackQuit, 156, 0}, + {IT_CALL | IT_STRING, "Toggle Free Camera (')", NULL, "M_PVIEWS", M_PlaybackToggleFreecam, 156, 0}, + {IT_CALL | IT_STRING, "Stop Playback", NULL, "M_PEXIT", M_PlaybackQuit, 172, 0}, }; menu_t PAUSE_PlaybackMenuDef = { diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 73eb877e7..f9c86ade3 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -46,6 +46,7 @@ #include "st_stuff.h" #include "i_sound.h" #include "k_kart.h" // SRB2kart +#include "k_hud.h" // SRB2kart #include "d_player.h" // KITEM_ constants #include "doomstat.h" // MAXSPLITSCREENPLAYERS @@ -84,11 +85,26 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #define SLIDER_WIDTH (8*SLIDER_RANGE+6) #define SERVERS_PER_PAGE 11 +void M_DrawMenuBackground(void) +{ + V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUBG", PU_CACHE), NULL); +} + +void M_DrawMenuForeground(void) +{ + // draw non-green resolution border + if ((vid.width % BASEVIDWIDTH != 0) || (vid.height % BASEVIDHEIGHT != 0)) + { + V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("WEIRDRES", PU_CACHE), NULL); + } +} + // // M_Drawer // Called after the view has been rendered, // but before it has been blitted. // + void M_Drawer(void) { if (currentMenu == &MessageDef) @@ -99,17 +115,19 @@ void M_Drawer(void) if (menuactive) { - if (gamestate == GS_MENU) // draw BG - V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUBG", PU_CACHE), NULL); + if (gamestate == GS_MENU) + { + M_DrawMenuBackground(); + } else if (!WipeInAction && currentMenu != &PAUSE_PlaybackMenuDef) + { V_DrawCustomFadeScreen("FADEMAP0", 4); // now that's more readable with a faded background (yeah like Quake...) + } if (currentMenu->drawroutine) currentMenu->drawroutine(); // call current menu Draw routine - // draw non-green resolution border - if ((vid.width % BASEVIDWIDTH != 0) || (vid.height % BASEVIDHEIGHT != 0)) - V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("WEIRDRES", PU_CACHE), NULL); + M_DrawMenuForeground(); // Draw version down in corner // ... but only in the MAIN MENU. I'm a picky bastard. @@ -228,7 +246,6 @@ void M_DrawGenericMenu(void) { if (i == itemOn) cursory = y; - switch (currentMenu->menuitems[i].status & IT_DISPLAY) { case IT_PATCH: @@ -274,8 +291,6 @@ void M_DrawGenericMenu(void) 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) { #if 0 @@ -285,27 +300,6 @@ void M_DrawGenericMenu(void) case IT_CV_INVISSLIDER: // monitor toggles use this break; #endif - 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); @@ -544,7 +538,7 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) if (p->mdepth == CSSTEP_ALTS) numoptions = setup_chargrid[p->gridx][p->gridy].numskins; else - numoptions = MAXSKINCOLORS-1; + numoptions = numskincolors-1; angamt /= numoptions; @@ -570,7 +564,7 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) n %= numoptions; skin = setup_chargrid[p->gridx][p->gridy].skinlist[n]; - patch = facerankprefix[skin]; + patch = faceprefix[skin][FACE_RANK]; colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE); radius = 24<gridx][p->gridy].skinlist[p->clonenum]; + UINT8 color = p->color; + UINT8 *colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); + + if (skin != -1) + { + UINT8 spr; + spritedef_t *sprdef; + spriteframe_t *sprframe; + patch_t *sprpatch; + + UINT32 flags = 0; + UINT32 frame; + + spr = P_GetSkinSprite2(&skins[skin], SPR2_FSTN, NULL); + sprdef = &skins[skin].sprites[spr]; + + if (!sprdef->numframes) // No frames ?? + return; // Can't render! + + frame = states[S_KART_FAST].frame & FF_FRAMEMASK; + if (frame >= sprdef->numframes) // Walking animation missing + frame = 0; // Try to use standing frame + + sprframe = &sprdef->spriteframes[frame]; + sprpatch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE); + + if (sprframe->flip & 1) // Only for first sprite + flags |= V_FLIP; // This sprite is left/right flipped! + + // Flip for left-side players + if (!(num & 1)) + flags ^= V_FLIP; + + if (skins[skin].flags & SF_HIRES) + { + V_DrawFixedPatch(x<mdepth >= CSSTEP_CHARS) { - skin = setup_chargrid[p->gridx][p->gridy].skinlist[p->clonenum]; - - color = p->color; - colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); - - if (skin != -1) - { - statenum_t st = S_KART_FAST1; - UINT32 flags = 0; - UINT32 frame; - - if (skullAnimCounter & 1) - st++; - - sprdef = &skins[skin].spritedef; - - if (sprdef->numframes) // No frames ?? - { - frame = states[st].frame & FF_FRAMEMASK; - if (frame >= sprdef->numframes) // Walking animation missing - frame = 0; // Try to use standing frame - - sprframe = &sprdef->spriteframes[frame]; - sprpatch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE); - if (sprframe->flip & 1) // Only for first sprite - flags |= V_FLIP; // This sprite is left/right flipped! - - // Flip for left-side players - if (!(num & 1)) - flags ^= V_FLIP; - - if (skins[skin].flags & SF_HIRES) - { - V_DrawFixedPatch((x+32)<mdepth == CSSTEP_ALTS || p->mdepth == CSSTEP_COLORS) + { M_DrawCharSelectCircle(p, x+32, y+64); + } } if ((setup_animcounter/10) & 1) { if (p->mdepth == CSSTEP_NONE && num == setup_numplayers) + { V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PSTART", PU_CACHE)); - //else if (p->mdepth >= CSSTEP_READY) - // V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PREADY", PU_CACHE)); + } + /* + else if (p->mdepth >= CSSTEP_READY) + { + V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PREADY", PU_CACHE)); + } + */ } V_DrawScaledPatch(x+9, y+2, 0, W_CachePatchName("FILEBACK", PU_CACHE)); @@ -856,7 +861,7 @@ void M_DrawCharacterSelect(void) else colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE); - V_DrawMappedPatch(82 + (i*16) + quadx, 22 + (j*16) + quady, 0, facerankprefix[skin], colormap); + V_DrawMappedPatch(82 + (i*16) + quadx, 22 + (j*16) + quady, 0, faceprefix[skin][FACE_RANK], colormap); if (setup_chargrid[i][j].numskins > 1) V_DrawScaledPatch(82 + (i*16) + quadx, 22 + (j*16) + quady + 11, 0, W_CachePatchName("ALTSDOT", PU_CACHE)); @@ -1087,19 +1092,13 @@ static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map) } } - if (mapheaderinfo[map]->actnum[0]) + if (mapheaderinfo[map]->actnum) { word2[word2len] = ' '; word2len++; - for (i = 0; i < 3; i++) - { - if (!mapheaderinfo[map]->actnum[i]) - break; - - word2[word2len] = mapheaderinfo[map]->actnum[i]; - word2len++; - } + word2[word2len] = '0' + mapheaderinfo[map]->actnum; + word2len++; } word1[word1len] = '\0'; @@ -1243,11 +1242,19 @@ void M_DrawTimeAttack(void) // // INGAME / PAUSE MENUS // + +tic_t playback_last_menu_interaction_leveltime = 0; + void M_DrawPlaybackMenu(void) { INT16 i; patch_t *icon = NULL; UINT8 *activemap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GOLD, GTC_MENUCACHE); + UINT32 transmap = max(0, (INT32)(leveltime - playback_last_menu_interaction_leveltime - 4*TICRATE)) / 5; + transmap = min(8, transmap) << V_ALPHASHIFT; + + if (leveltime - playback_last_menu_interaction_leveltime >= 6*TICRATE) + playback_last_menu_interaction_leveltime = leveltime - 6*TICRATE; // Toggle items if (paused && !demo.rewinding) @@ -1312,7 +1319,7 @@ void M_DrawPlaybackMenu(void) { INT32 ply = displayplayers[i - playback_view1]; - icon = facerankprefix[players[ply].skin]; + icon = faceprefix[players[ply].skin][FACE_RANK]; if (i != itemOn) inactivemap = R_GetTranslationColormap(players[ply].skin, players[ply].skincolor, GTC_MENUCACHE); } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index e534c5bca..89508cc63 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -58,6 +58,8 @@ // And just some randomness for the exits. #include "m_random.h" +#include "r_skins.h" + #if defined(HAVE_SDL) #include "SDL.h" #if SDL_VERSION_ATLEAST(2,0,0) @@ -75,6 +77,10 @@ int snprintf(char *str, size_t n, const char *fmt, ...); // GLOBAL VARIABLES // ========================================================================== +#ifdef HAVE_THREADS +I_mutex k_menu_mutex; +#endif + boolean menuactive = false; boolean fromlevelselect = false; @@ -102,7 +108,14 @@ static boolean noFurtherInput = false; static void Dummymenuplayer_OnChange(void); static void Dummystaff_OnChange(void); -consvar_t cv_showfocuslost = {"showfocuslost", "Yes", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL }; +consvar_t cv_showfocuslost = CVAR_INIT ("showfocuslost", "Yes", CV_SAVE, CV_YesNo, NULL); + +static CV_PossibleValue_t skins_cons_t[MAXSKINS+1] = {{1, DEFAULTSKIN}}; +consvar_t cv_chooseskin = CVAR_INIT ("chooseskin", DEFAULTSKIN, CV_HIDDEN, skins_cons_t, NULL); + +// This gametype list is integral for many different reasons. +// When you add gametypes here, don't forget to update them in dehacked.c and doomstat.h! +CV_PossibleValue_t gametype_cons_t[NUMGAMETYPES+1]; static CV_PossibleValue_t serversort_cons_t[] = { {0,"Ping"}, @@ -113,44 +126,41 @@ static CV_PossibleValue_t serversort_cons_t[] = { {5,"Gametype"}, {0,NULL} }; -consvar_t cv_serversort = {"serversort", "Ping", CV_CALL, serversort_cons_t, M_SortServerList, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_serversort = CVAR_INIT ("serversort", "Ping", CV_CALL, serversort_cons_t, M_SortServerList); + +// first time memory +consvar_t cv_tutorialprompt = CVAR_INIT ("tutorialprompt", "On", CV_SAVE, CV_OnOff, NULL); // autorecord demos for time attack -static consvar_t cv_autorecord = {"autorecord", "Yes", 0, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; +static consvar_t cv_autorecord = CVAR_INIT ("autorecord", "Yes", 0, CV_YesNo, 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}; - -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}; +consvar_t cv_ghost_besttime = CVAR_INIT ("ghost_besttime", "Show All", CV_SAVE, ghost_cons_t, NULL); +consvar_t cv_ghost_bestlap = CVAR_INIT ("ghost_bestlap", "Show All", CV_SAVE, ghost_cons_t, NULL); +consvar_t cv_ghost_last = CVAR_INIT ("ghost_last", "Show All", CV_SAVE, ghost_cons_t, NULL); +consvar_t cv_ghost_guest = CVAR_INIT ("ghost_guest", "Show", CV_SAVE, ghost2_cons_t, NULL); +consvar_t cv_ghost_staff = CVAR_INIT ("ghost_staff", "Show", CV_SAVE, ghost2_cons_t, 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 CV_PossibleValue_t skins_cons_t[MAXSKINS+1] = {{1, DEFAULTSKIN}}; -consvar_t cv_chooseskin = {"chooseskin", DEFAULTSKIN, CV_HIDDEN, skins_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +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 = CVAR_INIT ("splitplayers", "One", CV_CALL, splitplayers_cons_t, Splitplayers_OnChange); static CV_PossibleValue_t dummymenuplayer_cons_t[] = {{0, "NOPE"}, {1, "P1"}, {2, "P2"}, {3, "P3"}, {4, "P4"}, {0, NULL}}; -static consvar_t cv_dummymenuplayer = {"dummymenuplayer", "P1", CV_HIDDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange, 0, NULL, NULL, 0, 0, NULL}; - static CV_PossibleValue_t dummyteam_cons_t[] = {{0, "Spectator"}, {1, "Red"}, {2, "Blue"}, {0, NULL}}; -static consvar_t cv_dummyteam = {"dummyteam", "Spectator", CV_HIDDEN, dummyteam_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; - static CV_PossibleValue_t dummyspectate_cons_t[] = {{0, "Spectator"}, {1, "Playing"}, {0, NULL}}; -static consvar_t cv_dummyspectate = {"dummyspectate", "Spectator", CV_HIDDEN, dummyspectate_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; - static CV_PossibleValue_t dummyscramble_cons_t[] = {{0, "Random"}, {1, "Points"}, {0, NULL}}; -static consvar_t cv_dummyscramble = {"dummyscramble", "Random", CV_HIDDEN, dummyscramble_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; - static CV_PossibleValue_t dummystaff_cons_t[] = {{0, "MIN"}, {100, "MAX"}, {0, NULL}}; -static consvar_t cv_dummystaff = {"dummystaff", "0", CV_HIDDEN|CV_CALL, dummystaff_cons_t, Dummystaff_OnChange, 0, NULL, NULL, 0, 0, NULL}; + +static consvar_t cv_dummymenuplayer = CVAR_INIT ("dummymenuplayer", "P1", CV_HIDDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange); +static consvar_t cv_dummyteam = CVAR_INIT ("dummyteam", "Spectator", CV_HIDDEN, dummyteam_cons_t, NULL); +static consvar_t cv_dummyspectate = CVAR_INIT ("dummyspectate", "Spectator", CV_HIDDEN, dummyspectate_cons_t, NULL); +static consvar_t cv_dummyscramble = CVAR_INIT ("dummyscramble", "Random", CV_HIDDEN, dummyscramble_cons_t, NULL); +static consvar_t cv_dummystaff = CVAR_INIT ("dummystaff", "0", CV_HIDDEN|CV_CALL, dummystaff_cons_t, Dummystaff_OnChange); // ========================================================================== // CVAR ONCHANGE EVENTS GO HERE @@ -330,6 +340,14 @@ void Moviemode_mode_Onchange(void) #endif } +void Moviemode_option_Onchange(void) +{ +#if 0 + OP_ScreenshotOptionsMenu[op_movie_folder].status = + (cv_movie_option.value == 3 ? IT_CVAR|IT_STRING|IT_CV_STRING : IT_DISABLED); +#endif +} + void Addons_option_Onchange(void) { #if 0 @@ -371,6 +389,179 @@ void M_SortServerList(void) // BASIC MENU HANDLING // ========================================================================= +void M_AddMenuColor(UINT16 color) { + menucolor_t *c; + + // SRB2Kart: I do not understand vanilla doesn't need this but WE do???!?!??! + if (!skincolors[color].accessible) { + return; + } + + if (color >= numskincolors) { + CONS_Printf("M_AddMenuColor: color %d does not exist.",color); + return; + } + + c = (menucolor_t *)malloc(sizeof(menucolor_t)); + c->color = color; + if (menucolorhead == NULL) { + c->next = c; + c->prev = c; + menucolorhead = c; + menucolortail = c; + } else { + c->next = menucolorhead; + c->prev = menucolortail; + menucolortail->next = c; + menucolorhead->prev = c; + menucolortail = c; + } +} + +void M_MoveColorBefore(UINT16 color, UINT16 targ) { + menucolor_t *look, *c = NULL, *t = NULL; + + if (color == targ) + return; + if (color >= numskincolors) { + CONS_Printf("M_MoveColorBefore: color %d does not exist.",color); + return; + } + if (targ >= numskincolors) { + CONS_Printf("M_MoveColorBefore: target color %d does not exist.",targ); + return; + } + + for (look=menucolorhead;;look=look->next) { + if (look->color == color) + c = look; + else if (look->color == targ) + t = look; + if (c != NULL && t != NULL) + break; + if (look==menucolortail) + return; + } + + if (c == t->prev) + return; + + if (t==menucolorhead) + menucolorhead = c; + if (c==menucolortail) + menucolortail = c->prev; + + c->prev->next = c->next; + c->next->prev = c->prev; + + c->prev = t->prev; + c->next = t; + t->prev->next = c; + t->prev = c; +} + +void M_MoveColorAfter(UINT16 color, UINT16 targ) { + menucolor_t *look, *c = NULL, *t = NULL; + + if (color == targ) + return; + if (color >= numskincolors) { + CONS_Printf("M_MoveColorAfter: color %d does not exist.\n",color); + return; + } + if (targ >= numskincolors) { + CONS_Printf("M_MoveColorAfter: target color %d does not exist.\n",targ); + return; + } + + for (look=menucolorhead;;look=look->next) { + if (look->color == color) + c = look; + else if (look->color == targ) + t = look; + if (c != NULL && t != NULL) + break; + if (look==menucolortail) + return; + } + + if (t == c->prev) + return; + + if (t==menucolortail) + menucolortail = c; + else if (c==menucolortail) + menucolortail = c->prev; + + c->prev->next = c->next; + c->next->prev = c->prev; + + c->next = t->next; + c->prev = t; + t->next->prev = c; + t->next = c; +} + +UINT16 M_GetColorBefore(UINT16 color) { + menucolor_t *look; + + if (color >= numskincolors) { + CONS_Printf("M_GetColorBefore: color %d does not exist.\n",color); + return 0; + } + + for (look=menucolorhead;;look=look->next) { + if (look->color == color) + return look->prev->color; + if (look==menucolortail) + return 0; + } +} + +UINT16 M_GetColorAfter(UINT16 color) { + menucolor_t *look; + + if (color >= numskincolors) { + CONS_Printf("M_GetColorAfter: color %d does not exist.\n",color); + return 0; + } + + for (look=menucolorhead;;look=look->next) { + if (look->color == color) + return look->next->color; + if (look==menucolortail) + return 0; + } +} + +void M_InitPlayerSetupColors(void) { + UINT8 i; + numskincolors = SKINCOLOR_FIRSTFREESLOT; + menucolorhead = menucolortail = NULL; + for (i=0; inext; + free(tmp); + } else { + free(look); + return; + } + } + + menucolorhead = menucolortail = NULL; +} + static void M_ChangeCvar(INT32 choice) { consvar_t *cv = (consvar_t *)currentMenu->menuitems[itemOn].itemaction; @@ -378,17 +569,6 @@ static void M_ChangeCvar(INT32 choice) // Backspace sets values to default value if (choice == -1) { - // There's a default color technically, but it's not ideal. Use your skin's prefcolor instead! - 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; } @@ -533,15 +713,15 @@ boolean M_Responder(event_t *ev) switch (ch) { case KEY_MOUSE1: - //case KEY_JOY1: - //case KEY_JOY1 + 2: + //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_JOY1 + 3: // Brake can function as 'n' for message boxes now. + ch = 'n'; + break;*/ case KEY_MOUSE1 + 1: - //case KEY_JOY1 + 1: + //case KEY_JOY1 + 1: ch = KEY_BACKSPACE; break; case KEY_HAT1: @@ -562,10 +742,10 @@ boolean M_Responder(event_t *ev) { if (ev->type == ev_joystick && ev->data1 == 0 && joywait < I_GetTime()) { - const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_deadzone.value) >> FRACBITS; + const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_deadzone[0].value) >> FRACBITS; if (ev->data3 != INT32_MAX) { - if (Joystick.bGamepadStyle || abs(ev->data3) > jdeadzone) + if (Joystick[0].bGamepadStyle || abs(ev->data3) > jdeadzone) { if (ev->data3 < 0 && pjoyy >= 0) { @@ -585,7 +765,7 @@ boolean M_Responder(event_t *ev) if (ev->data2 != INT32_MAX) { - if (Joystick.bGamepadStyle || abs(ev->data2) > jdeadzone) + if (Joystick[0].bGamepadStyle || abs(ev->data2) > jdeadzone) { if (ev->data2 < 0 && pjoyx >= 0) { @@ -637,9 +817,9 @@ boolean M_Responder(event_t *ev) if (ch == -1) return false; - else if (ch == gamecontrol[gc_systemmenu][0] || ch == gamecontrol[gc_systemmenu][1]) // allow remappable ESC key + else if (ch == gamecontrol[0][gc_systemmenu][0] || ch == gamecontrol[0][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) + else if ((ch == gamecontrol[0][gc_accelerate][0] || ch == gamecontrol[0][gc_accelerate][1]) && ch >= KEY_MOUSE1) ch = KEY_ENTER; // F-Keys @@ -699,11 +879,12 @@ boolean M_Responder(event_t *ev) return true; case KEY_F11: // Empty (used to be Gamma) - //CV_AddValue(&cv_usegamma, 1); + //CV_AddValue(&cv_globalgamma, 1); return true; // Spymode on F12 handled in game logic #endif + case KEY_ESCAPE: // Pop up menu if (chat_on) { @@ -714,13 +895,11 @@ boolean M_Responder(event_t *ev) 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 + if ((ch == gamecontrol[0][gc_brake][0] || ch == gamecontrol[0][gc_brake][1]) && ch >= KEY_MOUSE1) // do this here, otherwise brake opens the menu mid-game ch = KEY_ESCAPE; routine = currentMenu->menuitems[itemOn].itemaction; @@ -751,8 +930,11 @@ boolean M_Responder(event_t *ev) 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) + if (ev->type == ev_mouse + || ev->type == ev_joystick + || ev->type == ev_joystick2 + || ev->type == ev_joystick3 + || ev->type == ev_joystick4) return true; if (routine) { @@ -766,10 +948,8 @@ boolean M_Responder(event_t *ev) // 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 ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_STRING) { - 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]; @@ -782,8 +962,9 @@ boolean M_Responder(event_t *ev) routine = M_ChangeCvar; } - if (currentMenu == &PAUSE_PlaybackMenuDef) + if (currentMenu == &PAUSE_PlaybackMenuDef && !con_destlines) { + playback_last_menu_interaction_leveltime = leveltime; // Flip left/right with up/down for the playback menu, since it's a horizontal icon row. switch (ch) { @@ -791,6 +972,56 @@ boolean M_Responder(event_t *ev) case KEY_UPARROW: ch = KEY_RIGHTARROW; break; case KEY_RIGHTARROW: ch = KEY_DOWNARROW; break; case KEY_DOWNARROW: ch = KEY_LEFTARROW; break; + + // arbitrary keyboard shortcuts because fuck you + + case '\'': // toggle freecam + M_PlaybackToggleFreecam(0); + break; + + case ']': // ffw / advance frame (depends on if paused or not) + if (paused) + M_PlaybackAdvance(0); + else + M_PlaybackFastForward(0); + break; + + case '[': // rewind /backupframe, uses the same function + M_PlaybackRewind(0); + break; + + case '\\': // pause + M_PlaybackPause(0); + break; + + // viewpoints, an annoyance (tm) + case '-': // viewpoint minus + M_PlaybackSetViews(-1); // yeah lol. + break; + + case '=': // viewpoint plus + M_PlaybackSetViews(1); // yeah lol. + break; + + // switch viewpoints: + case '1': // viewpoint for p1 (also f12) + // maximum laziness: + if (!demo.freecam) + G_AdjustView(1, 1, true); + break; + case '2': // viewpoint for p2 + if (!demo.freecam) + G_AdjustView(2, 1, true); + break; + case '3': // viewpoint for p3 + if (!demo.freecam) + G_AdjustView(3, 1, true); + break; + case '4': // viewpoint for p4 + if (!demo.freecam) + G_AdjustView(4, 1, true); + break; + default: break; } } @@ -840,9 +1071,9 @@ boolean M_Responder(event_t *ev) if (currentMenu == &PAUSE_PlaybackMenuDef) { boolean held = (boolean)playback_enterheld; - playback_enterheld = TICRATE/7; if (held) return true; + playback_enterheld = 3; } #endif @@ -856,7 +1087,7 @@ boolean M_Responder(event_t *ev) { if (((currentMenu->menuitems[itemOn].status & IT_CALLTYPE) & IT_CALL_NOTMODIFIED) && majormods) { - 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); + M_StartMessage(M_GetText("This cannot be done with complex addons\nor in a cheated game.\n\n(Press a key)\n"), NULL, MM_NOTHING); return true; } } @@ -876,7 +1107,6 @@ boolean M_Responder(event_t *ev) break; } } - return true; case KEY_ESCAPE: @@ -902,15 +1132,17 @@ boolean M_Responder(event_t *ev) if (cv == &cv_chooseskin || cv == &cv_dummystaff - /*|| cv == &cv_nextmap - || cv == &cv_newgametype*/) + /* + || cv == &cv_nextmap + || cv == &cv_newgametype + */ + ) return true; #if 0 if (currentMenu != &OP_SoundOptionsDef || itemOn > 3) #endif S_StartSound(NULL, sfx_s3k5b); - routine(-1); return true; } @@ -1194,7 +1426,6 @@ void M_GoBack(INT32 choice) //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; } @@ -1599,8 +1830,6 @@ struct setup_explosions_s setup_explosions[48]; UINT8 setup_numplayers = 0; tic_t setup_animcounter = 0; -consvar_t *setup_playercvars[MAXSPLITSCREENPLAYERS][SPLITCV_MAX]; - void M_CharacterSelectInit(INT32 choice) { UINT8 i, j; @@ -1621,22 +1850,6 @@ void M_CharacterSelectInit(INT32 choice) memset(setup_explosions, 0, sizeof(setup_explosions)); setup_animcounter = 0; - // Keep these in a table for the sake of my sanity later - setup_playercvars[0][SPLITCV_SKIN] = &cv_skin; - setup_playercvars[1][SPLITCV_SKIN] = &cv_skin2; - setup_playercvars[2][SPLITCV_SKIN] = &cv_skin3; - setup_playercvars[3][SPLITCV_SKIN] = &cv_skin4; - - setup_playercvars[0][SPLITCV_COLOR] = &cv_playercolor; - setup_playercvars[1][SPLITCV_COLOR] = &cv_playercolor2; - setup_playercvars[2][SPLITCV_COLOR] = &cv_playercolor3; - setup_playercvars[3][SPLITCV_COLOR] = &cv_playercolor4; - - setup_playercvars[0][SPLITCV_NAME] = &cv_playername; - setup_playercvars[1][SPLITCV_NAME] = &cv_playername2; - setup_playercvars[2][SPLITCV_NAME] = &cv_playername3; - setup_playercvars[3][SPLITCV_NAME] = &cv_playername4; - for (i = 0; i < numskins; i++) { UINT8 x = skins[i].kartspeed-1; @@ -1652,7 +1865,7 @@ void M_CharacterSelectInit(INT32 choice) for (j = 0; j < MAXSPLITSCREENPLAYERS; j++) { - if (!strcmp(setup_playercvars[j][SPLITCV_SKIN]->string, skins[i].name)) + if (!strcmp(cv_skin[j].string, skins[i].name)) { setup_player[j].gridx = x; setup_player[j].gridy = y; @@ -1809,7 +2022,7 @@ static void M_HandleColorRotate(INT32 choice, setup_player_t *p) { case KEY_RIGHTARROW: p->color++; - if (p->color >= MAXSKINCOLORS) + if (p->color >= numskincolors) p->color = 1; p->rotate = CSROTATETICS; //p->delay = CSROTATETICS; @@ -1818,7 +2031,7 @@ static void M_HandleColorRotate(INT32 choice, setup_player_t *p) case KEY_LEFTARROW: p->color--; if (p->color < 1) - p->color = MAXSKINCOLORS-1; + p->color = numskincolors-1; p->rotate = -CSROTATETICS; //p->delay = CSROTATETICS; S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s @@ -1949,8 +2162,8 @@ void M_CharacterSelectTick(void) { for (i = 0; i < setup_numplayers; i++) { - CV_StealthSet(setup_playercvars[i][SPLITCV_SKIN], skins[setup_player[i].skin].name); - CV_StealthSetValue(setup_playercvars[i][SPLITCV_COLOR], setup_player[i].color); + CV_StealthSet(&cv_skin[i], skins[setup_player[i].skin].name); + CV_StealthSetValue(&cv_playercolor[i], setup_player[i].color); } CV_StealthSetValue(&cv_splitplayers, setup_numplayers); @@ -1989,7 +2202,7 @@ boolean M_CanShowLevelInList(INT16 mapnum, UINT8 gt) if (mapheaderinfo[mapnum]->menuflags & LF2_HIDEINMENU /*&& mapnum+1 != gamemap*/) return false; - if (gt == GT_MATCH && (mapheaderinfo[mapnum]->typeoflevel & TOL_MATCH)) + if (gt == GT_BATTLE && (mapheaderinfo[mapnum]->typeoflevel & TOL_BATTLE)) return true; if (gt == GT_RACE && (mapheaderinfo[mapnum]->typeoflevel & TOL_RACE)) @@ -2474,6 +2687,32 @@ void M_PlaybackAdjustView(INT32 choice) G_AdjustView(itemOn - playback_viewcount, (choice > 0) ? 1 : -1, true); } +// this one's rather tricky +void M_PlaybackToggleFreecam(INT32 choice) +{ + (void)choice; + M_ClearMenus(true); + + // remove splitscreen: + splitscreen = 0; + R_ExecuteSetViewSize(); + + P_InitCameraCmd(); // init camera controls + if (!demo.freecam) // toggle on + { + demo.freecam = true; + democam.cam = &camera[0]; // this is rather useful + } + else // toggle off + { + demo.freecam = false; + // reset democam vars: + democam.cam = NULL; + democam.turnheld = false; + democam.keyboardlook = false; // reset only these. localangle / aiming gets set before the cam does anything anyway + } +} + void M_PlaybackQuit(INT32 choice) { (void)choice; diff --git a/src/lua_baselib.c b/src/lua_baselib.c index eff2264c1..d3423c2c7 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -22,7 +22,6 @@ #include "m_random.h" #include "s_sound.h" #include "g_game.h" -#include "m_menu.h" #include "y_inter.h" #include "hu_stuff.h" // HU_AddChatText #include "console.h" @@ -31,7 +30,7 @@ #include "k_color.h" #include "k_hud.h" #include "d_netcmd.h" // IsPlayerAdmin -#include "m_menu.h" // Player Setup menu color stuff +#include "k_menu.h" // Player Setup menu color stuff // SRB2Kart #include "p_spec.h" // P_StartQuake diff --git a/src/mserv.c b/src/mserv.c index fef056b1e..e01c345a2 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -116,11 +116,11 @@ void AddMServCommands(void) static void WarnGUI (void) { #ifdef HAVE_THREADS - I_lock_mutex(&m_menu_mutex); + I_lock_mutex(&k_menu_mutex); #endif M_StartMessage(M_GetText("There was a problem connecting to\nthe Master Server\n\nCheck the console for details.\n"), NULL, MM_NOTHING); #ifdef HAVE_THREADS - I_unlock_mutex(m_menu_mutex); + I_unlock_mutex(k_menu_mutex); #endif } diff --git a/src/r_things.c b/src/r_things.c index e1e38041b..a85b72642 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -18,7 +18,7 @@ #include "st_stuff.h" #include "w_wad.h" #include "z_zone.h" -#include "m_menu.h" // character select +#include "k_menu.h" // character select #include "m_misc.h" #include "info.h" // spr2names #include "i_video.h" // rendermode diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 5b41985a8..0e1528bc2 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -181,7 +181,7 @@ static char returnWadPath[256]; #include "../m_argv.h" -#include "../m_menu.h" +#include "../k_menu.h" #ifdef MAC_ALERT #include "macosx/mac_alert.h" diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index e9e8b4c13..214ccb7bd 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -373,7 +373,7 @@ static boolean IgnoreMouse(void) if (cv_alwaysgrabmouse.value) return false; if (menuactive) - return !M_MouseNeeded(); + return false; // return !M_MouseNeeded(); if (paused || con_destlines || chat_on) return true; if (gamestate != GS_LEVEL && gamestate != GS_INTERMISSION && diff --git a/src/v_video.c b/src/v_video.c index 56584795e..3e98c3429 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -2542,145 +2542,6 @@ INT32 V_LevelNameHeight(const char *string) return w; } -// -// Find string width from hu_font chars -// -INT32 V_StringWidth(const char *string, INT32 option) -{ - INT32 c, w = 0; - INT32 spacewidth = 4, charwidth = 0; - size_t i; - - switch (option & V_SPACINGMASK) - { - case V_MONOSPACE: - spacewidth = 8; - /* FALLTHRU */ - case V_OLDSPACING: - charwidth = 8; - break; - case V_6WIDTHSPACE: - spacewidth = 6; - default: - break; - } - - for (i = 0; i < strlen(string); i++) - { - c = string[i]; - if ((UINT8)c & 0x80) //color parsing! -Inuyasha 2.16.09 - continue; - - c = toupper(c) - HU_FONTSTART; - if (c < 0 || c >= HU_FONTSIZE || !fontv[HU_FONT].font[c]) - w += spacewidth; - else - w += (charwidth ? charwidth : SHORT(fontv[HU_FONT].font[c]->width)); - } - - if (option & (V_NOSCALESTART|V_NOSCALEPATCH)) - w *= vid.dupx; - - return w; -} - -// -// Find string width from hu_font chars, 0.5x scale -// -INT32 V_SmallStringWidth(const char *string, INT32 option) -{ - INT32 c, w = 0; - INT32 spacewidth = 2, charwidth = 0; - size_t i; - - switch (option & V_SPACINGMASK) - { - case V_MONOSPACE: - spacewidth = 4; - /* FALLTHRU */ - case V_OLDSPACING: - charwidth = 4; - break; - case V_6WIDTHSPACE: - spacewidth = 3; - default: - break; - } - - for (i = 0; i < strlen(string); i++) - { - c = string[i]; - if ((UINT8)c & 0x80) //color parsing! -Inuyasha 2.16.09 - continue; - - c = toupper(c) - HU_FONTSTART; - if (c < 0 || c >= HU_FONTSIZE || !fontv[HU_FONT].font[c]) - w += spacewidth; - else - w += (charwidth ? charwidth : SHORT(fontv[HU_FONT].font[c]->width)/2); - } - - return w; -} - -// -// Find string width from tny_font chars -// -INT32 V_ThinStringWidth(const char *string, INT32 option) -{ - INT32 c, w = 0; - INT32 spacewidth = 2, charwidth = 0; - boolean lowercase = (option & V_ALLOWLOWERCASE); - size_t i; - - switch (option & V_SPACINGMASK) - { - case V_MONOSPACE: - spacewidth = 5; - /* FALLTHRU */ - case V_OLDSPACING: - charwidth = 5; - break; - // Out of video flags, so we're reusing this for alternate charwidth instead - /*case V_6WIDTHSPACE: - spacewidth = 3;*/ - default: - break; - } - - for (i = 0; i < strlen(string); i++) - { - c = string[i]; - if ((UINT8)c & 0x80) //color parsing! -Inuyasha 2.16.09 - continue; - - if (!lowercase || !fontv[TINY_FONT].font[c-HU_FONTSTART]) - c = toupper(c); - c -= HU_FONTSTART; - - if (c < 0 || c >= HU_FONTSIZE || !fontv[TINY_FONT].font[c]) - w += spacewidth; - else - { - w += (charwidth ? charwidth - : ((option & V_6WIDTHSPACE && i < strlen(string)-1) ? max(1, SHORT(fontv[TINY_FONT].font[c]->width)-1) // Reuse this flag for the alternate bunched-up spacing - : SHORT(fontv[TINY_FONT].font[c]->width))); - } - } - - - return w; -} - -// -// Find string width from tny_font chars, 0.5x scale -// -INT32 V_SmallThinStringWidth(const char *string, INT32 option) -{ - INT32 w = V_ThinStringWidth(string, option)<>1,option,HU_FONT,string) + V__DrawDupxString (x,y,FRACUNIT>>1,option,NULL,HU_FONT,string) + +#define V_SmallStringWidth( string,option ) \ + V__IntegerStringWidth ( FRACUNIT>>1,option,HU_FONT,string ) + void V_DrawCenteredSmallString(INT32 x, INT32 y, INT32 option, const char *string); void V_DrawRightAlignedSmallString(INT32 x, INT32 y, INT32 option, const char *string); diff --git a/src/y_inter.c b/src/y_inter.c index 051ecfe60..1885d9b1f 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -43,7 +43,6 @@ #include "g_input.h" // PlayerInputDown #include "k_battle.h" #include "k_pwrlv.h" -#include "lua_hook.h" // IntermissionThinker hook #include "k_grandprix.h" #ifdef HWRENDER @@ -86,25 +85,11 @@ static patch_t *bgpatch = NULL; // INTERSCR static patch_t *widebgpatch = NULL; static patch_t *bgtile = NULL; // SPECTILE/SRB2BACK static patch_t *interpic = NULL; // custom picture defined in map header -static boolean usetile; static INT32 timer; -typedef struct -{ - INT32 source_width, source_height; - INT32 source_bpp, source_rowbytes; - UINT8 *source_picture; - INT32 target_width, target_height; - INT32 target_bpp, target_rowbytes; - UINT8 *target_picture; -} y_buffer_t; - -boolean usebuffer = false; -static boolean useinterpic; static INT32 timer; static INT32 powertype = PWRLV_DISABLED; static boolean safetorender = true; -static y_buffer_t *y_buffer; static INT32 intertic; static INT32 endtic = -1; @@ -114,7 +99,6 @@ intertype_t intertype = int_none; intertype_t intermissiontypes[NUMGAMETYPES]; static void Y_FollowIntermission(void); -static void Y_RescaleScreenBuffer(void); static void Y_UnloadData(void); static void Y_CleanupData(void); @@ -335,91 +319,6 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32)) } } -// -// Y_ConsiderScreenBuffer -// -// Can we copy the current screen to a buffer? -// -void Y_ConsiderScreenBuffer(void) -{ - if (gameaction != ga_completed) - return; - - if (y_buffer == NULL) - y_buffer = Z_Calloc(sizeof(y_buffer_t), PU_STATIC, NULL); - else - return; - - y_buffer->source_width = vid.width; - y_buffer->source_height = vid.height; - y_buffer->source_bpp = vid.bpp; - y_buffer->source_rowbytes = vid.rowbytes; - y_buffer->source_picture = ZZ_Alloc(y_buffer->source_width*vid.bpp * y_buffer->source_height); - VID_BlitLinearScreen(screens[1], y_buffer->source_picture, vid.width*vid.bpp, vid.height, vid.width*vid.bpp, vid.rowbytes); - - // Make the rescaled screen buffer - Y_RescaleScreenBuffer(); -} - -// -// Y_RescaleScreenBuffer -// -// Write the rescaled source picture, to the destination picture that has the current screen's resolutions. -// -static void Y_RescaleScreenBuffer(void) -{ - INT32 sx, sy; // source - INT32 dx, dy; // dest - fixed_t scalefac, yscalefac; - fixed_t rowfrac, colfrac; - UINT8 *dest; - - // Who knows? - if (y_buffer == NULL) - return; - - if (y_buffer->target_picture) - Z_Free(y_buffer->target_picture); - - y_buffer->target_width = vid.width; - y_buffer->target_height = vid.height; - y_buffer->target_rowbytes = vid.rowbytes; - y_buffer->target_bpp = vid.bpp; - y_buffer->target_picture = ZZ_Alloc(y_buffer->target_width*vid.bpp * y_buffer->target_height); - dest = y_buffer->target_picture; - - scalefac = FixedDiv(y_buffer->target_width*FRACUNIT, y_buffer->source_width*FRACUNIT); - yscalefac = FixedDiv(y_buffer->target_height*FRACUNIT, y_buffer->source_height*FRACUNIT); - - rowfrac = FixedDiv(FRACUNIT, yscalefac); - colfrac = FixedDiv(FRACUNIT, scalefac); - - for (sy = 0, dy = 0; sy < (y_buffer->source_height << FRACBITS) && dy < y_buffer->target_height; sy += rowfrac, dy++) - for (sx = 0, dx = 0; sx < (y_buffer->source_width << FRACBITS) && dx < y_buffer->target_width; sx += colfrac, dx += y_buffer->target_bpp) - dest[(dy * y_buffer->target_rowbytes) + dx] = y_buffer->source_picture[((sy>>FRACBITS) * y_buffer->source_width) + (sx>>FRACBITS)]; -} - -// -// Y_CleanupScreenBuffer -// -// Free all related memory. -// -void Y_CleanupScreenBuffer(void) -{ - // Who knows? - if (y_buffer == NULL) - return; - - if (y_buffer->target_picture) - Z_Free(y_buffer->target_picture); - - if (y_buffer->source_picture) - Z_Free(y_buffer->source_picture); - - Z_Free(y_buffer); - y_buffer = NULL; -} - // // Y_IntermissionDrawer // @@ -441,52 +340,18 @@ void Y_IntermissionDrawer(void) } if (!safetorender) - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); - - if (!safetorender) - goto dontdrawbg; - - if (useinterpic) - V_DrawScaledPatch(0, 0, 0, interpic); - else if (!usetile) { - if (rendermode == render_soft && usebuffer) - { - // no y_buffer - if (y_buffer == NULL) - VID_BlitLinearScreen(screens[1], screens[0], vid.width*vid.bpp, vid.height, vid.width*vid.bpp, vid.rowbytes); - else - { - // Maybe the resolution changed? - if ((y_buffer->target_width != vid.width) || (y_buffer->target_height != vid.height)) - Y_RescaleScreenBuffer(); - - // Blit the already-scaled screen buffer to the current screen - VID_BlitLinearScreen(y_buffer->target_picture, screens[0], vid.width*vid.bpp, vid.height, vid.width*vid.bpp, vid.rowbytes); - } - } -#ifdef HWRENDER - else if (rendermode != render_soft && usebuffer) - HWR_DrawIntermissionBG(); -#endif - else if (bgpatch) - { - fixed_t hs = vid.width * FRACUNIT / BASEVIDWIDTH; - fixed_t vs = vid.height * FRACUNIT / BASEVIDHEIGHT; - V_DrawStretchyFixedPatch(0, 0, hs, vs, V_NOSCALEPATCH, bgpatch, NULL); - } + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + } + else + { + M_DrawMenuBackground(); } - else if (bgtile) - V_DrawPatchFill(bgtile); -dontdrawbg: LUAh_IntermissionHUD(); if (!LUA_HudEnabled(hud_intermissiontally)) goto skiptallydrawer; - if (usebuffer) // Fade everything out - V_DrawFadeScreen(0xFF00, 22); - if (!r_splitscreen) whiteplayer = demo.playback ? displayplayers[0] : consoleplayer; @@ -1145,8 +1010,6 @@ void Y_StartIntermission(void) bgpatch = W_CachePatchName("MENUBG", PU_STATIC); widebgpatch = W_CachePatchName("WEIRDRES", PU_STATIC); - - useinterpic = usetile = usebuffer = false; } // ====== @@ -1161,7 +1024,6 @@ void Y_EndIntermission(void) endtic = -1; sorttic = -1; intertype = int_none; - usebuffer = false; } // diff --git a/src/y_inter.h b/src/y_inter.h index da29dd0f3..20fbde91e 100644 --- a/src/y_inter.h +++ b/src/y_inter.h @@ -15,9 +15,6 @@ void Y_Ticker(void); void Y_StartIntermission(void); void Y_EndIntermission(void); -void Y_ConsiderScreenBuffer(void); -void Y_CleanupScreenBuffer(void); - void Y_DetermineIntermissionType(void); void Y_VoteDrawer(void); From 521eff868cd92011dd2546ad92370cad398e9e4d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 11 Jan 2021 01:02:16 -0500 Subject: [PATCH 022/379] Chengi's new background added --- src/k_menudraw.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index f9c86ade3..8c8a9f897 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -85,9 +85,36 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #define SLIDER_WIDTH (8*SLIDER_RANGE+6) #define SERVERS_PER_PAGE 11 +static UINT32 bgTextScroll = 0; + void M_DrawMenuBackground(void) { - V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUBG", PU_CACHE), NULL); + patch_t *text1 = W_CachePatchName("MENUBGT1", PU_CACHE); + patch_t *text2 = W_CachePatchName("MENUBGT2", PU_CACHE); + + INT32 text1loop = SHORT(text1->height); + INT32 text2loop = SHORT(text2->width); + + fixed_t text1scroll = -(bgTextScroll % text1loop) * FRACUNIT; + fixed_t text2scroll = -(bgTextScroll % text2loop) * FRACUNIT; + + V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUBG4", PU_CACHE), NULL); + V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUBG2", PU_CACHE), NULL); + V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUBG1", PU_CACHE), NULL); + + V_DrawFixedPatch(0, BASEVIDHEIGHT * FRACUNIT, FRACUNIT, V_TRANSLUCENT, W_CachePatchName("MENUBG5", PU_CACHE), NULL); + + V_DrawFixedPatch(text2scroll, (BASEVIDHEIGHT-8) * FRACUNIT, + FRACUNIT, V_TRANSLUCENT, text2, NULL); + V_DrawFixedPatch(text2scroll + (text2loop * FRACUNIT), (BASEVIDHEIGHT-8) * FRACUNIT, + FRACUNIT, V_TRANSLUCENT, text2, NULL); + + V_DrawFixedPatch(8 * FRACUNIT, text1scroll, + FRACUNIT, V_TRANSLUCENT, text1, NULL); + V_DrawFixedPatch(8 * FRACUNIT, text1scroll + (text1loop * FRACUNIT), + FRACUNIT, V_TRANSLUCENT, text1, NULL); + + bgTextScroll += 8; } void M_DrawMenuForeground(void) From 8289bf252f6c7c06d352a2f567cd0d31afce4867 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 13 Jan 2021 01:16:14 -0500 Subject: [PATCH 023/379] Chengi's menu selectors - Menu BG image is defined by menu option - Menu BG image slides in - Gamemode menu options are now like Chengi's mockup --- src/hu_stuff.c | 3 +- src/k_menu.h | 1 + src/k_menudef.c | 20 +++++---- src/k_menudraw.c | 108 +++++++++++++++++++++++++++++++---------------- src/k_menufunc.c | 8 ++++ src/v_video.c | 2 + src/y_inter.c | 80 ++++++++++++++++++----------------- 7 files changed, 139 insertions(+), 83 deletions(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index e857691ca..b1bfe8eb9 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -268,10 +268,11 @@ void HU_Init(void) PR ("MKFNT"); REG; - ADIM (AZ); + ADIM (LT); PR ("GAMEM"); REG; + ADIM (AZ); PR ("FILEF"); REG; diff --git a/src/k_menu.h b/src/k_menu.h index 461dbccee..2a119c1e3 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -365,6 +365,7 @@ void M_ReplayHut(INT32 choice); // M_MENUDRAW.C +void M_UpdateMenuBGImage(boolean forceReset); void M_DrawMenuBackground(void); void M_DrawMenuForeground(void); void M_Drawer(void); diff --git a/src/k_menudef.c b/src/k_menudef.c index ea95e9b6a..b442878ef 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -18,17 +18,21 @@ // --------- menuitem_t MainMenu[] = { - {IT_STRING | IT_CALL, "Play", "Cut to the chase and start the race!", - NULL, M_CharacterSelectInit, 0, 0}, + {IT_STRING | IT_CALL, "Play", + "Cut to the chase and start the race!", NULL, + M_CharacterSelectInit, 0, 0}, - {IT_STRING, "Extra", "Check out some bonus features.", - NULL, NULL, 0, 0}, + {IT_STRING, "Extra", + "Check out some bonus features.", "MENUI001", + NULL, 0, 0}, - {IT_STRING, "Option", "Configure your controls, settings, and preferences.", - NULL, NULL, 0, 0}, + {IT_STRING, "Option", + "Configure your controls, settings, and preferences.", NULL, + NULL, 0, 0}, - {IT_STRING | IT_CALL, "Quit", "Exit SRB2Kart.", - NULL, M_QuitSRB2, 0, 0}, + {IT_STRING | IT_CALL, "Quit", + "Exit \"Dr. Robotnik's Ring Racers\".", NULL, + M_QuitSRB2, 0, 0}, }; menu_t MainDef = KARTGAMEMODEMENU(MainMenu, NULL); diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 8c8a9f897..e423c969c 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -86,6 +86,32 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #define SERVERS_PER_PAGE 11 static UINT32 bgTextScroll = 0; +static UINT32 bgImageScroll = 0; +static char bgImageName[9]; + +#define MENUBG_TEXTSCROLL 6 +#define MENUBG_IMAGESCROLL 32 + +void M_UpdateMenuBGImage(boolean forceReset) +{ + char oldName[9]; + + memcpy(oldName, bgImageName, 9); + + if (currentMenu->menuitems[itemOn].patch) + { + sprintf(bgImageName, "%s", currentMenu->menuitems[itemOn].patch); + } + else + { + sprintf(bgImageName, "MENUI000"); + } + + if (forceReset == false && strcmp(bgImageName, oldName)) + { + bgImageScroll = (BASEVIDWIDTH / 2) / MENUBG_IMAGESCROLL; + } +} void M_DrawMenuBackground(void) { @@ -95,14 +121,15 @@ void M_DrawMenuBackground(void) INT32 text1loop = SHORT(text1->height); INT32 text2loop = SHORT(text2->width); - fixed_t text1scroll = -(bgTextScroll % text1loop) * FRACUNIT; - fixed_t text2scroll = -(bgTextScroll % text2loop) * FRACUNIT; + fixed_t text1scroll = -((bgTextScroll * MENUBG_TEXTSCROLL) % text1loop) * FRACUNIT; + fixed_t text2scroll = -((bgTextScroll * MENUBG_TEXTSCROLL) % text2loop) * FRACUNIT; V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUBG4", PU_CACHE), NULL); - V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUBG2", PU_CACHE), NULL); - V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUBG1", PU_CACHE), NULL); - V_DrawFixedPatch(0, BASEVIDHEIGHT * FRACUNIT, FRACUNIT, V_TRANSLUCENT, W_CachePatchName("MENUBG5", PU_CACHE), NULL); + V_DrawFixedPatch(-(bgImageScroll * MENUBG_IMAGESCROLL) * FRACUNIT, 0, FRACUNIT, 0, W_CachePatchName("MENUBG1", PU_CACHE), NULL); + V_DrawFixedPatch(-(bgImageScroll * MENUBG_IMAGESCROLL) * FRACUNIT, 0, FRACUNIT, 0, W_CachePatchName(bgImageName, PU_CACHE), NULL); + + V_DrawFixedPatch(0, (BASEVIDHEIGHT + 16) * FRACUNIT, FRACUNIT, V_TRANSLUCENT, W_CachePatchName("MENUBG2", PU_CACHE), NULL); V_DrawFixedPatch(text2scroll, (BASEVIDHEIGHT-8) * FRACUNIT, FRACUNIT, V_TRANSLUCENT, text2, NULL); @@ -114,7 +141,12 @@ void M_DrawMenuBackground(void) V_DrawFixedPatch(8 * FRACUNIT, text1scroll + (text1loop * FRACUNIT), FRACUNIT, V_TRANSLUCENT, text1, NULL); - bgTextScroll += 8; + bgTextScroll++; + + if (bgImageScroll > 0) + { + bgImageScroll--; + } } void M_DrawMenuForeground(void) @@ -214,26 +246,6 @@ static void M_DrawMenuTooltips(void) } } -// -// M_DrawMenuPreviews -// -// Draw a box with a preview image of the current option -// -static void M_DrawMenuPreviews(void) -{ - V_DrawFixedPatch(172<menuitems[itemOn].patch == NULL) - { - patch_t *st = W_CachePatchName(va("MIMGST0%d", (skullAnimCounter % 4) + 1), PU_CACHE); - V_DrawFixedPatch(181<menuitems[itemOn].patch, PU_CACHE), NULL); - } -} - // 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) @@ -404,6 +416,11 @@ void M_DrawGenericMenu(void) } } +#define GM_STARTX 128 +#define GM_STARTY 80 +#define GM_XOFFSET 17 +#define GM_YOFFSET 34 + // // M_DrawKartGamemodeMenu // @@ -411,33 +428,52 @@ void M_DrawGenericMenu(void) // void M_DrawKartGamemodeMenu(void) { - INT16 i, x = 170; UINT8 n = currentMenu->numitems-1; + INT32 i, x = GM_STARTX - ((GM_XOFFSET / 2) * (n-1)), y = GM_STARTY - ((GM_YOFFSET / 2) * (n-1)); M_DrawMenuTooltips(); - M_DrawMenuPreviews(); if (menutransition.tics) - x -= 24 * menutransition.tics; + { + x += 24 * menutransition.tics; + } for (i = 0; i < currentMenu->numitems; i++) { - INT16 y; + if (i >= n) + { + x = GM_STARTX + (GM_XOFFSET * 5 / 2); + y = GM_STARTY + (GM_YOFFSET * 5 / 2); - if (i == n) - y = 160; - else - y = 80 - (16 * (n-1)) + (32 * i); + if (menutransition.tics) + { + x += 24 * menutransition.tics; + } + } switch (currentMenu->menuitems[i].status & IT_DISPLAY) { case IT_STRING: { - UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, (i == itemOn) ? SKINCOLOR_PLAGUE : SKINCOLOR_PIGEON, GTC_CACHE); - V_DrawRightAlignedGamemodeString(x, y, 0, colormap, currentMenu->menuitems[i].text); + UINT8 *colormap = NULL; + + if (i == itemOn) + { + colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE); + } + else + { + colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_MOSS, GTC_CACHE); + } + + V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, W_CachePatchName("MENUPLTR", PU_CACHE), colormap); + V_DrawGamemodeString(x + 16, y - 3, V_ALLOWLOWERCASE, colormap, currentMenu->menuitems[i].text); } break; } + + x += GM_XOFFSET; + y += GM_YOFFSET; } } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 89508cc63..e2669667c 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -663,6 +663,8 @@ static void M_NextOpt(void) else itemOn++; } while (oldItemOn != itemOn && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_SPACE); + + M_UpdateMenuBGImage(false); } static void M_PrevOpt(void) @@ -679,6 +681,8 @@ static void M_PrevOpt(void) else itemOn--; } while (oldItemOn != itemOn && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_SPACE); + + M_UpdateMenuBGImage(false); } // @@ -1411,6 +1415,8 @@ void M_SetupNextMenu(menu_t *menudef, boolean notransition) } } } + + M_UpdateMenuBGImage(false); } void M_GoBack(INT32 choice) @@ -1542,6 +1548,8 @@ void M_Init(void) CV_RegisterVar(&cv_dummyscramble); CV_RegisterVar(&cv_dummystaff); + M_UpdateMenuBGImage(true); + #if 0 #ifdef HWRENDER // Permanently hide some options based on render mode diff --git a/src/v_video.c b/src/v_video.c index 3e98c3429..5e1ca6805 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -1973,6 +1973,8 @@ void V_DrawStringScaled( } break; case GM_FONT: + spacew = 6; + break; case FILE_FONT: spacew = 0; break; diff --git a/src/y_inter.c b/src/y_inter.c index 1885d9b1f..9941eeaf0 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -571,51 +571,53 @@ void Y_IntermissionDrawer(void) } skiptallydrawer: - if (!LUA_HudEnabled(hud_intermissionmessages)) - return; - - if (timer && grandprixinfo.gp == false) + if (LUA_HudEnabled(hud_intermissionmessages)) { - char *string; - INT32 tickdown = (timer+1)/TICRATE; - - if (multiplayer && demo.playback) - string = va("Replay ends in %d", tickdown); - else - string = va("%s starts in %d", cv_advancemap.string, tickdown); - - V_DrawCenteredString(BASEVIDWIDTH/2, 188, hilicol, - string); - } - - if ((demo.recording || demo.savemode == DSM_SAVED) && !demo.playback) - switch (demo.savemode) + if (timer && grandprixinfo.gp == false) { - case DSM_NOTSAVING: - V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|hilicol, "Look Backward: Save replay"); - break; + char *string; + INT32 tickdown = (timer+1)/TICRATE; - case DSM_SAVED: - V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|hilicol, "Replay saved!"); - break; + if (multiplayer && demo.playback) + string = va("Replay ends in %d", tickdown); + else + string = va("%s starts in %d", cv_advancemap.string, tickdown); - case DSM_TITLEENTRY: - ST_DrawDemoTitleEntry(); - break; - - default: // Don't render any text here - break; + V_DrawCenteredString(BASEVIDWIDTH/2, 188, hilicol, + string); } - //if ((intertic/TICRATE) & 1) // Make it obvious that scrambling is happening next round. (OR NOT, I GUESS) - //{ - /*if (cv_scrambleonchange.value && cv_teamscramble.value) - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, hilicol, M_GetText("Teams will be scrambled next round!"));*/ + if ((demo.recording || demo.savemode == DSM_SAVED) && !demo.playback) + switch (demo.savemode) + { + case DSM_NOTSAVING: + V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|hilicol, "Look Backward: Save replay"); + break; - if (speedscramble != -1 && speedscramble != gamespeed) - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24, hilicol|V_ALLOWLOWERCASE|V_SNAPTOBOTTOM, - va(M_GetText("Next race will be %s Speed!"), kartspeed_cons_t[1+speedscramble].strvalue)); - //} + case DSM_SAVED: + V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|hilicol, "Replay saved!"); + break; + + case DSM_TITLEENTRY: + ST_DrawDemoTitleEntry(); + break; + + default: // Don't render any text here + break; + } + + //if ((intertic/TICRATE) & 1) // Make it obvious that scrambling is happening next round. (OR NOT, I GUESS) + //{ + /*if (cv_scrambleonchange.value && cv_teamscramble.value) + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, hilicol, M_GetText("Teams will be scrambled next round!"));*/ + + if (speedscramble != -1 && speedscramble != gamespeed) + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24, hilicol|V_ALLOWLOWERCASE|V_SNAPTOBOTTOM, + va(M_GetText("Next race will be %s Speed!"), kartspeed_cons_t[1+speedscramble].strvalue)); + //} + } + + M_DrawMenuForeground(); } // @@ -1010,6 +1012,8 @@ void Y_StartIntermission(void) bgpatch = W_CachePatchName("MENUBG", PU_STATIC); widebgpatch = W_CachePatchName("WEIRDRES", PU_STATIC); + + M_UpdateMenuBGImage(true); } // ====== From 04bd3efd5b1a3ea8796f36b4463ab43cce17cb1e Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 13 Nov 2021 18:08:34 +0100 Subject: [PATCH 024/379] First attempt at multiplayer menus --- src/k_menu.h | 36 ++++++++++++++++++++++++++ src/k_menudef.c | 42 ++++++++++++++++++++++++++++-- src/k_menudraw.c | 58 ++++++++++++++++++++++++++++++++++++++++++ src/k_menufunc.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 200 insertions(+), 2 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 2a119c1e3..20ec2663d 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -180,6 +180,12 @@ extern menu_t PLAY_LevelSelectDef; extern menuitem_t PLAY_TimeAttack[]; extern menu_t PLAY_TimeAttackDef; +extern menuitem_t PLAY_MP_OptSelect[]; +extern menu_t PLAY_MP_OptSelectDef; + +extern menuitem_t PLAY_MP_RoomSelect[]; +extern menu_t PLAY_MP_RoomSelectDef; + extern menuitem_t PLAY_BattleGamemodesMenu[]; extern menu_t PLAY_BattleGamemodesDef; @@ -348,6 +354,31 @@ void M_CupSelectTick(void); void M_LevelSelectHandler(INT32 choice); void M_LevelSelectTick(void); +// Multiplayer menu stuff + +// Keep track of multiplayer menu related data +// We'll add more stuff here as we need em... + +extern struct mpmenu_s { + UINT8 modechoice; + + UINT8 room; + + tic_t ticker; +} mpmenu; + +// MP selection +void M_MPOptSelect(INT32 choice); +void M_MPOptSelectInit(INT32 choice); +void M_MPOptSelectTick(void); + +// Server browser room selection +void M_MPRoomSelect(INT32 choice); +void M_MPRoomSelectTick(void); +void M_MPRoomSelectInit(INT32 choice); + +// Replay Playback + extern tic_t playback_last_menu_interaction_leveltime; void M_EndModeAttackRun(void); @@ -381,6 +412,11 @@ void M_DrawCupSelect(void); void M_DrawLevelSelect(void); void M_DrawTimeAttack(void); +// Multiplayer menu stuff +void M_DrawMPOptSelect(void); +void M_DrawMPRoomSelect(void); + +// Replay Playback void M_DrawPlaybackMenu(void); // These defines make it a little easier to make menus diff --git a/src/k_menudef.c b/src/k_menudef.c index b442878ef..25420d7b9 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -63,8 +63,8 @@ menuitem_t PLAY_MainMenu[] = {IT_STRING | IT_SUBMENU, "Local Play", "Play only on this computer.", NULL, &PLAY_GamemodesDef, 0, 0}, - {IT_STRING, "Online", "Connect to other computers.", - NULL, NULL, 0, 0}, + {IT_STRING | IT_CALL, "Online", "Connect to other computers.", + NULL, M_MPOptSelectInit, /*M_MPRoomSelectInit,*/ 0, 0}, {IT_STRING | IT_CALL, "Back", NULL, NULL, M_GoBack, 0, 0}, }; @@ -171,6 +171,44 @@ menuitem_t PLAY_BattleGamemodesMenu[] = menu_t PLAY_BattleGamemodesDef = KARTGAMEMODEMENU(PLAY_BattleGamemodesMenu, &PLAY_GamemodesDef); +// MULTIPLAYER OPTION SELECT +menuitem_t PLAY_MP_OptSelect[] = +{ + {IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, M_MPOptSelect, 0, 0}, +}; + +menu_t PLAY_MP_OptSelectDef = { + sizeof (PLAY_MP_OptSelect) / sizeof (menuitem_t), + &PLAY_MainDef, + 0, + PLAY_MP_OptSelect, + 0, 0, + 0, 0, + M_DrawMPOptSelect, + M_MPOptSelectTick, + NULL +}; + + +// MULTIPLAYER ROOM SELECT (CORE / MODDED) +menuitem_t PLAY_MP_RoomSelect[] = +{ + {IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, M_MPRoomSelect, 0, 0}, +}; + +menu_t PLAY_MP_RoomSelectDef = { + sizeof (PLAY_MP_RoomSelect) / sizeof (menuitem_t), + &PLAY_MP_OptSelectDef, + 0, + PLAY_MP_RoomSelect, + 0, 0, + 0, 0, + M_DrawMPRoomSelect, + M_MPRoomSelectTick, + NULL +}; + + // ------------------- // In-game/pause menus // ------------------- diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 291fff60b..9632b9f3f 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1305,6 +1305,64 @@ void M_DrawTimeAttack(void) } } +// Multiplayer mode option select +void M_DrawMPOptSelect(void) +{ + + patch_t *background = W_CachePatchName("M_EGGACH", PU_CACHE); + + V_DrawFill(0, 0, 999, 999, 25); + V_DrawFixedPatch(160<height)) + soffy)*FRACUNIT , FRACUNIT, V_ADD, drawp, NULL); + soffy += scrollp[mpmenu.room]->height; + } + + + // Draw buttons: + V_DrawFixedPatch(160<prevMenu) + M_SetupNextMenu(currentMenu->prevMenu, false); + else + M_ClearMenus(true); + break; + } + + } +} + +void M_MPRoomSelectTick(void) +{ + mpmenu.ticker++; +} + +void M_MPRoomSelectInit(INT32 choice) +{ + mpmenu.room = 0; + mpmenu.ticker = 0; + + M_SetupNextMenu(&PLAY_MP_RoomSelectDef, false); +} + // ===================== // PAUSE / IN-GAME MENUS // ===================== From feb0868292ce82b43d0aabbbba1e0745d369b0fa Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 13 Nov 2021 19:32:12 +0100 Subject: [PATCH 025/379] Small continuation of the MP menus, this allows us to reach CORE/MODDED split --- src/k_menu.h | 2 +- src/k_menudef.c | 10 ++++++++- src/k_menudraw.c | 56 +++++++++++++++++++++++++++++++++++++++++++++--- src/k_menufunc.c | 6 +++--- 4 files changed, 66 insertions(+), 8 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 20ec2663d..137e8c562 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -360,7 +360,7 @@ void M_LevelSelectTick(void); // We'll add more stuff here as we need em... extern struct mpmenu_s { - UINT8 modechoice; + UINT8 modechoice; UINT8 room; diff --git a/src/k_menudef.c b/src/k_menudef.c index 25420d7b9..0f4de555b 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -174,7 +174,15 @@ menu_t PLAY_BattleGamemodesDef = KARTGAMEMODEMENU(PLAY_BattleGamemodesMenu, &PLA // MULTIPLAYER OPTION SELECT menuitem_t PLAY_MP_OptSelect[] = { - {IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, M_MPOptSelect, 0, 0}, + //{IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, M_MPOptSelect, 0, 0}, + {IT_STRING | IT_CALL, "Host Game", "Start your own online game!", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CALL, "Join by IP", "Join an online game by its IP address.", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CALL, "Server Browser", "Search for game servers to play in.", + NULL, M_MPRoomSelectInit, 0, 0}, }; menu_t PLAY_MP_OptSelectDef = { diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 9632b9f3f..8eb55b040 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1305,14 +1305,64 @@ void M_DrawTimeAttack(void) } } +// This draws the options of a given menu in a fashion specific to the multiplayer option select screen (host game / server browser etc) +// TODO: Add 2nd argument that lets us "expand" a given option via an array + +static void M_MPOptDrawer(menu_t *m) +{ + // This is a copypaste of the generic gamemode menu code with a few changes. + // TODO: Allow specific options to "expand" into smaller ones. + + patch_t *buttback = W_CachePatchName("M_PLAT2", PU_CACHE); + + UINT8 n = m->numitems-1; + INT32 i, x = 142, y = 32; // Dirty magic numbers for now but they work out. + + if (menutransition.tics) + { + x += 24 * menutransition.tics; + } + + for (i = 0; i < m->numitems; i++) + { + + switch (m->menuitems[i].status & IT_DISPLAY) + { + case IT_STRING: + { + UINT8 *colormap = NULL; + + if (i == itemOn) + { + colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE); + } + else + { + colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_MOSS, GTC_CACHE); + } + + V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, buttback, colormap); + V_DrawCenteredGamemodeString(x, y - 3, V_ALLOWLOWERCASE, colormap, m->menuitems[i].text); + } + break; + } + + x += GM_XOFFSET; + y += GM_YOFFSET; + } +} + // Multiplayer mode option select void M_DrawMPOptSelect(void) { - + patch_t *background = W_CachePatchName("M_EGGACH", PU_CACHE); - + V_DrawFill(0, 0, 999, 999, 25); - V_DrawFixedPatch(160< Date: Sat, 13 Nov 2021 19:36:42 +0100 Subject: [PATCH 026/379] autoload newmenus.pk3 --- src/d_main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/d_main.c b/src/d_main.c index 4f3f7e969..44c2673ee 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1043,8 +1043,12 @@ static void IdentifyVersion(void) D_AddFile(startupiwads, va(pandf,srb2waddir,"textures.pk3")); D_AddFile(startupiwads, va(pandf,srb2waddir,"chars.pk3")); D_AddFile(startupiwads, va(pandf,srb2waddir,"maps.pk3")); + #ifdef USE_PATCH_FILE D_AddFile(startupiwads, va(pandf,srb2waddir,"patch.pk3")); + + // SPECIFIC HACK TO NEW-MENUS SO THAT MY DUMBASS STOPS FORGETTING TO ADD THE FILE (rip :youfuckedup:) + D_AddFile(startupiwads, va(pandf,srb2waddir,"newmenus.pk3")); #endif #if !defined (HAVE_SDL) || defined (HAVE_MIXER) @@ -1288,8 +1292,11 @@ void D_SRB2Main(void) mainwads++; // textures.pk3 mainwads++; // chars.pk3 mainwads++; // maps.pk3 + #ifdef USE_PATCH_FILE mainwads++; // patch.pk3 + // TODO: DON'T FORGET TO REMOVE THIS ONCE WE DON'T NEED IT ANYMORE. + mainwads++; // newmenus.pk3 #endif #endif //ifndef DEVELOP From fdf778c72fdc13fd0aa77b947bd4777512883e58 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sun, 14 Nov 2021 21:24:09 +0100 Subject: [PATCH 027/379] Allow hosting and joining by IP --- src/k_menu.h | 27 ++++++- src/k_menudef.c | 63 +++++++++++++++- src/k_menudraw.c | 191 ++++++++++++++++++++++++++++++++++++++++++----- src/k_menufunc.c | 175 ++++++++++++++++++++++++++++++++++++------- 4 files changed, 407 insertions(+), 49 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 137e8c562..0c59bfb19 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -183,6 +183,12 @@ extern menu_t PLAY_TimeAttackDef; extern menuitem_t PLAY_MP_OptSelect[]; extern menu_t PLAY_MP_OptSelectDef; +extern menuitem_t PLAY_MP_Host[]; +extern menu_t PLAY_MP_HostDef; + +extern menuitem_t PLAY_MP_JoinIP[]; +extern menu_t PLAY_MP_JoinIPDef; + extern menuitem_t PLAY_MP_RoomSelect[]; extern menu_t PLAY_MP_RoomSelectDef; @@ -331,7 +337,8 @@ extern struct cupgrid_s { SINT8 pageno; UINT8 numpages; tic_t previewanim; - boolean grandprix; // Setup grand prix server after picking + boolean grandprix; // Setup grand prix server after picking + boolean netgame; // Start the game in an actual server } cupgrid; extern struct levellist_s { @@ -342,6 +349,7 @@ extern struct levellist_s { INT16 choosemap; UINT8 newgametype; boolean timeattack; // Setup time attack menu after picking + boolean netgame; // Start the game in an actual server } levellist; boolean M_CanShowLevelInList(INT16 mapnum, UINT8 gt); @@ -361,6 +369,9 @@ void M_LevelSelectTick(void); extern struct mpmenu_s { UINT8 modechoice; + INT16 modewinextend[3][3]; // Used to "extend" the options in the mode select screen. + // format for each option: {extended?, max extension, # lines extended} + // See M_OptSelectTick, it'll make more sense there. Sorry if this is a bit of a mess! UINT8 room; @@ -371,6 +382,18 @@ extern struct mpmenu_s { void M_MPOptSelect(INT32 choice); void M_MPOptSelectInit(INT32 choice); void M_MPOptSelectTick(void); +boolean M_MPResetOpts(void); +consvar_t cv_dummygametype; // lazy hack to allow us to select the GT on the server host submenu +consvar_t cv_dummyip; // I HAVE + // HAVE YOUR IP ADDRESS (This just the hack Cvar we'll type into and then it apends itself to "connect" in the console for IP join) + +// MP Host +void M_MPHostInit(INT32 choice); +void M_MPSetupNetgameMapSelect(INT32 choice); + +// MP join by IP +void M_MPJoinIPInit(INT32 choice); +void M_JoinIP(void); // Server browser room selection void M_MPRoomSelect(INT32 choice); @@ -414,6 +437,8 @@ void M_DrawTimeAttack(void); // Multiplayer menu stuff void M_DrawMPOptSelect(void); +void M_DrawMPHost(void); +void M_DrawMPJoinIP(void); void M_DrawMPRoomSelect(void); // Replay Playback diff --git a/src/k_menudef.c b/src/k_menudef.c index 0f4de555b..987bd6e26 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -176,10 +176,10 @@ menuitem_t PLAY_MP_OptSelect[] = { //{IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, M_MPOptSelect, 0, 0}, {IT_STRING | IT_CALL, "Host Game", "Start your own online game!", - NULL, NULL, 0, 0}, + NULL, M_MPHostInit, 0, 0}, {IT_STRING | IT_CALL, "Join by IP", "Join an online game by its IP address.", - NULL, NULL, 0, 0}, + NULL, M_MPJoinIPInit, 0, 0}, {IT_STRING | IT_CALL, "Server Browser", "Search for game servers to play in.", NULL, M_MPRoomSelectInit, 0, 0}, @@ -191,12 +191,69 @@ menu_t PLAY_MP_OptSelectDef = { 0, PLAY_MP_OptSelect, 0, 0, - 0, 0, + -1, 1, M_DrawMPOptSelect, M_MPOptSelectTick, NULL }; +// MULTIPLAYER HOST SCREEN +menuitem_t PLAY_MP_Host[] = +{ + //{IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, M_MPOptSelect, 0, 0}, + + {IT_STRING | IT_CVAR | IT_CV_STRING, "Server Name", "Display name for your game online. Other players will see this.", + NULL, &cv_servername, 0, 0}, + + {IT_STRING | IT_CVAR, "Public Server", "Display or not your game in the Server Browser for other players.", + NULL, &cv_advertise, 0, 0}, + + {IT_STRING | IT_CVAR, "Max. Players", "Set how many players can play at once. Others will spectate.", + NULL, &cv_ingamecap, 0, 0}, + + {IT_STRING | IT_CVAR, "Gamemode", "Are we racing? Or perhaps battling?", + NULL, &cv_dummygametype, 0, 0}, + + {IT_STRING | IT_CALL, "GO", "Select a map with the currently selected gamemode", + NULL, M_MPSetupNetgameMapSelect, 0, 0}, + +}; + +menu_t PLAY_MP_HostDef = { + sizeof (PLAY_MP_Host) / sizeof (menuitem_t), + &PLAY_MP_OptSelectDef, + 0, + PLAY_MP_Host, + 0, 0, + -1, 1, // 1 frame transition.... This is really just because I don't want the black fade when we press esc, hehe + M_DrawMPHost, + M_MPOptSelectTick, // This handles the unfolding options + M_MPResetOpts +}; + +// MULTIPLAYER JOIN BY IP +menuitem_t PLAY_MP_JoinIP[] = +{ + //{IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, M_MPOptSelect, 0, 0}, + + {IT_STRING | IT_CVAR | IT_CV_STRING, "Address: ", "Type the IPv4 address of the server you wish to connect to.", + NULL, &cv_dummyip, 0, 0}, + + {IT_STRING | IT_CALL, "GO", "Select a map with the currently selected gamemode", + NULL, M_JoinIP, 0, 0}, +}; + +menu_t PLAY_MP_JoinIPDef = { + sizeof (PLAY_MP_JoinIP) / sizeof (menuitem_t), + &PLAY_MP_OptSelectDef, + 0, + PLAY_MP_JoinIP, + 0, 0, + -1, 1, // 1 frame transition.... This is really just because I don't want the black fade when we press esc, hehe + M_DrawMPJoinIP, + M_MPOptSelectTick, // This handles the unfolding options + M_MPResetOpts +}; // MULTIPLAYER ROOM SELECT (CORE / MODDED) menuitem_t PLAY_MP_RoomSelect[] = diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 8eb55b040..3c178b654 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1306,23 +1306,19 @@ void M_DrawTimeAttack(void) } // This draws the options of a given menu in a fashion specific to the multiplayer option select screen (host game / server browser etc) -// TODO: Add 2nd argument that lets us "expand" a given option via an array +// First argument is the menu to draw the options from, the 2nd one is a table that contains which option is to be "extendded" +// Format for each option: {extended? (only used by the ticker), max extension (likewise), # of lines to extend} -static void M_MPOptDrawer(menu_t *m) +// NOTE: This is pretty rigid and only intended for use with the multiplayer options menu which has *3* choices. + +static void M_MPOptDrawer(menu_t *m, INT16 extend[3][3]) { // This is a copypaste of the generic gamemode menu code with a few changes. // TODO: Allow specific options to "expand" into smaller ones. patch_t *buttback = W_CachePatchName("M_PLAT2", PU_CACHE); - - UINT8 n = m->numitems-1; INT32 i, x = 142, y = 32; // Dirty magic numbers for now but they work out. - if (menutransition.tics) - { - x += 24 * menutransition.tics; - } - for (i = 0; i < m->numitems; i++) { @@ -1331,8 +1327,9 @@ static void M_MPOptDrawer(menu_t *m) case IT_STRING: { UINT8 *colormap = NULL; + INT16 j = 0; - if (i == itemOn) + if ((currentMenu == m && i == itemOn) || extend[i][0]) // Selected / unfolded { colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE); } @@ -1341,28 +1338,184 @@ static void M_MPOptDrawer(menu_t *m) colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_MOSS, GTC_CACHE); } - V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, buttback, colormap); + if (extend[i][2]) + { + for (j=0; j < extend[i][2]/2; j++) + { + // Draw rectangles that look like the current selected item starting from the top of the actual selection graphic and going up to where it's supposed to go. + // With colour 169 (that's the index of the shade of black the plague colourization gives us. ...No I don't like using a magic number either. + V_DrawFill(x + (extend[i][2]/2) - j - (buttback->width/2), (y + extend[i][2]) - (2*j), 225, 2, 169); + } + } + V_DrawFixedPatch((x + (extend[i][2]/2)) *FRACUNIT, (y + extend[i][2])*FRACUNIT, FRACUNIT, 0, buttback, colormap); V_DrawCenteredGamemodeString(x, y - 3, V_ALLOWLOWERCASE, colormap, m->menuitems[i].text); } break; } x += GM_XOFFSET; - y += GM_YOFFSET; + y += GM_YOFFSET + extend[i][2]; } } +// Draws the EGGA CHANNEL background. +static void M_DrawEggaChannel(void) +{ + patch_t *background = W_CachePatchName("M_EGGACH", PU_CACHE); + + V_DrawFill(0, 0, 999, 999, 25); + V_DrawFixedPatch(160<numitems; i++) + { + + if (i == currentMenu->numitems-1) + { + + UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_MOSS, GTC_CACHE); + if (i == itemOn) + colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE); + + // Ideally we'd calculate this but it's not worth it for a 1-off menu probably..... + V_DrawFixedPatch(212<width/2), 100 -3, V_ALLOWLOWERCASE, colormap, currentMenu->menuitems[i].text); + } + else + { + switch (currentMenu->menuitems[i].status & IT_DISPLAY) + { + case IT_STRING: + { + V_DrawString(xp, yp, V_ALLOWLOWERCASE | (i == itemOn ? highlightflags : 0), 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_STRING: + V_DrawThinString(xp + 96, yp, V_ALLOWLOWERCASE, cv->string); + if (skullAnimCounter < 4 && i == itemOn) + V_DrawCharacter(xp + 93 + V_ThinStringWidth(cv->string, 0), yp +1, '_' | 0x80, false); + + break; + + default: + w = V_StringWidth(cv->string, 0); + V_DrawString(xp + 138 - w, yp, ((cv->flags & CV_CHEAT) && !CV_IsSetToDefault(cv) ? warningflags : highlightflags), cv->string); + if (i == itemOn) + { + V_DrawCharacter(xp + 138 - 10 - w - (skullAnimCounter/5), yp, '\x1C' | highlightflags, false); // left arrow + V_DrawCharacter(xp + 138 + 2 + (skullAnimCounter/5), yp, '\x1D' | highlightflags, false); // right arrow + } + break; + } + break; + } + } + + xp += 5; + yp += 11; + + break; + } + break; + } + + } + + } +} + +// Multiplayer mode option select: JOIN BY IP +// Once again we'll copypaste 1 bit of the generic menu handler we used for hosting but we only need it for IT_CV_STRING since that's all we got :) +// (I don't like duplicating code but I rather this than some horrible all-in-one function with too many options...) +void M_DrawMPJoinIP(void) +{ + + patch_t *gobutt = W_CachePatchName("M_BUTTGO", PU_CACHE); // I'm very mature + INT32 xp = 70, yp = 100, i = 0; // Starting position for the text drawing. + M_DrawMPOptSelect(); // Draw the Multiplayer option select menu first + + // Now draw our host options... + for (i = 0; i < currentMenu->numitems; i++) + { + + if (i == currentMenu->numitems-1) + { + + UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_MOSS, GTC_CACHE); + if (i == itemOn) + colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE); + + // Ideally we'd calculate this but it's not worth it for a 1-off menu probably..... + V_DrawFixedPatch(219<width/2), 114 -3, V_ALLOWLOWERCASE, colormap, currentMenu->menuitems[i].text); + } + else + { + switch (currentMenu->menuitems[i].status & IT_DISPLAY) + { + case IT_STRING: + { + V_DrawString(xp, yp, V_ALLOWLOWERCASE | (i == itemOn ? highlightflags : 0), 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_STRING: + V_DrawThinString(xp + 65, yp-1, V_ALLOWLOWERCASE, cv->string); + if (skullAnimCounter < 4 && i == itemOn) + V_DrawCharacter(xp + 65 + V_ThinStringWidth(cv->string, 0), yp, '_' | 0x80, false); + + break; + + default: + break; + } + break; + } + } + + xp += 5; + yp += 11; + + break; + } + break; + } + + } + + } } // Multiplayer room select diff --git a/src/k_menufunc.c b/src/k_menufunc.c index bd1890cf5..ae756c669 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -164,12 +164,15 @@ static CV_PossibleValue_t dummyteam_cons_t[] = {{0, "Spectator"}, {1, "Red"}, {2 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 dummystaff_cons_t[] = {{0, "MIN"}, {100, "MAX"}, {0, NULL}}; +static CV_PossibleValue_t dummygametype_cons_t[] = {{0, "Race"}, {1, "Battle"}, {0, NULL}}; static consvar_t cv_dummymenuplayer = CVAR_INIT ("dummymenuplayer", "P1", CV_HIDDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange); static consvar_t cv_dummyteam = CVAR_INIT ("dummyteam", "Spectator", CV_HIDDEN, dummyteam_cons_t, NULL); static consvar_t cv_dummyspectate = CVAR_INIT ("dummyspectate", "Spectator", CV_HIDDEN, dummyspectate_cons_t, NULL); static consvar_t cv_dummyscramble = CVAR_INIT ("dummyscramble", "Random", CV_HIDDEN, dummyscramble_cons_t, NULL); static consvar_t cv_dummystaff = CVAR_INIT ("dummystaff", "0", CV_HIDDEN|CV_CALL, dummystaff_cons_t, Dummystaff_OnChange); +consvar_t cv_dummygametype = CVAR_INIT ("dummygametype", "Race", CV_HIDDEN, dummygametype_cons_t, NULL); +consvar_t cv_dummyip = CVAR_INIT ("dummyip", "", CV_HIDDEN, NULL, NULL); // ========================================================================== // CVAR ONCHANGE EVENTS GO HERE @@ -1559,6 +1562,8 @@ void M_Init(void) CV_RegisterVar(&cv_dummyspectate); CV_RegisterVar(&cv_dummyscramble); CV_RegisterVar(&cv_dummystaff); + CV_RegisterVar(&cv_dummygametype); + CV_RegisterVar(&cv_dummyip); M_UpdateMenuBGImage(true); @@ -2286,30 +2291,10 @@ static void M_LevelSelectScrollDest(void) levellist.dest = (6*m)-3; } -void M_LevelSelectInit(INT32 choice) +// Builds the level list we'll be using from the gametype we're choosing and send us to the apropriate menu. +static void M_LevelListFromGametype(INT16 gt) { - (void)choice; - - switch (currentMenu->menuitems[itemOn].mvar1) - { - case 0: - cupgrid.grandprix = false; - levellist.timeattack = false; - break; - case 1: - cupgrid.grandprix = false; - levellist.timeattack = true; - break; - case 2: - cupgrid.grandprix = true; - levellist.timeattack = false; - break; - default: - CONS_Alert(CONS_WARNING, "Bad level select init\n"); - return; - } - - levellist.newgametype = currentMenu->menuitems[itemOn].mvar2; + levellist.newgametype = gt; PLAY_CupSelectDef.prevMenu = currentMenu; // Obviously go to Cup Select in gametypes that have cups. @@ -2351,6 +2336,41 @@ void M_LevelSelectInit(INT32 choice) PLAY_LevelSelectDef.prevMenu = currentMenu; M_SetupNextMenu(&PLAY_LevelSelectDef, false); + +} + +// Init level select for use in local play using the last choice we made. +// For the online MP version, see M_MPSetupNetgameMapSelect() + +void M_LevelSelectInit(INT32 choice) +{ + (void)choice; + + levellist.netgame = false; // Make sure this is reset as we'll only be using this function for offline games! + cupgrid.netgame = false; // Ditto + + switch (currentMenu->menuitems[itemOn].mvar1) + { + case 0: + cupgrid.grandprix = false; + levellist.timeattack = false; + break; + case 1: + cupgrid.grandprix = false; + levellist.timeattack = true; + break; + case 2: + cupgrid.grandprix = true; + levellist.timeattack = false; + break; + default: + CONS_Alert(CONS_WARNING, "Bad level select init\n"); + return; + } + + levellist.newgametype = currentMenu->menuitems[itemOn].mvar2; + + M_LevelListFromGametype(levellist.newgametype); } void M_CupSelectHandler(INT32 choice) @@ -2433,6 +2453,7 @@ void M_CupSelectHandler(INT32 choice) paused = false; SV_StartSinglePlayerServer(); multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... + netgame = levellist.netgame; // ^ ditto. D_MapChange( grandprixinfo.cup->levellist[0] + 1, GT_RACE, @@ -2567,6 +2588,8 @@ void M_LevelSelectHandler(INT32 choice) paused = false; SV_StartSinglePlayerServer(); multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... + netgame = levellist.netgame; // ^ ditto. + D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false); M_ClearMenus(true); @@ -2609,22 +2632,121 @@ void M_LevelSelectTick(void) struct mpmenu_s mpmenu; // MULTIPLAYER OPTION SELECT -void M_MPOptSelect(INT32 choice) -{ +// Use this as a quit routine within the HOST GAME and JOIN BY IP "sub" menus +boolean M_MPResetOpts(void) +{ + UINT8 i = 0; + + for (; i < 3; i++) + mpmenu.modewinextend[i][0] = 0; // Undo this + + return true; } -void M_MPOptSelectInit(INT32 chocie) +void M_MPOptSelectInit(INT32 choice) { + INT16 arrcpy[3][3] = {{0,68,0}, {0,48,0}, {0,12,0}}; + UINT8 i = 0, j = 0; // To copy the array into the struct + + (void)choice; + mpmenu.modechoice = 0; mpmenu.ticker = 0; + for (; i < 3; i++) + for (j = 0; j < 3; j++) + mpmenu.modewinextend[i][j] = arrcpy[i][j]; // I miss Lua already + M_SetupNextMenu(&PLAY_MP_OptSelectDef, false); } void M_MPOptSelectTick(void) { + UINT8 i = 0; + // 3 Because we have 3 options in the menu + for (; i < 3; i++) + { + if (mpmenu.modewinextend[i][0]) + mpmenu.modewinextend[i][2] += 8; + else + mpmenu.modewinextend[i][2] -= 8; + + mpmenu.modewinextend[i][2] = min(mpmenu.modewinextend[i][1], max(0, mpmenu.modewinextend[i][2])); + //CONS_Printf("%d - %d,%d,%d\n", i, mpmenu.modewinextend[i][0], mpmenu.modewinextend[i][1], mpmenu.modewinextend[i][2]); + } +} + + +// MULTIPLAYER HOST +void M_MPHostInit(INT32 choice) +{ + + (void)choice; + mpmenu.modewinextend[0][0] = 1; + M_SetupNextMenu(&PLAY_MP_HostDef, true); +} + +void M_MPSetupNetgameMapSelect(INT32 choice) +{ + + INT16 gt = GT_RACE; + (void)choice; + + levellist.netgame = true; // Yep, we'll be starting a netgame. + cupgrid.netgame = true; // Ditto + levellist.timeattack = false; // Make sure we reset those + cupgrid.grandprix = false; // Ditto + + // In case we ever want to add new gamemodes there somehow, have at it! + switch (cv_dummygametype.value) + { + case 1: // Battle + { + gt = GT_BATTLE; + break; + } + + default: + { + gt = GT_RACE; + break; + } + } + + M_LevelListFromGametype(gt); // Setup the level select. + // (This will also automatically send us to the apropriate menu) +} + +// MULTIPLAYER JOIN BY IP +void M_MPJoinIPInit(INT32 choice) +{ + + (void)choice; + mpmenu.modewinextend[1][0] = 1; + M_SetupNextMenu(&PLAY_MP_JoinIPDef, true); +} + +// Attempts to join a given IP from the menu. +void M_JoinIP(void) +{ + if (*(cv_dummyip.string) == '\0') // Jack shit + { + M_StartMessage("Please specify an address.\n", NULL, MM_NOTHING); + return; + } + + COM_BufAddText(va("connect \"%s\"\n", cv_dummyip.string)); + M_ClearMenus(true); + + // 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 } // MULTIPLAYER ROOM SELECT MENU @@ -2664,6 +2786,7 @@ void M_MPRoomSelectTick(void) void M_MPRoomSelectInit(INT32 choice) { + (void)choice; mpmenu.room = 0; mpmenu.ticker = 0; From fd1552cc567ae84c3041db8ff8e39618e095de38 Mon Sep 17 00:00:00 2001 From: SteelT Date: Sun, 14 Nov 2021 17:40:42 -0500 Subject: [PATCH 028/379] Fix some variables not be extern properly --- src/k_menu.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 0c59bfb19..96b341eb0 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -254,7 +254,7 @@ void M_GoBack(INT32 choice); void M_Ticker(void); void M_Init(void); -menu_t MessageDef; +extern menu_t MessageDef; void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtype); void M_StopMessage(INT32 choice); @@ -321,7 +321,7 @@ typedef enum SPLITCV_NAME, SPLITCV_MAX } splitscreencvars_t; -consvar_t *setup_playercvars[MAXSPLITSCREENPLAYERS][SPLITCV_MAX]; +extern consvar_t *setup_playercvars[MAXSPLITSCREENPLAYERS][SPLITCV_MAX]; void M_CharacterSelectInit(INT32 choice); void M_CharacterSelectHandler(INT32 choice); @@ -383,8 +383,8 @@ void M_MPOptSelect(INT32 choice); void M_MPOptSelectInit(INT32 choice); void M_MPOptSelectTick(void); boolean M_MPResetOpts(void); -consvar_t cv_dummygametype; // lazy hack to allow us to select the GT on the server host submenu -consvar_t cv_dummyip; // I HAVE +extern consvar_t cv_dummygametype; // lazy hack to allow us to select the GT on the server host submenu +extern consvar_t cv_dummyip; // I HAVE // HAVE YOUR IP ADDRESS (This just the hack Cvar we'll type into and then it apends itself to "connect" in the console for IP join) // MP Host From 1412b96b2074d243c547c3bd15c2e8267c3fc0f1 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Tue, 16 Nov 2021 23:03:46 +0100 Subject: [PATCH 029/379] EXPERIMENTAL: Save last 3 IPs manually joined to allow rejoining them later --- src/d_clisrv.c | 18 +++++++ src/d_main.c | 5 ++ src/k_menu.h | 6 ++- src/k_menudef.c | 37 ++++++++++---- src/k_menudraw.c | 100 +++++++++++++++++++----------------- src/k_menufunc.c | 64 ++++++++++++++++++++--- src/m_misc.c | 125 +++++++++++++++++++++++++++++++++++++++++++++ src/m_misc.h | 19 +++++++ src/sdl/i_system.c | 1 + 9 files changed, 312 insertions(+), 63 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 7c6c78f01..c0305d8bd 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2072,6 +2072,16 @@ static void CL_ConnectToServer(void) DEBFILE(va("Synchronisation Finished\n")); displayplayers[0] = consoleplayer; + + // At this point we've succesfully joined the server, if we joined by IP (ie: a valid joinedIP string), save it! + // @TODO: Save the proper server name, right now it doesn't seem like we can consistently retrieve it from the serverlist....? + // It works... sometimes but not always which is weird. + + if (*joinedIP) // false if we have "" which is \0 + M_AddToJoinedIPs(joinedIP, netbuffer->u.serverinfo.servername); + + strcpy(joinedIP, ""); // And empty this for good measure regardless of whether or not we actually used it. + } #ifndef NONET @@ -2240,6 +2250,10 @@ static void Command_ReloadBan(void) //recheck ban.txt static void Command_connect(void) { + + // By default, clear the saved address that we'd save after succesfully joining just to be sure: + strcpy(joinedIP, ""); + if (COM_Argc() < 2 || *COM_Argv(1) == 0) { CONS_Printf(M_GetText( @@ -2295,6 +2309,10 @@ static void Command_connect(void) servernode = I_NetMakeNodewPort(COM_Argv(1), COM_Argv(2)); else // address only, or address:port servernode = I_NetMakeNode(COM_Argv(1)); + + // Last IPs joined: + // Keep the address we typed in memory so that we can save it if we *succesfully* join the server + strcpy(joinedIP, COM_Argv(1)); } else { diff --git a/src/d_main.c b/src/d_main.c index 44c2673ee..3a2259337 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1158,6 +1158,9 @@ void D_SRB2Main(void) strcpy(savegamename, SAVEGAMENAME"%u.ssg"); strcpy(liveeventbackup, "live"SAVEGAMENAME".bkp"); // intentionally not ending with .ssg + // Init the joined IP table for quick rejoining of past games. + M_InitJoinedIPArray(); + { const char *userhome = D_Home(); //Alam: path to home @@ -1205,6 +1208,8 @@ void D_SRB2Main(void) configfile[sizeof configfile - 1] = '\0'; } + M_LoadJoinedIPs(); // load joined ips + // Create addons dir snprintf(addonsdir, sizeof addonsdir, "%s%s%s", srb2home, PATHSEP, "addons"); I_mkdir(addonsdir, 0755); diff --git a/src/k_menu.h b/src/k_menu.h index 96b341eb0..b9dacc5fe 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -133,6 +133,7 @@ typedef struct menu_s void (*drawroutine)(void); // draw routine void (*tickroutine)(void); // ticker routine boolean (*quitroutine)(void); // called before quit a menu return true if we can + boolean (*inputroutine)(INT32); // if set, called every frame in the input handler. Returning true overwrites normal input handling. } menu_t; typedef enum @@ -393,7 +394,8 @@ void M_MPSetupNetgameMapSelect(INT32 choice); // MP join by IP void M_MPJoinIPInit(INT32 choice); -void M_JoinIP(void); +boolean M_JoinIPInputs(INT32 ch); +void M_JoinIP(const char *ipa); // Server browser room selection void M_MPRoomSelect(INT32 choice); @@ -455,6 +457,7 @@ void M_DrawPlaybackMenu(void); 0, 0,\ M_DrawGenericMenu,\ NULL,\ + NULL,\ NULL\ } @@ -469,6 +472,7 @@ void M_DrawPlaybackMenu(void); 1, 10,\ M_DrawKartGamemodeMenu,\ NULL,\ + NULL,\ NULL\ } diff --git a/src/k_menudef.c b/src/k_menudef.c index 987bd6e26..d98c3b028 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -55,7 +55,8 @@ menu_t PLAY_CharSelectDef = { 0, 0, M_DrawCharacterSelect, M_CharacterSelectTick, - M_CharacterSelectQuit + M_CharacterSelectQuit, + NULL }; menuitem_t PLAY_MainMenu[] = @@ -116,6 +117,7 @@ menu_t PLAY_CupSelectDef = { 2, 10, M_DrawCupSelect, M_CupSelectTick, + NULL, NULL }; @@ -133,6 +135,7 @@ menu_t PLAY_LevelSelectDef = { 2, 10, M_DrawLevelSelect, M_LevelSelectTick, + NULL, NULL }; @@ -153,6 +156,7 @@ menu_t PLAY_TimeAttackDef = { 2, 10, M_DrawTimeAttack, NULL, + NULL, NULL }; @@ -178,11 +182,11 @@ menuitem_t PLAY_MP_OptSelect[] = {IT_STRING | IT_CALL, "Host Game", "Start your own online game!", NULL, M_MPHostInit, 0, 0}, - {IT_STRING | IT_CALL, "Join by IP", "Join an online game by its IP address.", - NULL, M_MPJoinIPInit, 0, 0}, - {IT_STRING | IT_CALL, "Server Browser", "Search for game servers to play in.", NULL, M_MPRoomSelectInit, 0, 0}, + + {IT_STRING | IT_CALL, "Join by IP", "Join an online game by its IP address.", + NULL, M_MPJoinIPInit, 0, 0}, }; menu_t PLAY_MP_OptSelectDef = { @@ -194,6 +198,7 @@ menu_t PLAY_MP_OptSelectDef = { -1, 1, M_DrawMPOptSelect, M_MPOptSelectTick, + NULL, NULL }; @@ -228,7 +233,8 @@ menu_t PLAY_MP_HostDef = { -1, 1, // 1 frame transition.... This is really just because I don't want the black fade when we press esc, hehe M_DrawMPHost, M_MPOptSelectTick, // This handles the unfolding options - M_MPResetOpts + M_MPResetOpts, + NULL }; // MULTIPLAYER JOIN BY IP @@ -236,11 +242,21 @@ menuitem_t PLAY_MP_JoinIP[] = { //{IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, M_MPOptSelect, 0, 0}, - {IT_STRING | IT_CVAR | IT_CV_STRING, "Address: ", "Type the IPv4 address of the server you wish to connect to.", + {IT_STRING | IT_CVAR | IT_CV_STRING, "IP: ", "Type the IPv4 address of the server then press enter to attempt connection.", NULL, &cv_dummyip, 0, 0}, - {IT_STRING | IT_CALL, "GO", "Select a map with the currently selected gamemode", - NULL, M_JoinIP, 0, 0}, + {IT_STRING | IT_SPACE, "LAST IPs JOINED:", "Kanade best waifu :)", + NULL, NULL, 0, 0}, + + {IT_STRING, "servip1", "The last 3 IPs you've succesfully joined are displayed here.", + NULL, NULL, 0, 0}, + + {IT_STRING, "servip2", "The last 3 IPs you've succesfully joined are displayed here.", + NULL, NULL, 0, 0}, + + {IT_STRING, "servip3", "The last 3 IPs you've succesfully joined are displayed here.", + NULL, NULL, 0, 0}, + }; menu_t PLAY_MP_JoinIPDef = { @@ -252,7 +268,8 @@ menu_t PLAY_MP_JoinIPDef = { -1, 1, // 1 frame transition.... This is really just because I don't want the black fade when we press esc, hehe M_DrawMPJoinIP, M_MPOptSelectTick, // This handles the unfolding options - M_MPResetOpts + M_MPResetOpts, + M_JoinIPInputs }; // MULTIPLAYER ROOM SELECT (CORE / MODDED) @@ -270,6 +287,7 @@ menu_t PLAY_MP_RoomSelectDef = { 0, 0, M_DrawMPRoomSelect, M_MPRoomSelectTick, + NULL, NULL }; @@ -308,5 +326,6 @@ menu_t PAUSE_PlaybackMenuDef = { 0, 0, M_DrawPlaybackMenu, NULL, + NULL, NULL }; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 3c178b654..d8945b0d5 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1317,7 +1317,7 @@ static void M_MPOptDrawer(menu_t *m, INT16 extend[3][3]) // TODO: Allow specific options to "expand" into smaller ones. patch_t *buttback = W_CachePatchName("M_PLAT2", PU_CACHE); - INT32 i, x = 142, y = 32; // Dirty magic numbers for now but they work out. + INT32 i, x = 132, y = 32; // Dirty magic numbers for now but they work out. for (i = 0; i < m->numitems; i++) { @@ -1382,7 +1382,7 @@ void M_DrawMPHost(void) { patch_t *gobutt = W_CachePatchName("M_BUTTGO", PU_CACHE); // I'm very mature - INT32 xp = 50, yp = 64, i = 0, w = 0; // Starting position for the text drawing. + INT32 xp = 40, yp = 64, i = 0, w = 0; // Starting position for the text drawing. M_DrawMPOptSelect(); // Draw the Multiplayer option select menu first // Now draw our host options... @@ -1457,62 +1457,68 @@ void M_DrawMPJoinIP(void) { patch_t *gobutt = W_CachePatchName("M_BUTTGO", PU_CACHE); // I'm very mature - INT32 xp = 70, yp = 100, i = 0; // Starting position for the text drawing. + INT32 xp = 73, yp = 133, i = 0; // Starting position for the text drawing. M_DrawMPOptSelect(); // Draw the Multiplayer option select menu first + // Now draw our host options... for (i = 0; i < currentMenu->numitems; i++) { - if (i == currentMenu->numitems-1) + switch (currentMenu->menuitems[i].status & IT_DISPLAY) { - - UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_MOSS, GTC_CACHE); - if (i == itemOn) - colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE); - - // Ideally we'd calculate this but it's not worth it for a 1-off menu probably..... - V_DrawFixedPatch(219<width/2), 114 -3, V_ALLOWLOWERCASE, colormap, currentMenu->menuitems[i].text); - } - else - { - switch (currentMenu->menuitems[i].status & IT_DISPLAY) + case IT_STRING: { - case IT_STRING: + + char str[MAXSTRINGLENGTH]; + strcpy(str, currentMenu->menuitems[i].text); + + // The last 3 options of this menu are to be the joined IP addresses... + if (currentMenu->numitems - i <= NUMLOGIP) { - V_DrawString(xp, yp, V_ALLOWLOWERCASE | (i == itemOn ? highlightflags : 0), 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_STRING: - V_DrawThinString(xp + 65, yp-1, V_ALLOWLOWERCASE, cv->string); - if (skullAnimCounter < 4 && i == itemOn) - V_DrawCharacter(xp + 65 + V_ThinStringWidth(cv->string, 0), yp, '_' | 0x80, false); - - break; - - default: - break; - } - break; - } - } - - xp += 5; - yp += 11; - - break; + UINT8 index = NUMLOGIP - (currentMenu->numitems - i); + if (joinedIPlist[index][1] && strlen(joinedIPlist[index][1])) // Try drawing server name + strcpy(str, joinedIPlist[index][1]); + else if (joinedIPlist[index][0] && strlen(joinedIPlist[index][0])) // If that fails, get the address + strcpy(str, joinedIPlist[index][0]); + else + strcpy(str, "---"); // If that fails too then there's nothing! } - break; - } + + V_DrawString(xp, yp, V_ALLOWLOWERCASE | (i == itemOn ? highlightflags : 0), str); + // 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_STRING: + V_DrawThinString(xp + 24, yp-1, V_ALLOWLOWERCASE, cv->string); + if (skullAnimCounter < 4 && i == itemOn) + V_DrawCharacter(xp + 24 + V_ThinStringWidth(cv->string, 0), yp, '_' | 0x80, false); + + // On this specific menu the only time we'll ever see this is for the connect by IP typefield. + // ...In other words it's safe to just draw a "CONNECT" string there! + V_DrawRightAlignedString(xp + 210, yp, (i == itemOn ? highlightflags : 0), "GO!"); + + break; + + default: + break; + } + break; + } + } + + xp += 5; + yp += 11; + + break; + } + break; } } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index ae756c669..b5b9ea305 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -928,6 +928,19 @@ boolean M_Responder(event_t *ev) routine(ch); return true; } + + // Handle menu-specific input handling. If this returns true we skip regular input handling. + if (currentMenu->inputroutine) + { + INT32 res = 0; + if (shiftdown && ch >= 32 && ch <= 127) + ch = shiftxform[ch]; + + res = currentMenu->inputroutine(ch); + + if (res) + return true; + } if (currentMenu->menuitems[itemOn].status == IT_MSGHANDLER) { @@ -1602,7 +1615,8 @@ menu_t MessageDef = 0, 0, // transition tics M_DrawMessageMenu, // drawing routine -> NULL, // ticker routine - NULL // quit routine + NULL, // quit routine + NULL // input routine }; // @@ -2646,7 +2660,7 @@ boolean M_MPResetOpts(void) void M_MPOptSelectInit(INT32 choice) { - INT16 arrcpy[3][3] = {{0,68,0}, {0,48,0}, {0,12,0}}; + INT16 arrcpy[3][3] = {{0,68,0}, {0,12,0}, {0,64,0}}; UINT8 i = 0, j = 0; // To copy the array into the struct (void)choice; @@ -2724,20 +2738,20 @@ void M_MPJoinIPInit(INT32 choice) { (void)choice; - mpmenu.modewinextend[1][0] = 1; + mpmenu.modewinextend[2][0] = 1; M_SetupNextMenu(&PLAY_MP_JoinIPDef, true); } // Attempts to join a given IP from the menu. -void M_JoinIP(void) +void M_JoinIP(const char *ipa) { - if (*(cv_dummyip.string) == '\0') // Jack shit + if (*(ipa) == '\0') // Jack shit { M_StartMessage("Please specify an address.\n", NULL, MM_NOTHING); return; } - COM_BufAddText(va("connect \"%s\"\n", cv_dummyip.string)); + COM_BufAddText(va("connect \"%s\"\n", ipa)); M_ClearMenus(true); // A little "please wait" message. @@ -2749,6 +2763,44 @@ void M_JoinIP(void) I_FinishUpdate(); // page flip or blit buffer } +boolean M_JoinIPInputs(INT32 ch) +{ + if (itemOn == 0) // connect field + { + // enter: connect + if (ch == KEY_ENTER) + { + M_JoinIP(cv_dummyip.string); + return true; + } + // ctrl+v -> copy paste! + else if (ctrldown && (ch == 'v' || ch == 'V')) + { + const char *paste = I_ClipboardPaste(); + UINT16 i; + for (i=0; i < strlen(paste); i++) + M_ChangeStringCvar(paste[i]); // We can afford to do this since we're currently on that cvar. + + return true; // Don't input the V obviously lol. + } + + } + else if (currentMenu->numitems - itemOn <= NUMLOGIP && ch == KEY_ENTER) // On one of the last 3 options for IP rejoining + { + UINT8 index = NUMLOGIP - (currentMenu->numitems - itemOn); + + // Is there an address at this part of the table? + if (joinedIPlist[index][0] && strlen(joinedIPlist[index][0])) + M_JoinIP(joinedIPlist[index][0]); + else + S_StartSound(NULL, sfx_lose); + + return true; // eat input. + } + + return false; +} + // MULTIPLAYER ROOM SELECT MENU void M_MPRoomSelect(INT32 choice) diff --git a/src/m_misc.c b/src/m_misc.c index 512a606de..32a1efc32 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -173,6 +173,50 @@ boolean takescreenshot = false; // Take a screenshot this tic moviemode_t moviemode = MM_OFF; +char joinedIPlist[NUMLOGIP][2][255]; +char joinedIP[255]; + +// This initializes the above array to have NULL evrywhere it should. +void M_InitJoinedIPArray(void) +{ + UINT8 i; + for (i=0; i < NUMLOGIP; i++) + { + strcpy(joinedIPlist[i][0], ""); + strcpy(joinedIPlist[i][1], ""); + } +} + +// This adds an entry to the above array +void M_AddToJoinedIPs(char *address, char *servname) +{ + + UINT8 i = 0; + UINT8 dupeindex = 0; + + // Check for dupes... + for (; i < NUMLOGIP && !dupeindex; i++) + { + // I don't care about the server name (this is broken anyway...) but definitely check the addresses + if (strcmp(joinedIPlist[i][0], address) == 0) + dupeindex = i; + } + + CONS_Printf("Adding %s (%s) to list of manually joined IPs\n", servname, address); + + // Start by moving every IP up 1 slot (dropping the last IP in the table) + // If we found duplicates, start here instead and pull the rest up. + for (i = dupeindex ? : NUMLOGIP; i; i--) + { + strcpy(joinedIPlist[i][0], joinedIPlist[i-1][0]); + strcpy(joinedIPlist[i][1], joinedIPlist[i-1][1]); + } + + // and add the new IP at the start of the table! + strcpy(joinedIPlist[0][0], address); + strcpy(joinedIPlist[0][1], servname); +} + /** Returns the map number for a map identified by the last two characters in * its name. * @@ -441,6 +485,87 @@ boolean FIL_CheckExtension(const char *in) return false; } +// LAST IPs JOINED LOG FILE! +// ...It won't be as overly engineered as the config file because let's be real there's 0 need to... + +// Save the file: +void M_SaveJoinedIPs(void) +{ + FILE *f = NULL; + UINT8 i; + char *filepath; + + if (!joinedIPlist[0][0] || !strlen(joinedIPlist[0][0])) + return; // Don't bother, there's nothing to save. + + // append srb2home to beginning of filename + // but check if srb2home isn't already there, first + if (!strstr(IPLOGFILE, srb2home)) + filepath = va(pandf,srb2home, IPLOGFILE); + else + filepath = Z_StrDup(IPLOGFILE); + + f = fopen(filepath, "w"); + + if (f == NULL) + return; // Uh I guess you don't have disk space????????? + + for (i=0; i < NUMLOGIP; i++) + { + if (joinedIPlist[i][0] && strlen(joinedIPlist[i][0])) + { + char savestring[MAXSTRINGLENGTH]; + strcpy(savestring, joinedIPlist[i][0]); + strcat(savestring, IPLOGFILESEP); + strcat(savestring, joinedIPlist[i][1]); + + fputs(savestring, f); + fputs("\n", f); // Because this won't do it automatically now will it... + } + } + + fclose(f); +} + + +// Load the file: +void M_LoadJoinedIPs(void) +{ + FILE *f = NULL; + UINT8 i = 0; + char filepath[255]; + char *s; + char content[255]; // 255 is more than long enough! + + strcpy(filepath, IPLOGFILE); + f = fopen(filepath, "r"); + + if (f == NULL) + return; // File doesn't exist? sure, just do nothing then. + + while (fgets(content, 255, f) && i < NUMLOGIP && content[0] && content[0] != '\n') // Don't let us write more than we can chew! + { + + // Now we have garbage under the form of "address;string" + // Now you might ask yourself, but what do we do if the player fucked with their file and now there's a bunch of garbage? + // ...Well that's not my problem lol. + + s = strtok(content, IPLOGFILESEP); // We got the address + strcpy(joinedIPlist[i][0], s); + + s = strtok(NULL, IPLOGFILESEP); // Let's get rid of this awful \n while we're here! + + if (strlen(s)) + s[strlen(s)-1] = '\0'; // Remove the \n + + strcpy(joinedIPlist[i][1], s); + + i++; + } + fclose(f); // We're done here +} + + // ========================================================================== // CONFIGURATION FILE // ========================================================================== diff --git a/src/m_misc.h b/src/m_misc.h index d06d14397..c85aaad8e 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -42,6 +42,25 @@ void M_StopMovie(void); // the file where game vars and settings are saved #define CONFIGFILENAME "kartconfig.cfg" +// The file where we'll save the last IPs we joined +#define IPLOGFILE "kartsavedips.txt" +#define IPLOGFILESEP ";" +#define NUMLOGIP 3 + +// Array where we'll store addresses to display for last servers joined +// {address, servame} +// 255 is long enough to store the text +extern char joinedIPlist[NUMLOGIP][2][255]; + +// Keep the address we're joining in mind until we've finished joining. +// Since we don't wanna add an IP address we aren't even sure worked out. +extern char joinedIP[255]; + +void M_InitJoinedIPArray(void); +void M_AddToJoinedIPs(char *address, char *servname); +void M_SaveJoinedIPs(void); +void M_LoadJoinedIPs(void); + INT32 M_MapNumber(char first, char second); boolean FIL_WriteFile(char const *name, const void *source, size_t length); diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index a21ff7d99..fbac5e7a7 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1801,6 +1801,7 @@ void I_Quit(void) M_SaveConfig(NULL); //save game config, cvars.. #ifndef NONET D_SaveBan(); // save the ban list + M_SaveJoinedIPs(); #endif // Make sure you lose points for ALT-F4 From c3a3a001b0f98a65648dfe2d72137720ab02bd42 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Wed, 17 Nov 2021 13:39:26 +0100 Subject: [PATCH 030/379] Make join by IP menu less shitty looking --- src/k_menudraw.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index d8945b0d5..801103f7b 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1456,7 +1456,14 @@ void M_DrawMPHost(void) void M_DrawMPJoinIP(void) { - patch_t *gobutt = W_CachePatchName("M_BUTTGO", PU_CACHE); // I'm very mature + patch_t *minibutt = W_CachePatchName("M_SBUTT", PU_CACHE); + // There is no such things as mini butts, only thick thighs to rest your head on. + patch_t *minigo = W_CachePatchName("M_SGO", PU_CACHE); + patch_t *typebar = W_CachePatchName("M_TYPEB", PU_CACHE); + + UINT8 *colormap = NULL; + UINT8 *colormapc = NULL; + INT32 xp = 73, yp = 133, i = 0; // Starting position for the text drawing. M_DrawMPOptSelect(); // Draw the Multiplayer option select menu first @@ -1469,10 +1476,10 @@ void M_DrawMPJoinIP(void) { case IT_STRING: { - + char str[MAXSTRINGLENGTH]; strcpy(str, currentMenu->menuitems[i].text); - + // The last 3 options of this menu are to be the joined IP addresses... if (currentMenu->numitems - i <= NUMLOGIP) { @@ -1484,7 +1491,7 @@ void M_DrawMPJoinIP(void) else strcpy(str, "---"); // If that fails too then there's nothing! } - + V_DrawString(xp, yp, V_ALLOWLOWERCASE | (i == itemOn ? highlightflags : 0), str); // Cvar specific handling @@ -1496,14 +1503,20 @@ void M_DrawMPJoinIP(void) switch (currentMenu->menuitems[i].status & IT_CVARTYPE) { case IT_CV_STRING: - V_DrawThinString(xp + 24, yp-1, V_ALLOWLOWERCASE, cv->string); + + colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_MOSS, GTC_CACHE); + colormapc = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE); + + V_DrawFixedPatch((xp + 20)<string); if (skullAnimCounter < 4 && i == itemOn) V_DrawCharacter(xp + 24 + V_ThinStringWidth(cv->string, 0), yp, '_' | 0x80, false); - - // On this specific menu the only time we'll ever see this is for the connect by IP typefield. - // ...In other words it's safe to just draw a "CONNECT" string there! - V_DrawRightAlignedString(xp + 210, yp, (i == itemOn ? highlightflags : 0), "GO!"); - + + // On this specific menu the only time we'll ever see this is for the connect by IP typefield. + // Draw the small GO button here (and the text which is a separate graphic) + V_DrawFixedPatch((xp + 20 + typebar->width -4)<width -4 + (minibutt->width/2))< Date: Wed, 17 Nov 2021 13:49:20 +0100 Subject: [PATCH 031/379] Fix misaligned button on HOST submenu --- src/k_menudraw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 801103f7b..f4d6ad08a 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1397,8 +1397,8 @@ void M_DrawMPHost(void) colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE); // Ideally we'd calculate this but it's not worth it for a 1-off menu probably..... - V_DrawFixedPatch(212<width/2), 100 -3, V_ALLOWLOWERCASE, colormap, currentMenu->menuitems[i].text); + V_DrawFixedPatch(202<width/2), 100 -3, V_ALLOWLOWERCASE, colormap, currentMenu->menuitems[i].text); } else { From b750ef11dd1d40b2ebb42a0e0bd4b6ec72b50b12 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Wed, 17 Nov 2021 14:21:29 +0100 Subject: [PATCH 032/379] Make sure we don't save IPs when hosting games... --- src/d_clisrv.c | 3 ++- src/k_menufunc.c | 16 ++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index c0305d8bd..a9576bdcf 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2077,7 +2077,7 @@ static void CL_ConnectToServer(void) // @TODO: Save the proper server name, right now it doesn't seem like we can consistently retrieve it from the serverlist....? // It works... sometimes but not always which is weird. - if (*joinedIP) // false if we have "" which is \0 + if (*joinedIP && strlen(joinedIP)) // false if we have "" which is \0 M_AddToJoinedIPs(joinedIP, netbuffer->u.serverinfo.servername); strcpy(joinedIP, ""); // And empty this for good measure regardless of whether or not we actually used it. @@ -3757,6 +3757,7 @@ void SV_StartSinglePlayerServer(void) server = true; netgame = false; multiplayer = false; + strcpy(joinedIP, ""); // Make sure to empty this so that we don't save garbage when we start our own game. (because yes we use this for netgames too....) if (modeattacking == ATTACKING_CAPSULES) { diff --git a/src/k_menufunc.c b/src/k_menufunc.c index b5b9ea305..6c25233bd 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -928,16 +928,16 @@ boolean M_Responder(event_t *ev) routine(ch); return true; } - + // Handle menu-specific input handling. If this returns true we skip regular input handling. if (currentMenu->inputroutine) { INT32 res = 0; if (shiftdown && ch >= 32 && ch <= 127) ch = shiftxform[ch]; - + res = currentMenu->inputroutine(ch); - + if (res) return true; } @@ -2780,24 +2780,24 @@ boolean M_JoinIPInputs(INT32 ch) UINT16 i; for (i=0; i < strlen(paste); i++) M_ChangeStringCvar(paste[i]); // We can afford to do this since we're currently on that cvar. - + return true; // Don't input the V obviously lol. } - + } else if (currentMenu->numitems - itemOn <= NUMLOGIP && ch == KEY_ENTER) // On one of the last 3 options for IP rejoining { UINT8 index = NUMLOGIP - (currentMenu->numitems - itemOn); - + // Is there an address at this part of the table? if (joinedIPlist[index][0] && strlen(joinedIPlist[index][0])) M_JoinIP(joinedIPlist[index][0]); else S_StartSound(NULL, sfx_lose); - + return true; // eat input. } - + return false; } From e560bffcf76a296fd5f57fd3c86347012697251d Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Thu, 18 Nov 2021 15:19:25 +0100 Subject: [PATCH 033/379] Pause menu start + map switching --- src/k_menu.h | 29 +++++++++- src/k_menudef.c | 91 +++++++++++++++++++++++++++++- src/k_menudraw.c | 142 +++++++++++++++++++++++++++++++++++++++++++++++ src/k_menufunc.c | 115 +++++++++++++++++++++++++++++++++----- 4 files changed, 361 insertions(+), 16 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index b9dacc5fe..33d63b52a 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -133,7 +133,7 @@ typedef struct menu_s void (*drawroutine)(void); // draw routine void (*tickroutine)(void); // ticker routine boolean (*quitroutine)(void); // called before quit a menu return true if we can - boolean (*inputroutine)(INT32); // if set, called every frame in the input handler. Returning true overwrites normal input handling. + boolean (*inputroutine)(INT32); // if set, called every frame in the input handler. Returning true overwrites normal input handling. } menu_t; typedef enum @@ -196,6 +196,12 @@ extern menu_t PLAY_MP_RoomSelectDef; extern menuitem_t PLAY_BattleGamemodesMenu[]; extern menu_t PLAY_BattleGamemodesDef; +extern menuitem_t PAUSE_Main[]; +extern menu_t PAUSE_MainDef; + +extern menuitem_t PAUSE_GamemodesMenu[]; +extern menu_t PAUSE_GamemodesDef; + extern menuitem_t PAUSE_PlaybackMenu[]; extern menu_t PAUSE_PlaybackMenuDef; @@ -402,6 +408,24 @@ void M_MPRoomSelect(INT32 choice); void M_MPRoomSelectTick(void); void M_MPRoomSelectInit(INT32 choice); +// Pause menu: + +// Keep track of some pause menu data for visual goodness. +extern struct pausemenu_s { + + tic_t ticker; // How long the menu's been open for + INT16 offset; // To make the icons move smoothly when we transition! + + INT16 openoffset; // Used when you open / close the menu to slide everything in. + boolean closing; // When this is set, the open offset goes backwards to close the menu smoothly. +} pausemenu; + +void M_OpenPauseMenu(void); +void M_QuitPauseMenu(void); + +boolean M_PauseInputs(INT32 ch); +void M_PauseTick(void); + // Replay Playback extern tic_t playback_last_menu_interaction_leveltime; @@ -443,6 +467,9 @@ void M_DrawMPHost(void); void M_DrawMPJoinIP(void); void M_DrawMPRoomSelect(void); +// Pause menu: +void M_DrawPause(void); + // Replay Playback void M_DrawPlaybackMenu(void); diff --git a/src/k_menudef.c b/src/k_menudef.c index d98c3b028..f1ff28350 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -252,10 +252,10 @@ menuitem_t PLAY_MP_JoinIP[] = NULL, NULL, 0, 0}, {IT_STRING, "servip2", "The last 3 IPs you've succesfully joined are displayed here.", - NULL, NULL, 0, 0}, + NULL, NULL, 0, 0}, {IT_STRING, "servip3", "The last 3 IPs you've succesfully joined are displayed here.", - NULL, NULL, 0, 0}, + NULL, NULL, 0, 0}, }; @@ -296,6 +296,93 @@ menu_t PLAY_MP_RoomSelectDef = { // In-game/pause menus // ------------------- +// ESC pause menu +// Since there's no descriptions to each item, we'll use the descriptions as the names of the patches we want to draw for each option :) + +menuitem_t PAUSE_Main[] = +{ + + {IT_STRING | IT_CALL, "ADDONS", "M_ICOADD", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_SUBMENU, "CHANGE MAP", "M_ICOMAP", + NULL, &PAUSE_GamemodesDef, 0, 0}, + +#ifdef HAVE_DISCORDRPC + {IT_STRING | IT_CALL, "DISCORD REQUESTS", "M_ICODIS", + NULL, NULL, 0, 0}, +#endif + + {IT_STRING | IT_CALL, "RESUME GAME", "M_ICOUNP", + NULL, M_QuitPauseMenu, 0, 0}, + + {IT_STRING | IT_CALL, "SPECTATE", "M_ICOSPC", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CALL, "ENTER GAME", "M_ICOENT", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CALL, "CANCEL JOIN", "M_ICOSPC", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CALL, "PLAYER SETUP", "M_ICOCHR", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CALL, "OPTIONS", "M_ICOOPT", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CALL, "EXIT GAME", "M_ICOEXT", + NULL, NULL, 0, 0}, +}; + +// We'll need this since we're gonna have to dynamically enable and disable options depending on which state we're in. +typedef enum +{ + mpause_addons = 0, + mpause_switchmap, +#ifdef HAVE_DISCORDRPC + mpause_discordrequests, +#endif + + mpause_continue, + mpause_spectate, + mpause_entergame, + mpause_canceljoin, + mpause_psetup, + mpause_options, + + mpause_title, +} mpause_e; + + +menu_t PAUSE_MainDef = { + sizeof (PAUSE_Main) / sizeof (menuitem_t), + NULL, + 0, + PAUSE_Main, + 0, 0, + 1, 10, // For transition with some menus! + M_DrawPause, + M_PauseTick, + NULL, + M_PauseInputs +}; + +// PAUSE : Map switching gametype selection (In case you want to pick from battle / race...) +menuitem_t PAUSE_GamemodesMenu[] = +{ + {IT_STRING | IT_CALL, "Race", "Select which gamemode to choose a new map from.", + NULL, M_LevelSelectInit, 0, GT_RACE}, + + {IT_STRING | IT_CALL, "Battle", "Select which gamemode to choose a new map from.", + NULL, M_LevelSelectInit, 0, GT_BATTLE}, + + {IT_STRING | IT_CALL, "Back", NULL, NULL, M_GoBack, 0, 0}, +}; + +menu_t PAUSE_GamemodesDef = KARTGAMEMODEMENU(PAUSE_GamemodesMenu, &PAUSE_MainDef); + +// Replay popup menu menuitem_t PAUSE_PlaybackMenu[] = { {IT_CALL | IT_STRING, "Hide Menu (Esc)", NULL, "M_PHIDE", M_SelectableClearMenus, 0, 0}, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index f4d6ad08a..fb0653459 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1589,6 +1589,148 @@ void M_DrawMPRoomSelect(void) // INGAME / PAUSE MENUS // +// PAUSE + +// PAUSE MAIN MENU +void M_DrawPause(void) +{ + + SINT8 i; + SINT8 itemsdrawn = 0; + SINT8 countdown = 0; + INT16 ypos = -50; // Draw 3 items from selected item (y=100 - 3 items spaced by 50 px each... you get the idea.) + INT16 dypos; + + INT16 offset = menutransition.tics ? floor(pow(2, (double)menutransition.tics)) : pausemenu.openoffset; + INT16 arrxpos = 150 + 2*offset; // To draw the background arrow. + + INT16 j = 0; + char word1[MAXSTRINGLENGTH]; + INT16 word1len = 0; + char word2[MAXSTRINGLENGTH]; + INT16 word2len = 0; + boolean sok = false; + + patch_t *pausebg = W_CachePatchName("M_STRIPU", PU_CACHE); + patch_t *vertbg = W_CachePatchName("M_STRIPV", PU_CACHE); + patch_t *pausetext = W_CachePatchName("M_PAUSET", PU_CACHE); + + patch_t *arrstart = W_CachePatchName("M_PTIP", PU_CACHE); + patch_t *arrfill = W_CachePatchName("M_PFILL", PU_CACHE); + + //V_DrawFadeScreen(0xFF00, 16); + + // "PAUSED" + V_DrawFixedPatch(-offset*FRACUNIT, 0, FRACUNIT, V_ADD, pausebg, NULL); + V_DrawFixedPatch(-offset*FRACUNIT, 0, FRACUNIT, 0, pausetext, NULL); + + // Vertical Strip: + V_DrawFixedPatch((230 + offset)<numitems-1; + } + + // Aaaaand now we can start drawing! + // Reminder that we set the patches of the options to the description since we're not using that. I'm smart, I know... + + // Draw the background arrow + V_DrawFixedPatch(arrxpos<width) < BASEVIDWIDTH) + { + V_DrawFixedPatch(arrxpos<width; + } + + while (itemsdrawn < 7) + { + + switch (currentMenu->menuitems[i].status & IT_DISPLAY) + { + case IT_STRING: + { + + char iconame[9]; // 8 chars + \0 + patch_t *pp; + + if (i == itemOn) + { + strcpy(iconame, currentMenu->menuitems[i].tooltip); + iconame[7] = '2'; // Yes this is a stupid hack. Replace the last character with a 2 when we're selecting this graphic. + + pp = W_CachePatchName(iconame, PU_CACHE); + } + else + pp = W_CachePatchName(currentMenu->menuitems[i].tooltip, PU_CACHE); + + // 294 - 261 = 33 + // We need to move 33 px in 50 tics which means we move 33/50 = 0.66 px every tic = 2/3 of the offset. + // trust me i graduated highschool!!!! + + // Multiply by -1 or 1 depending on whether we're below or above 100 px. + // This double ternary is awful, yes. + + dypos = ypos + pausemenu.offset; + V_DrawFixedPatch( ((i == itemOn ? (294 - pausemenu.offset*2/3 * (dypos > 100 ? 1 : -1)) : 261) + offset) << FRACBITS, (dypos)*FRACUNIT, FRACUNIT, 0, pp, NULL); + + ypos += 50; + itemsdrawn++; // We drew that! + break; + } + } + + + i++; // Regardless of whether we drew or not, go to the next item in the menu. + if (i > currentMenu->numitems) + i = 0; + } + + // Draw the string! + // ...but first get what we need to get. + while (currentMenu->menuitems[itemOn].text[j] && j < MAXSTRINGLENGTH) + { + char c = currentMenu->menuitems[itemOn].text[j]; + + if (c == ' ') + { + sok = true; + j++; + continue; // We don't care about this :moyai: + } + + if (sok) + { + word2[word2len] = c; + word2len++; + } + else + { + word1[word1len] = c; + word1len++; + } + + j++; + } + + word1[word1len] = '\0'; + word2[word2len] = '\0'; + + // If there's no 2nd word, take this opportunity to center this line of text. + if (word1len) + V_DrawCenteredLSTitleHighString(220 + offset*2, 75 + (!word2len ? 10 : 0), 0, word1); + + if (word2len) + V_DrawCenteredLSTitleLowString(220 + offset*2, 103, 0, word2); +} + tic_t playback_last_menu_interaction_leveltime = 0; void M_DrawPlaybackMenu(void) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 6c25233bd..458bfd441 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1219,6 +1219,12 @@ void M_StartControlPanel(void) currentMenu = &MainDef; itemOn = 0; } + else + { + // For now let's just always open the same pause menu. + M_OpenPauseMenu(); + } + #if 0 else if (modeattacking) { @@ -2354,7 +2360,8 @@ static void M_LevelListFromGametype(INT16 gt) } // Init level select for use in local play using the last choice we made. -// For the online MP version, see M_MPSetupNetgameMapSelect() +// For the online MP version used to START HOSTING A GAME, see M_MPSetupNetgameMapSelect() +// (We still use this one midgame) void M_LevelSelectInit(INT32 choice) { @@ -2465,9 +2472,15 @@ void M_CupSelectHandler(INT32 choice) grandprixinfo.initalize = true; paused = false; - SV_StartSinglePlayerServer(); - multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... - netgame = levellist.netgame; // ^ ditto. + + // Don't restart the server if we're already in a game lol + if (gamestate == GS_MENU) + { + SV_StartSinglePlayerServer(); + multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... + netgame = levellist.netgame; // ^ ditto. + } + D_MapChange( grandprixinfo.cup->levellist[0] + 1, GT_RACE, @@ -2593,16 +2606,20 @@ void M_LevelSelectHandler(INT32 choice) S_StartSound(NULL, sfx_s3k63); - // Early fadeout to let the sound finish playing - F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); - F_WipeEndScreen(); - F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); - paused = false; - SV_StartSinglePlayerServer(); - multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... - netgame = levellist.netgame; // ^ ditto. + + if (gamestate == GS_MENU) // Don't restart the server if we're already in a game lol. Don't fade out either. + { + // Early fadeout to let the sound finish playing + F_WipeStartScreen(); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeEndScreen(); + F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); + + SV_StartSinglePlayerServer(); + multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... + netgame = levellist.netgame; // ^ ditto. + } D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false); @@ -2855,6 +2872,78 @@ void M_EndModeAttackRun(void) #endif } +struct pausemenu_s pausemenu; + +// Pause menu! +void M_OpenPauseMenu(void) +{ + currentMenu = &PAUSE_MainDef; + + // Ready the variables + pausemenu.ticker = 0; + + pausemenu.offset = 0; + pausemenu.openoffset = 256; + pausemenu.closing = false; +} + +void M_QuitPauseMenu(void) +{ + // M_PauseTick actually handles the quitting when it's been long enough. + pausemenu.closing = true; + pausemenu.openoffset = 4; +} + +void M_PauseTick(void) +{ + pausemenu.offset /= 2; + + if (pausemenu.closing) + { + pausemenu.openoffset *= 2; + if (pausemenu.openoffset > 255) + M_ClearMenus(true); + + } + else + pausemenu.openoffset /= 2; +} + +boolean M_PauseInputs(INT32 ch) +{ + + if (pausemenu.closing) + return true; // Don't allow inputs. + + switch (ch) + { + + case KEY_UPARROW: + { + pausemenu.offset -= 50; // Each item is spaced by 50 px + M_PrevOpt(); + return true; + } + + case KEY_DOWNARROW: + { + pausemenu.offset += 50; // Each item is spaced by 50 px + M_NextOpt(); + return true; + } + + case KEY_ESCAPE: + { + M_QuitPauseMenu(); + return true; + } + + } + + return false; +} + + // Replay Playback Menu void M_SetPlaybackMenuPointer(void) { From 55b9489af05d82101bc1df5f50d1d2b6e56ca819 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Thu, 18 Nov 2021 20:47:52 +0100 Subject: [PATCH 034/379] pause menu: spectate + exit game --- src/k_menu.h | 26 ++++++++- src/k_menudef.c | 28 ++-------- src/k_menudraw.c | 14 ++++- src/k_menufunc.c | 141 +++++++++++++++++++++++++++++++++++++---------- 4 files changed, 154 insertions(+), 55 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 33d63b52a..b16edd416 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -199,6 +199,25 @@ extern menu_t PLAY_BattleGamemodesDef; extern menuitem_t PAUSE_Main[]; extern menu_t PAUSE_MainDef; +// We'll need this since we're gonna have to dynamically enable and disable options depending on which state we're in. +typedef enum +{ + mpause_addons = 0, + mpause_switchmap, +#ifdef HAVE_DISCORDRPC + mpause_discordrequests, +#endif + + mpause_continue, + mpause_spectate, + mpause_entergame, + mpause_canceljoin, + mpause_psetup, + mpause_options, + + mpause_title, +} mpause_e; + extern menuitem_t PAUSE_GamemodesMenu[]; extern menu_t PAUSE_GamemodesDef; @@ -422,10 +441,15 @@ extern struct pausemenu_s { void M_OpenPauseMenu(void); void M_QuitPauseMenu(void); - boolean M_PauseInputs(INT32 ch); void M_PauseTick(void); +// Bunch of funny functions for the pause menu...~ +void M_ConfirmSpectate(INT32 choice); // Spectate confirm when you're alone +void M_ConfirmEnterGame(INT32 choice); // Enter game confirm when you're alone +void M_ConfirmSpectateChange(INT32 choice); // Splitscreen spectate/play menu func +void M_EndGame(INT32 choice); // Quitting to title + // Replay Playback extern tic_t playback_last_menu_interaction_leveltime; diff --git a/src/k_menudef.c b/src/k_menudef.c index f1ff28350..d80e74760 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -317,13 +317,13 @@ menuitem_t PAUSE_Main[] = NULL, M_QuitPauseMenu, 0, 0}, {IT_STRING | IT_CALL, "SPECTATE", "M_ICOSPC", - NULL, NULL, 0, 0}, + NULL, M_ConfirmSpectate, 0, 0}, {IT_STRING | IT_CALL, "ENTER GAME", "M_ICOENT", - NULL, NULL, 0, 0}, + NULL, M_ConfirmEnterGame, 0, 0}, {IT_STRING | IT_CALL, "CANCEL JOIN", "M_ICOSPC", - NULL, NULL, 0, 0}, + NULL, M_ConfirmSpectate, 0, 0}, {IT_STRING | IT_CALL, "PLAYER SETUP", "M_ICOCHR", NULL, NULL, 0, 0}, @@ -332,29 +332,9 @@ menuitem_t PAUSE_Main[] = NULL, NULL, 0, 0}, {IT_STRING | IT_CALL, "EXIT GAME", "M_ICOEXT", - NULL, NULL, 0, 0}, + NULL, M_EndGame, 0, 0}, }; -// We'll need this since we're gonna have to dynamically enable and disable options depending on which state we're in. -typedef enum -{ - mpause_addons = 0, - mpause_switchmap, -#ifdef HAVE_DISCORDRPC - mpause_discordrequests, -#endif - - mpause_continue, - mpause_spectate, - mpause_entergame, - mpause_canceljoin, - mpause_psetup, - mpause_options, - - mpause_title, -} mpause_e; - - menu_t PAUSE_MainDef = { sizeof (PAUSE_Main) / sizeof (menuitem_t), NULL, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index fb0653459..c2de58df2 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1632,10 +1632,17 @@ void M_DrawPause(void) // Which means... let's count down from itemOn for (i = itemOn; countdown < 3; countdown++) { - //CONS_Printf("pass %d: %d\n", countdown, i); i--; if (i < 0) i = currentMenu->numitems-1; + + while (currentMenu->menuitems[i].status == IT_DISABLED) + { + i--; + + if (i < 0) + i = currentMenu->numitems-1; + } } // Aaaaand now we can start drawing! @@ -1690,7 +1697,12 @@ void M_DrawPause(void) i++; // Regardless of whether we drew or not, go to the next item in the menu. if (i > currentMenu->numitems) + { i = 0; + while (!(currentMenu->menuitems[i].status & IT_DISPLAY)) + i++; + + } } // Draw the string! diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 458bfd441..6a1788974 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2577,39 +2577,39 @@ void M_LevelSelectHandler(INT32 choice) } else { - UINT8 ssplayers = cv_splitplayers.value-1; - - netgame = false; - multiplayer = true; - - strncpy(connectedservername, cv_servername.string, MAXSERVERNAME); - - // Still need to reset devmode - cv_debug = 0; - - if (demo.playback) - G_StopDemo(); - if (metalrecording) - G_StopMetalDemo(); - - /*if (levellist.choosemap == 0) - levellist.choosemap = G_RandMap(G_TOLFlag(levellist.newgametype), -1, false, 0, false, NULL);*/ - - if (cv_maxplayers.value < ssplayers+1) - CV_SetValue(&cv_maxplayers, ssplayers+1); - - if (splitscreen != ssplayers) + if (gamestate == GS_MENU) { - splitscreen = ssplayers; - SplitScreen_OnChange(); - } + UINT8 ssplayers = cv_splitplayers.value-1; - S_StartSound(NULL, sfx_s3k63); + netgame = false; + multiplayer = true; - paused = false; + strncpy(connectedservername, cv_servername.string, MAXSERVERNAME); + + // Still need to reset devmode + cv_debug = 0; + + if (demo.playback) + G_StopDemo(); + if (metalrecording) + G_StopMetalDemo(); + + /*if (levellist.choosemap == 0) + levellist.choosemap = G_RandMap(G_TOLFlag(levellist.newgametype), -1, false, 0, false, NULL);*/ + + if (cv_maxplayers.value < ssplayers+1) + CV_SetValue(&cv_maxplayers, ssplayers+1); + + if (splitscreen != ssplayers) + { + splitscreen = ssplayers; + SplitScreen_OnChange(); + } + + S_StartSound(NULL, sfx_s3k63); + + paused = false; - if (gamestate == GS_MENU) // Don't restart the server if we're already in a game lol. Don't fade out either. - { // Early fadeout to let the sound finish playing F_WipeStartScreen(); V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); @@ -2877,6 +2877,7 @@ struct pausemenu_s pausemenu; // Pause menu! void M_OpenPauseMenu(void) { + boolean singleplayermode = (modeattacking || grandprixinfo.gp); currentMenu = &PAUSE_MainDef; // Ready the variables @@ -2885,6 +2886,45 @@ void M_OpenPauseMenu(void) pausemenu.offset = 0; pausemenu.openoffset = 256; pausemenu.closing = false; + + itemOn = mpause_continue; // Make sure we select "RESUME GAME" by default + + + // Now the hilarious balancing act of deciding what options should be enabled and which ones shouldn't be! + // By default, disable anything sensitive: + + PAUSE_Main[mpause_addons].status = IT_DISABLED; + PAUSE_Main[mpause_switchmap].status = IT_DISABLED; +#ifdef HAVE_DISCORDRPC + PAUSE_Main[mpause_discordrequests].status = IT_DISABLED; +#endif + + PAUSE_Main[mpause_spectate].status = IT_DISABLED; + PAUSE_Main[mpause_entergame].status = IT_DISABLED; + PAUSE_Main[mpause_canceljoin].status = IT_DISABLED; + PAUSE_Main[mpause_psetup].status = IT_DISABLED; + + if (!singleplayermode && (server || IsPlayerAdmin(consoleplayer))) + { + PAUSE_Main[mpause_switchmap].status = IT_STRING | IT_SUBMENU; + PAUSE_Main[mpause_addons].status = IT_STRING | IT_CALL; + } + + if (!singleplayermode) + PAUSE_Main[mpause_psetup].status = IT_STRING | IT_CALL; + + if (G_GametypeHasSpectators()) + { + if (!players[consoleplayer].spectator) + PAUSE_Main[mpause_spectate].status = IT_STRING | IT_CALL; + else if (players[consoleplayer].pflags & PF_WANTSTOJOIN) + PAUSE_Main[mpause_canceljoin].status = IT_STRING | IT_CALL; + else + PAUSE_Main[mpause_entergame].status = IT_STRING | IT_CALL; + } + + + } void M_QuitPauseMenu(void) @@ -2943,6 +2983,49 @@ boolean M_PauseInputs(INT32 ch) return false; } +// Pause spectate / join functions +void M_ConfirmSpectate(INT32 choice) +{ + (void)choice; + // We allow switching to spectator even if team changing is not allowed + M_QuitPauseMenu(); + COM_ImmedExecute("changeteam spectator"); +} + +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_QuitPauseMenu(); + COM_ImmedExecute("changeteam playing"); +} + +static void M_ExitGameResponse(INT32 ch) +{ + if (ch != 'y' && ch != KEY_ENTER) + return; + + //Command_ExitGame_f(); + G_SetExitGameFlag(); + M_ClearMenus(true); +} + +void M_EndGame(INT32 choice) +{ + (void)choice; + if (demo.playback) + return; + + if (!Playing()) + return; + + M_StartMessage(M_GetText("Are you sure you want to return\nto the title screen?\n(Press 'Y' to confirm)\n"), M_ExitGameResponse, MM_YESNO); +} + // Replay Playback Menu void M_SetPlaybackMenuPointer(void) From a64f97da7b7a6cab5d2347111b97b6726cb9110e Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Thu, 18 Nov 2021 22:05:22 +0100 Subject: [PATCH 035/379] Tiny prep work for splitscreen spectate/join to implement later --- src/k_menu.h | 4 ++++ src/k_menudef.c | 3 +++ src/k_menudraw.c | 2 +- src/k_menufunc.c | 25 ++++++++++++++++++------- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index b16edd416..20dfcab17 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -212,6 +212,7 @@ typedef enum mpause_spectate, mpause_entergame, mpause_canceljoin, + mpause_spectatemenu, mpause_psetup, mpause_options, @@ -444,6 +445,9 @@ void M_QuitPauseMenu(void); boolean M_PauseInputs(INT32 ch); void M_PauseTick(void); +extern consvar_t cv_dummymenuplayer; +extern consvar_t cv_dummyspectator; + // Bunch of funny functions for the pause menu...~ void M_ConfirmSpectate(INT32 choice); // Spectate confirm when you're alone void M_ConfirmEnterGame(INT32 choice); // Enter game confirm when you're alone diff --git a/src/k_menudef.c b/src/k_menudef.c index d80e74760..ee9213bb7 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -325,6 +325,9 @@ menuitem_t PAUSE_Main[] = {IT_STRING | IT_CALL, "CANCEL JOIN", "M_ICOSPC", NULL, M_ConfirmSpectate, 0, 0}, + {IT_STRING | IT_SUBMENU, "JOIN OR SPECTATE", "M_ICOENT", + NULL, NULL, 0, 0}, + {IT_STRING | IT_CALL, "PLAYER SETUP", "M_ICOCHR", NULL, NULL, 0, 0}, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index c2de58df2..d6efce47d 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1711,7 +1711,7 @@ void M_DrawPause(void) { char c = currentMenu->menuitems[itemOn].text[j]; - if (c == ' ') + if (c == ' ' && !sok) { sok = true; j++; diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 6a1788974..2f1943081 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -166,13 +166,15 @@ static CV_PossibleValue_t dummyscramble_cons_t[] = {{0, "Random"}, {1, "Points"} static CV_PossibleValue_t dummystaff_cons_t[] = {{0, "MIN"}, {100, "MAX"}, {0, NULL}}; static CV_PossibleValue_t dummygametype_cons_t[] = {{0, "Race"}, {1, "Battle"}, {0, NULL}}; -static consvar_t cv_dummymenuplayer = CVAR_INIT ("dummymenuplayer", "P1", CV_HIDDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange); +//static consvar_t cv_dummymenuplayer = CVAR_INIT ("dummymenuplayer", "P1", CV_HIDDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange); static consvar_t cv_dummyteam = CVAR_INIT ("dummyteam", "Spectator", CV_HIDDEN, dummyteam_cons_t, NULL); -static consvar_t cv_dummyspectate = CVAR_INIT ("dummyspectate", "Spectator", CV_HIDDEN, dummyspectate_cons_t, NULL); +//static cv_dummyspectate = CVAR_INITconsvar_t ("dummyspectate", "Spectator", CV_HIDDEN, dummyspectate_cons_t, NULL); static consvar_t cv_dummyscramble = CVAR_INIT ("dummyscramble", "Random", CV_HIDDEN, dummyscramble_cons_t, NULL); static consvar_t cv_dummystaff = CVAR_INIT ("dummystaff", "0", CV_HIDDEN|CV_CALL, dummystaff_cons_t, Dummystaff_OnChange); consvar_t cv_dummygametype = CVAR_INIT ("dummygametype", "Race", CV_HIDDEN, dummygametype_cons_t, NULL); consvar_t cv_dummyip = CVAR_INIT ("dummyip", "", CV_HIDDEN, NULL, NULL); +consvar_t cv_dummymenuplayer = CVAR_INIT ("dummymenuplayer", "P1", CV_HIDDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange); +consvar_t cv_dummyspectate = CVAR_INIT ("dummyspectate", "Spectator", CV_HIDDEN, dummyspectate_cons_t, NULL); // ========================================================================== // CVAR ONCHANGE EVENTS GO HERE @@ -2902,8 +2904,11 @@ void M_OpenPauseMenu(void) PAUSE_Main[mpause_spectate].status = IT_DISABLED; PAUSE_Main[mpause_entergame].status = IT_DISABLED; PAUSE_Main[mpause_canceljoin].status = IT_DISABLED; + PAUSE_Main[mpause_spectatemenu].status = IT_DISABLED; PAUSE_Main[mpause_psetup].status = IT_DISABLED; + Dummymenuplayer_OnChange(); // Make sure the consvar is within bounds of the amount of splitscreen players we have. + if (!singleplayermode && (server || IsPlayerAdmin(consoleplayer))) { PAUSE_Main[mpause_switchmap].status = IT_STRING | IT_SUBMENU; @@ -2915,12 +2920,18 @@ void M_OpenPauseMenu(void) if (G_GametypeHasSpectators()) { - if (!players[consoleplayer].spectator) - PAUSE_Main[mpause_spectate].status = IT_STRING | IT_CALL; - else if (players[consoleplayer].pflags & PF_WANTSTOJOIN) - PAUSE_Main[mpause_canceljoin].status = IT_STRING | IT_CALL; + + if (splitscreen) + PAUSE_Main[mpause_spectatemenu].status = IT_STRING|IT_SUBMENU; else - PAUSE_Main[mpause_entergame].status = IT_STRING | IT_CALL; + { + if (!players[consoleplayer].spectator) + PAUSE_Main[mpause_spectate].status = IT_STRING | IT_CALL; + else if (players[consoleplayer].pflags & PF_WANTSTOJOIN) + PAUSE_Main[mpause_canceljoin].status = IT_STRING | IT_CALL; + else + PAUSE_Main[mpause_entergame].status = IT_STRING | IT_CALL; + } } From 685868edf50ff1ba3b2305d220d14984e2bb45e4 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 19 Nov 2021 22:08:17 +0100 Subject: [PATCH 036/379] Pause: Player setup --- src/d_netcmd.c | 33 +++++++++++++++-- src/k_menudef.c | 2 +- src/k_menufunc.c | 93 ++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 118 insertions(+), 10 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 822435042..10e580d09 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5415,7 +5415,7 @@ static void Followercolor4_OnChange(void) } } -/** Sends a skin change for the console player, unless that player is moving. +/** Sends a skin change for the console player, unless that player is moving. Also forces them to spectate if the change is done during gameplay * \sa cv_skin, Skin2_OnChange, Color_OnChange * \author Graue */ @@ -5432,7 +5432,23 @@ static void Skin_OnChange(void) } if (CanChangeSkin(consoleplayer) && !P_PlayerMoving(consoleplayer)) + { + UINT8 i; + SendNameAndColor(0); + // check to see if there's anyone else at all + // even if we're playing splitscreen, if it ain't free play it spectates us if it can. + if (G_GametypeHasSpectators() && !players[consoleplayer].spectator) // Make sure we CAN spectate. + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (i == consoleplayer) + continue; + if (playeringame[i] && !players[i].spectator && gamestate == GS_LEVEL) + COM_ImmedExecute("changeteam spectator"); + } + } + } else { CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n")); @@ -5441,7 +5457,7 @@ static void Skin_OnChange(void) } /** Sends a skin change for the secondary splitscreen player, unless that - * player is moving. + * player is moving. Forces spectate the player if the change is done during gameplay. * \sa cv_skin2, Skin_OnChange, Color2_OnChange * \author Graue */ @@ -5451,7 +5467,12 @@ static void Skin2_OnChange(void) return; // do whatever you want if (CanChangeSkin(g_localplayers[1]) && !P_PlayerMoving(g_localplayers[1])) + { SendNameAndColor(1); + // With how we handle splitscreen, only check for gamestate here. + if (gamestate == GS_LEVEL && G_GametypeHasSpectators() && !players[g_localplayers[1]].spectator) + COM_ImmedExecute("changeteam2 spectator"); + } else { CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n")); @@ -5465,7 +5486,11 @@ static void Skin3_OnChange(void) return; // do whatever you want if (CanChangeSkin(g_localplayers[2]) && !P_PlayerMoving(g_localplayers[2])) + { SendNameAndColor(2); + if (gamestate == GS_LEVEL && G_GametypeHasSpectators() && !players[g_localplayers[2]].spectator) + COM_ImmedExecute("changeteam3 spectator"); + } else { CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n")); @@ -5479,7 +5504,11 @@ static void Skin4_OnChange(void) return; // do whatever you want if (CanChangeSkin(g_localplayers[3]) && !P_PlayerMoving(g_localplayers[3])) + { SendNameAndColor(3); + if (gamestate == GS_LEVEL && G_GametypeHasSpectators() && !players[g_localplayers[3]].spectator) + COM_ImmedExecute("changeteam4 spectator"); + } else { CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n")); diff --git a/src/k_menudef.c b/src/k_menudef.c index ee9213bb7..55beeac82 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -329,7 +329,7 @@ menuitem_t PAUSE_Main[] = NULL, NULL, 0, 0}, {IT_STRING | IT_CALL, "PLAYER SETUP", "M_ICOCHR", - NULL, NULL, 0, 0}, + NULL, M_CharacterSelectInit, 0, 0}, {IT_STRING | IT_CALL, "OPTIONS", "M_ICOOPT", NULL, NULL, 0, 0}, diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 2f1943081..c9c085a32 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1870,6 +1870,8 @@ void M_QuitSRB2(INT32 choice) // ========= // Character Select! +// @TODO: Splitscreen handling when profiles are added into the game. ...I probably won't be the one to handle this however. -Lat' + struct setup_chargrid_s setup_chargrid[9][9]; setup_player_t setup_player[MAXSPLITSCREENPLAYERS]; struct setup_explosions_s setup_explosions[48]; @@ -1921,6 +1923,7 @@ void M_CharacterSelectInit(INT32 choice) } } + PLAY_CharSelectDef.prevMenu = currentMenu; M_SetupNextMenu(&PLAY_CharSelectDef, false); } @@ -2172,6 +2175,48 @@ void M_CharacterSelectHandler(INT32 choice) } } +// Apply character skin and colour changes while ingame (we just call the skin / color commands.) +// ...Will this cause command buffer issues? -Lat' +static void M_MPConfirmCharacterSelection(void) +{ + UINT8 i; + INT16 col; + + char colstr[8]; + char commandnames[][2][MAXSTRINGLENGTH] = { {"skin ", "color "}, {"skin2 ", "color2 "}, {"skin3 ", "color3 "}, {"skin4 ", "color4 "}}; + // ^ laziness 100 (we append a space directly so that we don't have to do it later too!!!!) + + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + char cmd[MAXSTRINGLENGTH]; + + // skin + strcpy(cmd, commandnames[i][0]); + strcat(cmd, skins[setup_player[i].skin].name); + + COM_ImmedExecute(cmd); + + // colour + // (convert the number that's saved to a string we can use) + col = setup_player[i].color; + itoa(col, colstr, 10); + strcpy(cmd, commandnames[i][1]); + strcat(cmd, colstr); + + COM_ImmedExecute(cmd); + } + + M_ClearMenus(true); +} + +static void M_MPConfirmCharacterResponse(INT32 ch) +{ + if (ch == 'y' || ch == KEY_ENTER) + M_MPConfirmCharacterSelection(); + + M_ClearMenus(true); +} + void M_CharacterSelectTick(void) { UINT8 i; @@ -2207,14 +2252,48 @@ void M_CharacterSelectTick(void) if (setupnext) { - for (i = 0; i < setup_numplayers; i++) - { - CV_StealthSet(&cv_skin[i], skins[setup_player[i].skin].name); - CV_StealthSetValue(&cv_playercolor[i], setup_player[i].color); - } - CV_StealthSetValue(&cv_splitplayers, setup_numplayers); - M_SetupNextMenu(&PLAY_MainDef, false); + // Selecting from the menu + if (gamestate == GS_MENU) + { + for (i = 0; i < setup_numplayers; i++) + { + CV_StealthSet(&cv_skin[i], skins[setup_player[i].skin].name); + CV_StealthSetValue(&cv_playercolor[i], setup_player[i].color); + } + + CV_StealthSetValue(&cv_splitplayers, setup_numplayers); + M_SetupNextMenu(&PLAY_MainDef, false); + } + else // In a game + { + // In the midst of a game, + // 1: warn players that confirming will force-spectate them until next round + // ^ This doesn't apply in FREEPLAY + + // 2: Call the "skin" and "color" commands for all local players. + // This command will force change team to spectate under the proper circumstances. (see d_clisrv.c) + UINT8 j; + + // check to see if there's anyone else at all + if (G_GametypeHasSpectators()) // Make sure we CAN spectate. + { + for (j = 0; j < MAXPLAYERS; j++) + { + if (j == displayplayers[0]) + continue; + if (playeringame[j] && !players[consoleplayer].spectator) + { + // Warn the player! + M_StartMessage(M_GetText("Any player who has changed skin will\nautomatically spectate. Proceed?\n(Press 'Y' to confirm)\n"), M_MPConfirmCharacterResponse, MM_YESNO); + return; + } + } + } + + // If we made it here then we're in freeplay or something and we can switch for free! + M_MPConfirmCharacterSelection(); + } } } From 17e45d92c5b742f00bd7ace3618a4dcaf55751c5 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 19 Nov 2021 23:58:35 +0100 Subject: [PATCH 037/379] Pause: addons menu --- src/k_menu.h | 20 +++ src/k_menudef.c | 25 +++- src/k_menudraw.c | 227 ++++++++++++++++++++++++++++++++- src/k_menufunc.c | 326 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 592 insertions(+), 6 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 20dfcab17..713b37837 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -199,6 +199,9 @@ extern menu_t PLAY_BattleGamemodesDef; extern menuitem_t PAUSE_Main[]; extern menu_t PAUSE_MainDef; +extern menuitem_t MISC_Addons[]; +extern menu_t MISC_AddonsDef; + // We'll need this since we're gonna have to dynamically enable and disable options depending on which state we're in. typedef enum { @@ -471,8 +474,20 @@ void M_PlaybackQuit(INT32 choice); void M_ReplayHut(INT32 choice); +// Misc menus: +#define numaddonsshown 4 +void M_Addons(INT32 choice); +boolean M_AddonsRefresh(void); +void M_HandleAddons(INT32 choice); +char *M_AddonsHeaderPath(void); + // M_MENUDRAW.C +// flags for text highlights +#define highlightflags V_ORANGEMAP +#define recommendedflags V_GREENMAP +#define warningflags V_GRAYMAP + void M_UpdateMenuBGImage(boolean forceReset); void M_DrawMenuBackground(void); void M_DrawMenuForeground(void); @@ -501,6 +516,11 @@ void M_DrawPause(void); // Replay Playback void M_DrawPlaybackMenu(void); +// Misc menus: +#define LOCATIONSTRING1 "Visit \x83SRB2.ORG/MODS\x80 to get & make addons!" +#define LOCATIONSTRING2 "Visit \x88SRB2.ORG/MODS\x80 to get & make addons!" +void M_DrawAddons(void); + // These defines make it a little easier to make menus #define DEFAULTMENUSTYLE(source, prev, x, y)\ {\ diff --git a/src/k_menudef.c b/src/k_menudef.c index 55beeac82..f4f6fe7c3 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -303,7 +303,7 @@ menuitem_t PAUSE_Main[] = { {IT_STRING | IT_CALL, "ADDONS", "M_ICOADD", - NULL, NULL, 0, 0}, + NULL, M_Addons, 0, 0}, {IT_STRING | IT_SUBMENU, "CHANGE MAP", "M_ICOMAP", NULL, &PAUSE_GamemodesDef, 0, 0}, @@ -399,3 +399,26 @@ menu_t PAUSE_PlaybackMenuDef = { NULL, NULL }; + + +// Other misc menus: + +// Addons menu! (Just a straight port for now) +menuitem_t MISC_AddonsMenu[] = +{ + {IT_KEYHANDLER | IT_NOTHING, NULL, NULL, + NULL, M_HandleAddons, 0, 0}, // dummy menuitem for the control func +}; + +menu_t MISC_AddonsDef = { + sizeof (MISC_AddonsMenu)/sizeof (menuitem_t), + NULL, + 0, + MISC_AddonsMenu, + 50, 28, + 0, 0, + M_DrawAddons, + NULL, + NULL, + NULL +}; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index d6efce47d..ec6d9af64 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -71,11 +71,6 @@ int snprintf(char *str, size_t n, const char *fmt, ...); //int vsnprintf(char *str, size_t n, const char *fmt, va_list ap); #endif -// flags for text highlights -#define highlightflags V_ORANGEMAP -#define recommendedflags V_GREENMAP -#define warningflags V_GRAYMAP - #define SKULLXOFF -32 #define LINEHEIGHT 16 #define STRINGHEIGHT 8 @@ -85,6 +80,8 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #define SLIDER_WIDTH (8*SLIDER_RANGE+6) #define SERVERS_PER_PAGE 11 +static patch_t *addonsp[NUM_EXT+5]; + static UINT32 bgTextScroll = 0; static UINT32 bgImageScroll = 0; static char bgImageName[9]; @@ -1877,3 +1874,223 @@ void M_DrawPlaybackMenu(void) } } } + + +// Draw misc menus: + +// Addons (this is merely copypasted, original code by toaster) + +#define lsheadingheight 16 + +#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, 0); + V_DrawFill(x + width, vpadding, 1, h, 0); + V_DrawFill(x - 1, vpadding-1, width+2, 1, 0); + V_DrawFill(x - 1, vpadding+h, width+2, 1, 0); + + // 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 + + +// Just do this here instead. +static void M_CacheAddonPatches(void) +{ + 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); +} + + +void M_DrawAddons(void) +{ + INT32 x, y; + ssize_t i, m; + const UINT8 *flashcol = NULL; + UINT8 hilicol; + + M_CacheAddonPatches(); + + // 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 + { + y = FixedDiv(((ssize_t)(numwadfiles) - (ssize_t)(mainwads+1))< 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)[0]; + + 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, 159); + + // 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]); +} diff --git a/src/k_menufunc.c b/src/k_menufunc.c index c9c085a32..fe4807e0d 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3262,3 +3262,329 @@ static void Splitplayers_OnChange(void) setupm_pselect = 1; #endif } + +// Misc menus + +// Addons menu: (Merely copypasted, original code by toaster) + +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", LOCATIONSTRING1),NULL,MM_NOTHING); + return; + } + else + dir_on[menudepthleft] = 0; + + MISC_AddonsDef.prevMenu = currentMenu; + M_SetupNextMenu(&MISC_AddonsDef, false); +} + + +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, false);\ + 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 +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 addons reached.\nA file could not be loaded.\nIf you wish to play with this addon, 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\nYou've loaded a gameplay-modifying addon.\n\nRecord Attack has been disabled, but you\ncan still play alone in local Multiplayer.\n\nIf you wish to play Record Attack mode, restart the game to disable loaded addons.\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_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 + +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: + 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, false); + else + M_ClearMenus(true); + } +} From 3449064b8b87b2da6e4d8151aa17beb792db29b5 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Thu, 25 Nov 2021 23:36:42 +0100 Subject: [PATCH 038/379] options menu + video options (res & ogl) --- src/hardware/hw_main.c | 3 +- src/k_menu.h | 71 +++++++++ src/k_menudef.c | 188 ++++++++++++++++++++++- src/k_menudraw.c | 337 +++++++++++++++++++++++++++++++++++++++++ src/k_menufunc.c | 251 ++++++++++++++++++++++++++++++ 5 files changed, 846 insertions(+), 4 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index ed0523119..f53393e20 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -6479,6 +6479,7 @@ void HWR_AddCommands(void) CV_RegisterVar(&cv_glshearing); CV_RegisterVar(&cv_glshaders); CV_RegisterVar(&cv_glallowshaders); + CV_RegisterVar(&cv_glanisotropicmode); CV_RegisterVar(&cv_glfiltermode); CV_RegisterVar(&cv_glsolvetjoin); @@ -6494,7 +6495,7 @@ void HWR_AddSessionCommands(void) { if (gl_sessioncommandsadded) return; - CV_RegisterVar(&cv_glanisotropicmode); + // Kept in case we ever need this again. gl_sessioncommandsadded = true; } diff --git a/src/k_menu.h b/src/k_menu.h index 713b37837..2aa4fb540 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -196,6 +196,22 @@ extern menu_t PLAY_MP_RoomSelectDef; extern menuitem_t PLAY_BattleGamemodesMenu[]; extern menu_t PLAY_BattleGamemodesDef; +// OPTIONS +extern menuitem_t OPTIONS_Main[]; +extern menu_t OPTIONS_MainDef; + +extern menuitem_t OPTIONS_Video[]; +extern menu_t OPTIONS_VideoDef; + +extern menuitem_t OPTIONS_VideoModes[]; +extern menu_t OPTIONS_VideoModesDef; + +#ifdef HWRENDER +extern menuitem_t OPTIONS_VideoOGL[]; +extern menu_t OPTIONS_VideoOGLDef; +#endif + +// PAUSE extern menuitem_t PAUSE_Main[]; extern menu_t PAUSE_MainDef; @@ -431,6 +447,55 @@ void M_MPRoomSelect(INT32 choice); void M_MPRoomSelectTick(void); void M_MPRoomSelectInit(INT32 choice); +// Options menu: + +// mode descriptions for video mode menu +typedef struct +{ + INT32 modenum; // video mode number in the vidmodes list + const char *desc; // XXXxYYY + UINT8 goodratio; // aspect correct if 1 +} modedesc_t; + + +#define MAXCOLUMNMODES 12 //max modes displayed in one column +#define MAXMODEDESCS (MAXCOLUMNMODES*3) +// Keep track of some options properties +extern struct optionsmenu_s { + + tic_t ticker; // How long the menu's been open for + INT16 offset; // To make the icons move smoothly when we transition! + + tic_t buttflash; // Button flashing before transitionning to the new submenu. + + // For moving the button when we get into a submenu. it's smooth and cool! (normal x/y and target x/y.) + // this is only used during menu transitions. + INT16 optx; + INT16 opty; + INT16 toptx; + INT16 topty; + + // for video mode testing: + INT32 vidm_testingmode; + INT32 vidm_previousmode; + INT32 vidm_selected; + INT32 vidm_nummodes; + INT32 vidm_column_size; + + modedesc_t modedescs[MAXMODEDESCS]; +} optionsmenu; + +void M_InitOptions(INT32 choice); // necessary for multiplayer since there's some options we won't want to access +void M_OptionsTick(void); +boolean M_OptionsInputs(INT32 ch); + +boolean M_OptionsQuit(void); // resets buttons when you quit the options. + +// video modes menu (resolution) + +void M_VideoModeMenu(INT32 choice); +void M_HandleVideoModes(INT32 ch); + // Pause menu: // Keep track of some pause menu data for visual goodness. @@ -516,6 +581,12 @@ void M_DrawPause(void); // Replay Playback void M_DrawPlaybackMenu(void); +// Options menus: +void M_DrawOptionsMovingButton(void); // for sick transitions... +void M_DrawOptions(void); +void M_DrawGenericOptions(void); +void M_DrawVideoModes(void); + // Misc menus: #define LOCATIONSTRING1 "Visit \x83SRB2.ORG/MODS\x80 to get & make addons!" #define LOCATIONSTRING2 "Visit \x88SRB2.ORG/MODS\x80 to get & make addons!" diff --git a/src/k_menudef.c b/src/k_menudef.c index f4f6fe7c3..860f64a0e 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -3,6 +3,9 @@ #include "k_menu.h" #include "screen.h" // BASEVIDWIDTH +#include "r_main.h" // cv_skybox +#include "v_video.h" // cv_globalgamma +#include "hardware/hw_main.h" // gl consvars // ========================================================================== // ORGANIZATION START. @@ -26,9 +29,9 @@ menuitem_t MainMenu[] = "Check out some bonus features.", "MENUI001", NULL, 0, 0}, - {IT_STRING, "Option", + {IT_STRING, "Options", "Configure your controls, settings, and preferences.", NULL, - NULL, 0, 0}, + M_InitOptions, 0, 0}, {IT_STRING | IT_CALL, "Quit", "Exit \"Dr. Robotnik's Ring Racers\".", NULL, @@ -291,6 +294,185 @@ menu_t PLAY_MP_RoomSelectDef = { NULL }; +// options menu +menuitem_t OPTIONS_Main[] = +{ + + {IT_STRING | IT_SUBMENU, "Control Setup", "Remap keys & buttons to your likings.", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_SUBMENU, "Video Options", "Change video settings such as the resolution.", + NULL, &OPTIONS_VideoDef, 0, 0}, + + {IT_STRING | IT_SUBMENU, "Sound Options", "Adjust various sound settings such as the volume.", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_SUBMENU, "HUD Options", "Options related to the Heads-Up Display.", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_SUBMENU, "Gameplay Options", "Change various game related options", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_SUBMENU, "Server Options", "Change various specific options for your game server.", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_SUBMENU, "Data Options", "Miscellaneous data options such as the screenshot format.", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_SUBMENU, "Tricks & Secrets", "Those who bother reading a game manual always get the edge over those who don't!", + NULL, NULL, 0, 0}, +}; + +menu_t OPTIONS_MainDef = { + sizeof (OPTIONS_Main) / sizeof (menuitem_t), + &MainDef, + 0, + OPTIONS_Main, + 0, 0, + 2, 10, + M_DrawOptions, + M_OptionsTick, + NULL, + M_OptionsInputs +}; + +// video options menu... +// options menu +menuitem_t OPTIONS_Video[] = +{ + + {IT_STRING | IT_CALL, "Set Resolution...", "Change the screen resolution for the game.", + NULL, M_VideoModeMenu, 0, 0}, + +// A check to see if you're not running on a fucking antique potato powered stone i guess??????? + +#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL) + {IT_STRING | IT_CVAR, "Fullscreen", "Set whether you want to use fullscreen or windowed mode.", + NULL, &cv_fullscreen, 0, 0}, +#endif + + {IT_NOTHING|IT_SPACE, NULL, "Kanade best waifu! I promise!", + NULL, NULL, 0, 0}, + + // Everytime I see a screenshot at max gamma I die inside + {IT_STRING | IT_CVAR | IT_CV_SLIDER, "Gamma", "Adjusts the overall brightness of the game.", + NULL, &cv_globalgamma, 0, 0}, + + {IT_STRING | IT_CVAR, "Vertical Sync", "Locks the framerate to your monitor's refresh rate.", + NULL, &cv_vidwait, 0, 0}, + + {IT_STRING | IT_CVAR, "Enable Skyboxes", "Turning this off will improve performance at the detriment of visuals for many maps.", + NULL, &cv_skybox, 0, 0}, + + {IT_STRING | IT_CVAR, "Draw Distance", "How far objects can be drawn. Lower values may improve performance at the cost of visibility.", + NULL, &cv_drawdist, 0, 0}, + + {IT_STRING | IT_CVAR, "Weather Draw Distance", "Affects how far weather visuals can be drawn. Lower values improve performance.", + NULL, &cv_drawdist_precip, 0, 0}, + + {IT_STRING | IT_CVAR, "Show FPS", "Displays the game framerate at the lower right corner of the screen.", + NULL, &cv_ticrate, 0, 0}, + + {IT_NOTHING|IT_SPACE, NULL, "Kanade best waifu! I promise!", + NULL, NULL, 0, 0}, + +#ifdef HWRENDER + {IT_STRING | IT_SUBMENU, "Hardware Options...", "For usage and configuration of the OpenGL renderer.", + NULL, &OPTIONS_VideoOGLDef, 0, 0}, +#endif + +}; + +menu_t OPTIONS_VideoDef = { + sizeof (OPTIONS_Video) / sizeof (menuitem_t), + &OPTIONS_MainDef, + 0, + OPTIONS_Video, + 32, 80, + 2, 10, + M_DrawGenericOptions, + M_OptionsTick, + NULL, + NULL, +}; + +menuitem_t OPTIONS_VideoModes[] = { + + {IT_KEYHANDLER | IT_NOTHING, NULL, "Select a resolution.", + NULL, M_HandleVideoModes, 0, 0}, // dummy menuitem for the control func + +}; + +menu_t OPTIONS_VideoModesDef = { + sizeof (OPTIONS_VideoModes) / sizeof (menuitem_t), + &OPTIONS_VideoDef, + 0, + OPTIONS_VideoModes, + 48, 80, + 2, 10, + M_DrawVideoModes, + M_OptionsTick, + NULL, + NULL, +}; + +#ifdef HWRENDER +menuitem_t OPTIONS_VideoOGL[] = +{ + + {IT_STRING | IT_CVAR, "Renderer", "Change renderers between Software and OpenGL", + NULL, &cv_renderer, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_SPACE | IT_NOTHING | IT_STRING, "OPTIONS BELOW ARE OPENGL ONLY!", "Watch people get confused anyway!!", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR, "3D Models", "Use 3D models instead of sprites when applicable.", + NULL, &cv_glmodels, 0, 0}, + + {IT_STRING | IT_CVAR, "Shaders", "Use GLSL Shaders. Turning them off increases performance at the expanse of visual quality.", + NULL, &cv_glshaders, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR, "Texture Quality", "Texture depth. Higher values are recommended.", + NULL, &cv_scr_depth, 0, 0}, + + {IT_STRING | IT_CVAR, "Texture Filter", "Texture Filter. Nearest is recommended.", + NULL, &cv_glfiltermode, 0, 0}, + + {IT_STRING | IT_CVAR, "Anisotropic", "Lower values will improve performance at a minor quality loss.", + NULL, &cv_glanisotropicmode, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR, "Wall Contrast Style", "Allows faking or not Software wall colour contrast.", + NULL, &cv_glfakecontrast, 0, 0}, + + {IT_STRING | IT_CVAR, "Sprite Billboarding", "Adjusts sprites when viewed from above or below to not make them appear flat.", + NULL, &cv_glspritebillboarding, 0, 0}, + + {IT_STRING | IT_CVAR, "Software Perspective", "Emulates Software shearing when looking up or down. Not recommended.", + NULL, &cv_glshearing, 0, 0}, +}; + +menu_t OPTIONS_VideoOGLDef = { + sizeof (OPTIONS_VideoOGL) / sizeof (menuitem_t), + &OPTIONS_VideoDef, + 0, + OPTIONS_VideoOGL, + 32, 80, + 2, 10, + M_DrawGenericOptions, + M_OptionsTick, + NULL, + NULL, +}; +#endif // ------------------- // In-game/pause menus @@ -332,7 +514,7 @@ menuitem_t PAUSE_Main[] = NULL, M_CharacterSelectInit, 0, 0}, {IT_STRING | IT_CALL, "OPTIONS", "M_ICOOPT", - NULL, NULL, 0, 0}, + NULL, M_InitOptions, 0, 0}, {IT_STRING | IT_CALL, "EXIT GAME", "M_ICOEXT", NULL, M_EndGame, 0, 0}, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index ec6d9af64..ce26c9189 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -80,6 +80,74 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #define SLIDER_WIDTH (8*SLIDER_RANGE+6) #define SERVERS_PER_PAGE 11 + +// horizontally centered text +static void M_CentreText(INT32 xoffs, 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) + xoffs; + V_DrawString(x,y,V_OLDSPACING,string); +} + + +// 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); +} + + static patch_t *addonsp[NUM_EXT+5]; static UINT32 bgTextScroll = 0; @@ -1582,6 +1650,275 @@ void M_DrawMPRoomSelect(void) V_DrawFixedPatch(160<numitems; i++) + { + INT32 py = y - (itemOn*48); + INT32 px = x - menutransition.tics*64; + + if (i == itemOn) + c = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE); + else + c = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_BLACK, GTC_CACHE); + + if (!(menutransition.tics && i == itemOn)) + { + V_DrawFixedPatch(px*FRACUNIT, py*FRACUNIT, FRACUNIT, 0, buttback, c); + V_DrawCenteredGamemodeString(px-3, py - 16, V_ALLOWLOWERCASE, (i == itemOn ? c : NULL), currentMenu->menuitems[i].text); + } + + y += 48; + x += 48; + } + + M_DrawMenuTooltips(); + + if (menutransition.tics) + M_DrawOptionsMovingButton(); + +} + +void M_DrawGenericOptions(void) +{ + INT32 x = currentMenu->x - menutransition.tics*48, y = currentMenu->y, w, i, cursory = 0; + + M_DrawOptionsCogs(); + M_DrawMenuTooltips(); + M_DrawOptionsMovingButton(); + + 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 += SMALLLINEHEIGHT; + break; +#if 0 + case IT_BIGSLIDER: + M_DrawThermo(x, y, (consvar_t *)currentMenu->menuitems[i].itemaction); + y += LINEHEIGHT; + break; +#endif + case IT_STRING: + case IT_WHITESTRING: + 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; + 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_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: + case IT_SPACE: + 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].mvar1) + y = currentMenu->y+currentMenu->menuitems[i].mvar1; + /* FALLTHRU */ + case IT_TRANSTEXT2: + V_DrawString(x, y, V_TRANSLUCENT, currentMenu->menuitems[i].text); + y += SMALLLINEHEIGHT; + break; + case IT_QUESTIONMARKS: + if (currentMenu->menuitems[i].mvar1) + y = currentMenu->y+currentMenu->menuitems[i].mvar1; + + 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].mvar1) + y = currentMenu->y+currentMenu->menuitems[i].mvar1; + + 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(x + SKULLXOFF, cursory - 5, 0, + W_CachePatchName("M_CURSOR", PU_CACHE)); + } + else + { + V_DrawScaledPatch(x - 24, cursory, 0, + W_CachePatchName("M_CURSOR", PU_CACHE)); + V_DrawString(x, cursory, highlightflags, currentMenu->menuitems[itemOn].text); + } +} + +// Draw the video modes list, a-la-Quake +void M_DrawVideoModes(void) +{ + INT32 i, j, row, col; + + M_DrawOptionsCogs(); + M_DrawMenuTooltips(); + M_DrawOptionsMovingButton(); + + V_DrawCenteredString(BASEVIDWIDTH/2 + menutransition.tics*64, currentMenu->y, + highlightflags, "Choose mode, reselect to change default"); + + row = 41 + menutransition.tics*64; + col = currentMenu->y + 14; + for (i = 0; i < optionsmenu.vidm_nummodes; i++) + { + if (i == optionsmenu.vidm_selected) + V_DrawString(row, col, highlightflags, optionsmenu.modedescs[i].desc); + // Show multiples of 320x200 as green. + else + V_DrawString(row, col, (optionsmenu.modedescs[i].goodratio) ? recommendedflags : 0, optionsmenu.modedescs[i].desc); + + col += 8; + if ((i % optionsmenu.vidm_column_size) == (optionsmenu.vidm_column_size-1)) + { + row += 7*13; + col = currentMenu->y + 14; + } + } + + if (optionsmenu.vidm_testingmode > 0) + { + INT32 testtime = (optionsmenu.vidm_testingmode/TICRATE) + 1; + + M_CentreText(menutransition.tics*64, currentMenu->y + 75, + va("Previewing mode %c%dx%d", + (SCR_IsAspectCorrect(vid.width, vid.height)) ? 0x83 : 0x80, + vid.width, vid.height)); + M_CentreText(menutransition.tics*64, currentMenu->y + 75+8, + "Press ENTER again to keep this mode"); + M_CentreText(menutransition.tics*64, currentMenu->y + 75+16, + va("Wait %d second%s", testtime, (testtime > 1) ? "s" : "")); + M_CentreText(menutransition.tics*64, currentMenu->y + 75+24, + "or press ESC to return"); + + } + else + { + M_CentreText(menutransition.tics*64, currentMenu->y + 75, + va("Current mode is %c%dx%d", + (SCR_IsAspectCorrect(vid.width, vid.height)) ? 0x83 : 0x80, + vid.width, vid.height)); + M_CentreText(menutransition.tics*64, currentMenu->y + 75+8, + 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 + menutransition.tics*64, currentMenu->y + 75+16, + recommendedflags, "Marked modes are recommended."); + V_DrawCenteredString(BASEVIDWIDTH/2 + menutransition.tics*64, currentMenu->y + 75+24, + highlightflags, "Other modes may have visual errors."); + V_DrawCenteredString(BASEVIDWIDTH/2 + menutransition.tics*64, currentMenu->y + 75+32, + highlightflags, "Larger modes may have performance issues."); + } + + // Draw the cursor for the VidMode menu + i = 41 - 10 + ((optionsmenu.vidm_selected / optionsmenu.vidm_column_size)*7*13) + menutransition.tics*64; + j = currentMenu->y + 14 + ((optionsmenu.vidm_selected % optionsmenu.vidm_column_size)*8); + + V_DrawScaledPatch(i - 8, j, 0, + W_CachePatchName("M_CURSOR", PU_CACHE)); +} + + // // INGAME / PAUSE MENUS // diff --git a/src/k_menufunc.c b/src/k_menufunc.c index fe4807e0d..6e043bbaf 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2943,6 +2943,257 @@ void M_MPRoomSelectInit(INT32 choice) M_SetupNextMenu(&PLAY_MP_RoomSelectDef, false); } +// Options menu: +struct optionsmenu_s optionsmenu; + +void M_InitOptions(INT32 choice) +{ + (void)choice; + + // @TODO: Change options when you do them from a netgame. + + optionsmenu.ticker = 0; + optionsmenu.offset = 0; + + optionsmenu.optx = 0; + optionsmenu.opty = 0; + optionsmenu.toptx = 0; + optionsmenu.topty = 0; + + OPTIONS_MainDef.prevMenu = currentMenu; + + M_SetupNextMenu(&OPTIONS_MainDef, false); +} + +boolean M_OptionsQuit(void) +{ + optionsmenu.toptx = 140-1; + optionsmenu.topty = 70+1; + + return true; // Always allow quitting, duh. +} + +void M_OptionsTick(void) +{ + optionsmenu.offset /= 2; + optionsmenu.ticker++; + + optionsmenu.optx += (optionsmenu.toptx - optionsmenu.optx)/2; + optionsmenu.opty += (optionsmenu.topty - optionsmenu.opty)/2; + + if (abs(optionsmenu.optx - optionsmenu.opty) < 2) + { + optionsmenu.optx = optionsmenu.toptx; + optionsmenu.opty = optionsmenu.topty; // Avoid awkward 1 px errors. + } + + // Garbage: + if (currentMenu == &OPTIONS_MainDef) + { + M_OptionsQuit(); // ...So now this is used here. + } + else + { + optionsmenu.toptx = 160; + optionsmenu.topty = 50; + } + +} + +boolean M_OptionsInputs(INT32 ch) +{ + + switch (ch) + { + case KEY_DOWNARROW: + { + optionsmenu.offset += 48; + M_NextOpt(); + + if (itemOn == 0) + optionsmenu.offset -= currentMenu->numitems*48; + + return true; + } + case KEY_UPARROW: + { + optionsmenu.offset -= 48; + M_PrevOpt(); + + if (itemOn == currentMenu->numitems-1) + optionsmenu.offset += currentMenu->numitems*48; + + return true; + } + case KEY_ENTER: + { + optionsmenu.optx = 140; + optionsmenu.opty = 70; // Default position for the currently selected option. + + return false; // Don't eat. + } + + } + + return false; +} + +// setup video mode menu +void M_VideoModeMenu(INT32 choice) +{ + INT32 i, j, vdup, nummodes, width, height; + const char *desc; + + (void)choice; + + memset(optionsmenu.modedescs, 0, sizeof(optionsmenu.modedescs)); + +#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL) + VID_PrepareModeList(); // FIXME: hack +#endif + optionsmenu.vidm_nummodes = 0; + optionsmenu.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 && optionsmenu.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 < optionsmenu.vidm_nummodes; j++) + { + if (!strcmp(optionsmenu.modedescs[j].desc, desc)) + { + // mode(0): 320x200 is always standard VGA, not vesa + if (optionsmenu.modedescs[j].modenum) + { + optionsmenu.modedescs[j].modenum = i; + vdup = 1; + + if (i == vid.modenum) + optionsmenu.vidm_selected = j; + } + else + vdup = 1; + + break; + } + } + + if (!vdup) + { + optionsmenu.modedescs[optionsmenu.vidm_nummodes].modenum = i; + optionsmenu.modedescs[optionsmenu.vidm_nummodes].desc = desc; + + if (i == vid.modenum) + optionsmenu.vidm_selected = optionsmenu.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)) + optionsmenu.modedescs[optionsmenu.vidm_nummodes].goodratio = 1; + + optionsmenu.vidm_nummodes++; + } + } + } + + optionsmenu.vidm_column_size = (optionsmenu.vidm_nummodes+2) / 3; + + M_SetupNextMenu(&OPTIONS_VideoModesDef, false); +} + + +// special menuitem key handler for video mode list +void M_HandleVideoModes(INT32 ch) +{ + if (optionsmenu.vidm_testingmode > 0) switch (ch) + { + // change back to the previous mode quickly + case KEY_ESCAPE: + setmodeneeded = optionsmenu.vidm_previousmode + 1; + optionsmenu.vidm_testingmode = 0; + break; + + case KEY_ENTER: + S_StartSound(NULL, sfx_menu1); + optionsmenu.vidm_testingmode = 0; // stop testing + } + + else switch (ch) + { + case KEY_DOWNARROW: + S_StartSound(NULL, sfx_menu1); + if (++optionsmenu.vidm_selected >= optionsmenu.vidm_nummodes) + optionsmenu.vidm_selected = 0; + break; + + case KEY_UPARROW: + S_StartSound(NULL, sfx_menu1); + if (--optionsmenu.vidm_selected < 0) + optionsmenu.vidm_selected = optionsmenu.vidm_nummodes - 1; + break; + + case KEY_LEFTARROW: + S_StartSound(NULL, sfx_menu1); + optionsmenu.vidm_selected -= optionsmenu.vidm_column_size; + if (optionsmenu.vidm_selected < 0) + optionsmenu.vidm_selected = (optionsmenu.vidm_column_size*3) + optionsmenu.vidm_selected; + if (optionsmenu.vidm_selected >= optionsmenu.vidm_nummodes) + optionsmenu.vidm_selected = optionsmenu.vidm_nummodes - 1; + break; + + case KEY_RIGHTARROW: + S_StartSound(NULL, sfx_menu1); + optionsmenu.vidm_selected += optionsmenu.vidm_column_size; + if (optionsmenu.vidm_selected >= (optionsmenu.vidm_column_size*3)) + optionsmenu.vidm_selected %= optionsmenu.vidm_column_size; + if (optionsmenu.vidm_selected >= optionsmenu.vidm_nummodes) + optionsmenu.vidm_selected = optionsmenu.vidm_nummodes - 1; + break; + + case KEY_ENTER: + S_StartSound(NULL, sfx_menu1); + if (vid.modenum == optionsmenu.modedescs[optionsmenu.vidm_selected].modenum) + SCR_SetDefaultMode(); + else + { + optionsmenu.vidm_testingmode = 15*TICRATE; + optionsmenu.vidm_previousmode = vid.modenum; + if (!setmodeneeded) // in case the previous setmode was not finished + setmodeneeded = optionsmenu.modedescs[optionsmenu.vidm_selected].modenum + 1; + } + break; + + case KEY_ESCAPE: // this one same as M_Responder + if (currentMenu->prevMenu) + M_SetupNextMenu(currentMenu->prevMenu, false); + else + M_ClearMenus(true); + break; + + default: + break; + } +} + + // ===================== // PAUSE / IN-GAME MENUS // ===================== From 9c08483505d010a4411ebd9b0348a32b0e93de6e Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 26 Nov 2021 09:04:08 +0100 Subject: [PATCH 039/379] options: sound & hud --- src/k_menu.h | 9 +++ src/k_menudef.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 177 insertions(+), 2 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 2aa4fb540..b273cc581 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -211,6 +211,15 @@ extern menuitem_t OPTIONS_VideoOGL[]; extern menu_t OPTIONS_VideoOGLDef; #endif +extern menuitem_t OPTIONS_Sound[]; +extern menu_t OPTIONS_SoundDef; + +extern menuitem_t OPTIONS_HUD[]; +extern menu_t OPTIONS_HUDDef; + +extern menuitem_t OPTIONS_HUDOnline[]; +extern menu_t OPTIONS_HUDOnlineDef; + // PAUSE extern menuitem_t PAUSE_Main[]; extern menu_t PAUSE_MainDef; diff --git a/src/k_menudef.c b/src/k_menudef.c index 860f64a0e..6abb80e56 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -6,6 +6,9 @@ #include "r_main.h" // cv_skybox #include "v_video.h" // cv_globalgamma #include "hardware/hw_main.h" // gl consvars +#include "s_sound.h" // sounds consvars +#include "g_game.h" // cv_chatnotifications +#include "console.h" // console cvars // ========================================================================== // ORGANIZATION START. @@ -305,10 +308,10 @@ menuitem_t OPTIONS_Main[] = NULL, &OPTIONS_VideoDef, 0, 0}, {IT_STRING | IT_SUBMENU, "Sound Options", "Adjust various sound settings such as the volume.", - NULL, NULL, 0, 0}, + NULL, &OPTIONS_SoundDef, 0, 0}, {IT_STRING | IT_SUBMENU, "HUD Options", "Options related to the Heads-Up Display.", - NULL, NULL, 0, 0}, + NULL, &OPTIONS_HUDDef, 0, 0}, {IT_STRING | IT_SUBMENU, "Gameplay Options", "Change various game related options", NULL, NULL, 0, 0}, @@ -474,6 +477,169 @@ menu_t OPTIONS_VideoOGLDef = { }; #endif +menuitem_t OPTIONS_Sound[] = +{ + + {IT_STRING | IT_CVAR, "SFX", "Enable or disable sound effect playback.", + NULL, &cv_gamesounds, 0, 0}, + + {IT_STRING | IT_CVAR | IT_CV_SLIDER, "SFX Volume", "Adjust the volume of sound effects.", + NULL, &cv_soundvolume, 0, 0}, + + {IT_STRING | IT_CVAR, "Music", "Enable or disable music playback.", + NULL, &cv_gamedigimusic, 0, 0}, + + {IT_STRING | IT_CVAR | IT_CV_SLIDER, "Music Volume", "Adjust the volume of music playback.", + NULL, &cv_digmusicvolume, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR, "Reverse L/R Channels", "Reverse left & right channels for Stereo playback.", + NULL, &stereoreverse, 0, 0}, + + {IT_STRING | IT_CVAR, "Surround", "Enables or disable Surround sound playback.", + NULL, &surround, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR, "Chat Notifications", "Set when to play notification sounds when chat messages are received.", + NULL, &cv_chatnotifications, 0, 0}, + + {IT_STRING | IT_CVAR, "Character Voices", "Set how often to play character voices in game.", + NULL, &cv_kartvoices, 0, 0}, + + {IT_STRING | IT_CVAR, "Powerup Warning", "Set how to warn you from other player's powerups such as Invincibility.", + NULL, &cv_kartinvinsfx, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR, "Play Music While Unfocused", "Keeps playing music even if the game is not the active window.", + NULL, &cv_playmusicifunfocused, 0, 0}, + + {IT_STRING | IT_CVAR, "Play SFX While Unfocused", "Keeps playing sound effects even if the game is not the active window.", + NULL, &cv_playsoundifunfocused, 0, 0}, + + // @TODO: Sound test (there's currently no space on this menu, might be better to throw it in extras?) +}; + +menu_t OPTIONS_SoundDef = { + sizeof (OPTIONS_Sound) / sizeof (menuitem_t), + &OPTIONS_MainDef, + 0, + OPTIONS_Sound, + 48, 80, + 2, 10, + M_DrawGenericOptions, + M_OptionsTick, + NULL, + NULL, +}; + +menuitem_t OPTIONS_HUD[] = +{ + + {IT_STRING | IT_CVAR, "Show HUD (F3)", "Toggles HUD display. Great for taking screenshots!", + NULL, &cv_showhud, 0, 0}, + + {IT_STRING | IT_CVAR | IT_CV_SLIDER, "HUD Opacity", "Non opaque values may have performance impacts in software mode.", + NULL, &cv_translucenthud, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR | IT_CV_SLIDER, "Minimap Opacity", "Changes the opacity of the minimap.", + NULL, &cv_kartminimap, 0, 0}, + + {IT_STRING | IT_CVAR, "Speedometer Display", "Choose to what speed unit to display or toggle off the speedometer.", + NULL, &cv_kartspeedometer, 0, 0}, + + {IT_STRING | IT_CVAR, "Display \"CHECK\"", "Displays an icon when a player is tailing you.", + NULL, &cv_kartcheck, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR, "Console Text Size", "Size of the text within the console.", + NULL, &cv_constextsize, 0, 0}, + + // we spell words properly here. + {IT_STRING | IT_CVAR, "Console Tint", "Change the background colour of the console.", + NULL, &cons_backcolor, 0, 0}, + + {IT_STRING | IT_CVAR, "Show \"FOCUS LOST\"", "Displays \"FOCUS LOST\" when the game window isn't the active window.", + NULL, &cv_showfocuslost, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_SUBMENU, "Online HUD Options...", "HUD options related to the online chat box and other features.", + NULL, &OPTIONS_HUDOnlineDef, 0, 0}, +}; + +menu_t OPTIONS_HUDDef = { + sizeof (OPTIONS_HUD) / sizeof (menuitem_t), + &OPTIONS_MainDef, + 0, + OPTIONS_HUD, + 48, 80, + 2, 10, + M_DrawGenericOptions, + M_OptionsTick, + NULL, + NULL, +}; + +menuitem_t OPTIONS_HUDOnline[] = +{ + + {IT_STRING | IT_CVAR, "Chat Mode", "Choose whether to display chat in its own window or the console.", + NULL, &cv_consolechat, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR, "Chat Box Tint", "Changes the background colour of the chat box.", + NULL, &cv_chatbacktint, 0, 0}, + + {IT_STRING | IT_CVAR | IT_CV_SLIDER, "Chat Box Width", "Change the width of the Chat Box", + NULL, &cv_chatwidth, 0, 0}, + + {IT_STRING | IT_CVAR | IT_CV_SLIDER, "Chat Box Height", "Change the height of the Chat Box", + NULL, &cv_chatheight, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR, "Message Fadeout Time", "How long chat messages stay displayed with the chat closed.", + NULL, &cv_chattime, 0, 0}, + + {IT_STRING | IT_CVAR, "Spam Protection", "Prevents too many message from a single player from being displayed.", + NULL, &cv_chatspamprotection, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR, "Local Ping Display", "In netgames, displays your ping at the lower right corner of the screen.", + NULL, &cv_showping, 0, 0}, + +}; + +menu_t OPTIONS_HUDOnlineDef = { + sizeof (OPTIONS_HUDOnline) / sizeof (menuitem_t), + &OPTIONS_HUDDef, + 0, + OPTIONS_HUDOnline, + 48, 80, + 2, 10, + M_DrawGenericOptions, + M_OptionsTick, + NULL, + NULL, +}; + // ------------------- // In-game/pause menus // ------------------- From 76bba0fc9cd695ac92ffd501f796bf583a533588 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 26 Nov 2021 10:18:50 +0100 Subject: [PATCH 040/379] options: gameplay --- src/k_menu.h | 10 +++ src/k_menudef.c | 100 ++++++++++++++++++++++++++++- src/k_menudraw.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++ src/k_menufunc.c | 98 +++++++++++++++++++++++++++++ 4 files changed, 367 insertions(+), 2 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index b273cc581..0f8e0b237 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -220,6 +220,13 @@ extern menu_t OPTIONS_HUDDef; extern menuitem_t OPTIONS_HUDOnline[]; extern menu_t OPTIONS_HUDOnlineDef; +extern menuitem_t OPTIONS_Gameplay[]; +extern menu_t OPTIONS_GameplayDef; + +extern menuitem_t OPTIONS_GameplayItems[]; +extern menu_t OPTIONS_GameplayItemsDef; + + // PAUSE extern menuitem_t PAUSE_Main[]; extern menu_t PAUSE_MainDef; @@ -500,6 +507,8 @@ boolean M_OptionsInputs(INT32 ch); boolean M_OptionsQuit(void); // resets buttons when you quit the options. +void M_HandleItemToggles(INT32 choice); // For item toggling + // video modes menu (resolution) void M_VideoModeMenu(INT32 choice); @@ -595,6 +604,7 @@ void M_DrawOptionsMovingButton(void); // for sick transitions... void M_DrawOptions(void); void M_DrawGenericOptions(void); void M_DrawVideoModes(void); +void M_DrawItemToggles(void); // Misc menus: #define LOCATIONSTRING1 "Visit \x83SRB2.ORG/MODS\x80 to get & make addons!" diff --git a/src/k_menudef.c b/src/k_menudef.c index 6abb80e56..8e2df8a5c 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -314,7 +314,7 @@ menuitem_t OPTIONS_Main[] = NULL, &OPTIONS_HUDDef, 0, 0}, {IT_STRING | IT_SUBMENU, "Gameplay Options", "Change various game related options", - NULL, NULL, 0, 0}, + NULL, &OPTIONS_GameplayDef, 0, 0}, {IT_STRING | IT_SUBMENU, "Server Options", "Change various specific options for your game server.", NULL, NULL, 0, 0}, @@ -553,7 +553,7 @@ menuitem_t OPTIONS_HUD[] = {IT_STRING | IT_CVAR | IT_CV_SLIDER, "Minimap Opacity", "Changes the opacity of the minimap.", NULL, &cv_kartminimap, 0, 0}, - {IT_STRING | IT_CVAR, "Speedometer Display", "Choose to what speed unit to display or toggle off the speedometer.", + {IT_STRING | IT_CVAR, "Speedometer", "Choose to what speed unit to display or toggle off the speedometer.", NULL, &cv_kartspeedometer, 0, 0}, {IT_STRING | IT_CVAR, "Display \"CHECK\"", "Displays an icon when a player is tailing you.", @@ -640,6 +640,102 @@ menu_t OPTIONS_HUDOnlineDef = { NULL, }; + +menuitem_t OPTIONS_Gameplay[] = +{ + + {IT_STRING | IT_CVAR, "Game Speed", "Change Game Speed for the next map.", + NULL, &cv_kartspeed, 0, 0}, + + {IT_STRING | IT_CVAR, "Base Lap Count", "Change how many laps must be completed per race.", + NULL, &cv_kartspeed, 0, 0}, + + {IT_STRING | IT_CVAR, "Frantic Items", "Make item odds crazier with more powerful items!", + NULL, &cv_kartfrantic, 0, 0}, + + {IT_STRING | IT_CVAR, "Encore Mode", "Forces Encore Mode on for the next map.", + NULL, &cv_kartencore, 0, 0}, + + {IT_STRING | IT_CVAR, "Exit Countdown", "How long players have to finish after 1st place finishes.", + NULL, &cv_countdowntime, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR, "Time Limit", "Change the time limit for Battle rounds.", + NULL, &cv_timelimit, 0, 0}, + + {IT_STRING | IT_CVAR, "Starting Bumpers", "Change how many bumpers player start with in Battle.", + NULL, &cv_kartbumpers, 0, 0}, + + {IT_STRING | IT_CVAR, "Karma Comeback", "Enable Karma Comeback in Battle mode.", + NULL, &cv_kartcomeback, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_SUBMENU, "Random Item Toggles...", "Change which items to enable for your games.", + NULL, &OPTIONS_GameplayItemsDef, 0, 0}, + +}; + +menu_t OPTIONS_GameplayDef = { + sizeof (OPTIONS_Gameplay) / sizeof (menuitem_t), + &OPTIONS_MainDef, + 0, + OPTIONS_Gameplay, + 48, 80, + 2, 10, + M_DrawGenericOptions, + M_OptionsTick, + NULL, + NULL, +}; + +menuitem_t OPTIONS_GameplayItems[] = +{ + // Mostly handled by the drawing function. + {IT_KEYHANDLER | IT_NOTHING, "Sneakers", NULL, NULL, M_HandleItemToggles, KITEM_SNEAKER, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Sneakers x3", NULL, NULL, M_HandleItemToggles, KRITEM_TRIPLESNEAKER, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Toggle All", NULL, NULL, M_HandleItemToggles, 0, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Rocket Sneakers", NULL, NULL, M_HandleItemToggles, KITEM_ROCKETSNEAKER, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Bananas", NULL, NULL, M_HandleItemToggles, KITEM_BANANA, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Bananas x3", NULL, NULL, M_HandleItemToggles, KRITEM_TRIPLEBANANA, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Bananas x10", NULL, NULL, M_HandleItemToggles, KRITEM_TENFOLDBANANA, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Eggman Monitors", NULL, NULL, M_HandleItemToggles, KITEM_EGGMAN, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Orbinauts", NULL, NULL, M_HandleItemToggles, KITEM_ORBINAUT, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Orbinauts x3", NULL, NULL, M_HandleItemToggles, KRITEM_TRIPLEORBINAUT, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Orbinauts x4", NULL, NULL, M_HandleItemToggles, KRITEM_QUADORBINAUT, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Mines", NULL, NULL, M_HandleItemToggles, KITEM_MINE, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Jawz", NULL, NULL, M_HandleItemToggles, KITEM_JAWZ, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Jawz x2", NULL, NULL, M_HandleItemToggles, KRITEM_DUALJAWZ, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Ballhogs", NULL, NULL, M_HandleItemToggles, KITEM_BALLHOG, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Self-Propelled Bombs", NULL, NULL, M_HandleItemToggles, KITEM_SPB, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Invinciblity", NULL, NULL, M_HandleItemToggles, KITEM_INVINCIBILITY, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Grow", NULL, NULL, M_HandleItemToggles, KITEM_GROW, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Shrink", NULL, NULL, M_HandleItemToggles, KITEM_SHRINK, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Thunder Shields", NULL, NULL, M_HandleItemToggles, KITEM_THUNDERSHIELD, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Bubble Shields", NULL, NULL, M_HandleItemToggles, KITEM_BUBBLESHIELD, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Flame Shields", NULL, NULL, M_HandleItemToggles, KITEM_FLAMESHIELD, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Hyudoros", NULL, NULL, M_HandleItemToggles, KITEM_HYUDORO, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Pogo Springs", NULL, NULL, M_HandleItemToggles, KITEM_POGOSPRING, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Super Rings", NULL, NULL, M_HandleItemToggles, KITEM_SUPERRING, 0}, + {IT_KEYHANDLER | IT_NOTHING, "Kitchen Sinks", NULL, NULL, M_HandleItemToggles, KITEM_KITCHENSINK, 0}, +}; + +menu_t OPTIONS_GameplayItemsDef = { + sizeof (OPTIONS_GameplayItems) / sizeof (menuitem_t), + &OPTIONS_GameplayDef, + 0, + OPTIONS_GameplayItems, + 0, 75, + 2, 10, + M_DrawItemToggles, + M_OptionsTick, + NULL, + NULL, +}; + // ------------------- // In-game/pause menus // ------------------- diff --git a/src/k_menudraw.c b/src/k_menudraw.c index ce26c9189..e8e242c87 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1918,6 +1918,167 @@ void M_DrawVideoModes(void) W_CachePatchName("M_CURSOR", PU_CACHE)); } +// Gameplay Item Tggles: +static tic_t shitsfree = 0; + +void M_DrawItemToggles(void) +{ + const INT32 edges = 9; + const INT32 height = 3; + const INT32 spacing = 35; + const INT32 column = itemOn/height; + //const INT32 row = itemOn%height; + INT32 leftdraw, rightdraw, totaldraw; + INT32 x = currentMenu->x + menutransition.tics*64, y = currentMenu->y+(spacing/4); + INT32 onx = 0, ony = 0; + consvar_t *cv; + INT32 i, translucent, drawnum; + + M_DrawOptionsCogs(); + M_DrawMenuTooltips(); + M_DrawOptionsMovingButton(); + + // 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; + } + + if (currentMenu->menuitems[thisitem].mvar1 == 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 = KartItemCVars[currentMenu->menuitems[thisitem].mvar1-1]; + translucent = (cv->value ? 0 : V_TRANSLUCENT); + + switch (currentMenu->menuitems[thisitem].mvar1) + { + case KRITEM_DUALSNEAKER: + 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].mvar1, 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].mvar1, true), PU_CACHE)); + + y += spacing; + } + + x += spacing; + y = currentMenu->y+(spacing/4); + } + + { + if (currentMenu->menuitems[itemOn].mvar1 == 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 = KartItemCVars[currentMenu->menuitems[itemOn].mvar1-1]; + translucent = (cv->value ? 0 : V_TRANSLUCENT); + + switch (currentMenu->menuitems[itemOn].mvar1) + { + case KRITEM_DUALSNEAKER: + 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].mvar1, 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].mvar1, false), PU_CACHE)); + } + } + + if (shitsfree) + shitsfree--; + + V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y, highlightflags, va("* %s *", currentMenu->menuitems[itemOn].text)); +} + // // INGAME / PAUSE MENUS diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 6e043bbaf..b91276feb 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3193,6 +3193,104 @@ void M_HandleVideoModes(INT32 ch) } } +void M_HandleItemToggles(INT32 choice) +{ + const INT32 width = 9, height = 3; + 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].mvar1 == 255) + { + //S_StartSound(NULL, sfx_s26d); + if (!shitsfree) + { + shitsfree = TICRATE; + S_StartSound(NULL, sfx_itfree); + } + } + else +#endif + if (currentMenu->menuitems[itemOn].mvar1 == 0) + { + INT32 v = cv_sneaker.value; + S_StartSound(NULL, sfx_s1b4); + for (i = 0; i < NUMKARTRESULTS-1; i++) + { + if (KartItemCVars[i]->value == v) + CV_AddValue(KartItemCVars[i], 1); + } + } + else + { + S_StartSound(NULL, sfx_s1ba); + CV_AddValue(KartItemCVars[currentMenu->menuitems[itemOn].mvar1-1], 1); + } + break; + + case KEY_ESCAPE: + exitmenu = true; + break; + } + + if (exitmenu) + { + if (currentMenu->prevMenu) + M_SetupNextMenu(currentMenu->prevMenu, false); + else + M_ClearMenus(true); + } +} + // ===================== // PAUSE / IN-GAME MENUS From 6bea4a393a8a4dda8d953eee27a895d28107dcab Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 26 Nov 2021 10:55:33 +0100 Subject: [PATCH 041/379] options: server options --- src/k_menu.h | 7 +++ src/k_menudef.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 129 insertions(+), 1 deletion(-) diff --git a/src/k_menu.h b/src/k_menu.h index 0f8e0b237..fea9b40e5 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -226,6 +226,13 @@ extern menu_t OPTIONS_GameplayDef; extern menuitem_t OPTIONS_GameplayItems[]; extern menu_t OPTIONS_GameplayItemsDef; +extern menuitem_t OPTIONS_Server[]; +extern menu_t OPTIONS_ServerDef; + +#ifndef NONET +extern menuitem_t OPTIONS_ServerAdvanced[]; +extern menu_t OPTIONS_ServerAdvancedDef; +#endif // PAUSE extern menuitem_t PAUSE_Main[]; diff --git a/src/k_menudef.c b/src/k_menudef.c index 8e2df8a5c..cf4f131d8 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -317,7 +317,7 @@ menuitem_t OPTIONS_Main[] = NULL, &OPTIONS_GameplayDef, 0, 0}, {IT_STRING | IT_SUBMENU, "Server Options", "Change various specific options for your game server.", - NULL, NULL, 0, 0}, + NULL, &OPTIONS_ServerDef, 0, 0}, {IT_STRING | IT_SUBMENU, "Data Options", "Miscellaneous data options such as the screenshot format.", NULL, NULL, 0, 0}, @@ -736,6 +736,127 @@ menu_t OPTIONS_GameplayItemsDef = { NULL, }; +menuitem_t OPTIONS_Server[] = +{ + + {IT_STRING | IT_CVAR | IT_CV_STRING, "Server Name", "Change the name of your server.", + NULL, &cv_servername, 0, 0}, + + {IT_STRING | IT_CVAR, "Intermission", "Set how long to stay on the result screen.", + NULL, &cv_inttime, 0, 0}, + + {IT_STRING | IT_CVAR, "Map Progression", "Set how the next map is chosen.", + NULL, &cv_advancemap, 0, 0}, + + {IT_STRING | IT_CVAR, "Vote Timer", "Set how long players have to vote.", + NULL, &cv_votetime, 0, 0}, + + {IT_STRING | IT_CVAR, "Vote Mode Change", "Set how often voting proposes a different gamemode.", + NULL, &cv_kartvoterulechanges, 0, 0}, + +#ifndef NONET + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR, "Ingame Max. Players", "How many players can play at once. 0 Allows everyone who joins.", + NULL, &cv_ingamecap, 0, 0}, + + {IT_STRING | IT_CVAR, "Server Max. Players", "How many players can connect to the server.", + NULL, &cv_maxplayers, 0, 0}, + + {IT_STRING | IT_CVAR, "Allow Joining", "Sets whether players can connect to your server.", + NULL, &cv_allownewplayer, 0, 0}, + + {IT_STRING | IT_CVAR, "Allow Downloads", "Allows joiners to download missing files from you.", + NULL, &cv_downloading, 0, 0}, + + {IT_STRING | IT_CVAR, "Pause Permissions", "Sets who can pause the game.", + NULL, &cv_pause, 0, 0}, + + {IT_STRING | IT_CVAR, "Mute Chat", "Prevents non-admins from sending chat messages.", + NULL, &cv_mute, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_SUBMENU, "Advanced...", "Advanced options. Be careful when messing with these!", + NULL, &OPTIONS_ServerAdvancedDef, 0, 0}, + +#endif +}; + +menu_t OPTIONS_ServerDef = { + sizeof (OPTIONS_Server) / sizeof (menuitem_t), + &OPTIONS_MainDef, + 0, + OPTIONS_Server, + 48, 70, // This menu here is slightly higher because there's a lot of options... + 2, 10, + M_DrawGenericOptions, + M_OptionsTick, + NULL, + NULL, +}; + +#ifndef NONET +menuitem_t OPTIONS_ServerAdvanced[] = +{ + + {IT_STRING | IT_CVAR | IT_CV_STRING, "Server Browser Address", "Default is \'https://ms.kartkew.org/ms/api\'", + NULL, &cv_masterserver, 0, 0}, + + {IT_STRING | IT_CVAR, "Resynch. Attempts", "How many times to attempt sending data to desynchronized players.", + NULL, &cv_resynchattempts, 0, 0}, + + {IT_STRING | IT_CVAR, "Ping Limit (ms)", "Players above the ping limit will get kicked from the server.", + NULL, &cv_maxping, 0, 0}, + + {IT_STRING | IT_CVAR, "Ping Timeout (s)", "Players must be above the ping limit for this long before being kicked.", + NULL, &cv_pingtimeout, 0, 0}, + + {IT_STRING | IT_CVAR, "Connection Timeout (tics)", "Players not giving any netowrk activity for this long are kicked.", + NULL, &cv_nettimeout, 0, 0}, + + {IT_STRING | IT_CVAR, "Join Timeout (tics)", "Players taking too long to join are kicked.", + NULL, &cv_jointimeout, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR, "Max File Transfer", "Maximum size of the files that can be downloaded from joining clients. (KB)", + NULL, &cv_maxsend, 0, 0}, + + {IT_STRING | IT_CVAR, "File Transfer Speed", "File transfer packet rate. Larger values send more data.", + NULL, &cv_downloadspeed, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR, "Log Joiner IPs", "Shows the IP of connecting players.", + NULL, &cv_showjoinaddress, 0, 0}, + + {IT_STRING | IT_CVAR, "Log Resynch", "Shows which players need resynchronization.", + NULL, &cv_blamecfail, 0, 0}, + + {IT_STRING | IT_CVAR, "Log Transfers", "Shows when clients are downloading files from you.", + NULL, &cv_noticedownload, 0, 0}, +}; + +menu_t OPTIONS_ServerAdvancedDef = { + sizeof (OPTIONS_ServerAdvanced) / sizeof (menuitem_t), + &OPTIONS_ServerDef, + 0, + OPTIONS_ServerAdvanced, + 48, 70, // This menu here is slightly higher because there's a lot of options... + 2, 10, + M_DrawGenericOptions, + M_OptionsTick, + NULL, + NULL, +}; +#endif + // ------------------- // In-game/pause menus // ------------------- From c377ea1186183429d18f875a6b08cf3a00b185b0 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 26 Nov 2021 16:36:15 +0100 Subject: [PATCH 042/379] options: data & manual --- src/k_menu.h | 49 +++++++++- src/k_menudef.c | 248 ++++++++++++++++++++++++++++++++++++++++++++++- src/k_menudraw.c | 6 +- src/k_menufunc.c | 77 +++++++++++++-- 4 files changed, 359 insertions(+), 21 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index fea9b40e5..99f651713 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -234,6 +234,26 @@ extern menuitem_t OPTIONS_ServerAdvanced[]; extern menu_t OPTIONS_ServerAdvancedDef; #endif +extern menuitem_t OPTIONS_Data[]; +extern menu_t OPTIONS_DataDef; + +extern menuitem_t OPTIONS_DataScreenshot[]; +extern menu_t OPTIONS_DataScreenshotDef; + +extern menuitem_t OPTIONS_DataAddon[]; +extern menu_t OPTIONS_DataAddonDef; + +extern menuitem_t OPTIONS_DataReplay[]; +extern menu_t OPTIONS_DataReplayDef; + +#ifdef HAVE_DISCORDRPC +extern menuitem_t OPTIONS_DataDiscord[]; +extern menu_t OPTIONS_DataDiscordDef; +#endif + +extern menuitem_t OPTIONS_DataErase[]; +extern menu_t OPTIONS_DataEraseDef; + // PAUSE extern menuitem_t PAUSE_Main[]; extern menu_t PAUSE_MainDef; @@ -241,6 +261,10 @@ extern menu_t PAUSE_MainDef; extern menuitem_t MISC_Addons[]; extern menu_t MISC_AddonsDef; +// MANUAL +extern menuitem_t MISC_Manual[]; +extern menu_t MISC_ManualDef; + // We'll need this since we're gonna have to dynamically enable and disable options depending on which state we're in. typedef enum { @@ -327,8 +351,6 @@ extern menu_t MessageDef; void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtype); void M_StopMessage(INT32 choice); -void M_HandleImageDef(INT32 choice); - void M_QuitResponse(INT32 ch); void M_QuitSRB2(INT32 choice); @@ -506,15 +528,16 @@ extern struct optionsmenu_s { INT32 vidm_column_size; modedesc_t modedescs[MAXMODEDESCS]; + + UINT8 erasecontext; } optionsmenu; void M_InitOptions(INT32 choice); // necessary for multiplayer since there's some options we won't want to access void M_OptionsTick(void); boolean M_OptionsInputs(INT32 ch); - boolean M_OptionsQuit(void); // resets buttons when you quit the options. - void M_HandleItemToggles(INT32 choice); // For item toggling +void M_EraseData(INT32 choice); // For data erasing // video modes menu (resolution) @@ -571,6 +594,9 @@ boolean M_AddonsRefresh(void); void M_HandleAddons(INT32 choice); char *M_AddonsHeaderPath(void); +void M_Manual(INT32 choice); +void M_HandleImageDef(INT32 choice); + // M_MENUDRAW.C // flags for text highlights @@ -648,4 +674,19 @@ void M_DrawAddons(void); NULL\ } +#define IMAGEDEF(source)\ +{\ + sizeof(source) / sizeof(menuitem_t),\ + NULL,\ + 0,\ + source,\ + 0, 0,\ + 1, 10,\ + M_DrawImageDef,\ + NULL,\ + NULL,\ + NULL\ +} + + #endif //__K_MENU__ diff --git a/src/k_menudef.c b/src/k_menudef.c index cf4f131d8..824facbd6 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -9,6 +9,9 @@ #include "s_sound.h" // sounds consvars #include "g_game.h" // cv_chatnotifications #include "console.h" // console cvars +#include "filesrch.h" // addons cvars +#include "m_misc.h" // screenshot cvars +#include "discord.h" // discord rpc cvars // ========================================================================== // ORGANIZATION START. @@ -320,10 +323,10 @@ menuitem_t OPTIONS_Main[] = NULL, &OPTIONS_ServerDef, 0, 0}, {IT_STRING | IT_SUBMENU, "Data Options", "Miscellaneous data options such as the screenshot format.", - NULL, NULL, 0, 0}, + NULL, &OPTIONS_DataDef, 0, 0}, - {IT_STRING | IT_SUBMENU, "Tricks & Secrets", "Those who bother reading a game manual always get the edge over those who don't!", - NULL, NULL, 0, 0}, + {IT_STRING | IT_CALL, "Tricks & Secrets", "Those who bother reading a game manual always get the edge over those who don't!", + NULL, M_Manual, 0, 0}, }; menu_t OPTIONS_MainDef = { @@ -429,7 +432,7 @@ menuitem_t OPTIONS_VideoOGL[] = {IT_SPACE | IT_NOTHING, NULL, NULL, NULL, NULL, 0, 0}, - {IT_SPACE | IT_NOTHING | IT_STRING, "OPTIONS BELOW ARE OPENGL ONLY!", "Watch people get confused anyway!!", + {IT_HEADER, "OPTIONS BELOW ARE OPENGL ONLY!", "Watch people get confused anyway!!", NULL, NULL, 0, 0}, {IT_STRING | IT_CVAR, "3D Models", "Use 3D models instead of sprites when applicable.", @@ -803,7 +806,7 @@ menu_t OPTIONS_ServerDef = { menuitem_t OPTIONS_ServerAdvanced[] = { - {IT_STRING | IT_CVAR | IT_CV_STRING, "Server Browser Address", "Default is \'https://ms.kartkew.org/ms/api\'", + {IT_STRING | IT_CVAR | IT_CV_STRING, "Server Browser Address", "Default is \'https://ms.kartkrew.org/ms/api\'", NULL, &cv_masterserver, 0, 0}, {IT_STRING | IT_CVAR, "Resynch. Attempts", "How many times to attempt sending data to desynchronized players.", @@ -857,6 +860,221 @@ menu_t OPTIONS_ServerAdvancedDef = { }; #endif +menuitem_t OPTIONS_Data[] = +{ + + {IT_STRING | IT_SUBMENU, "Screenshot Options...", "Set options relative to screenshot and GIF capture.", + NULL, &OPTIONS_DataScreenshotDef, 0, 0}, + + {IT_STRING | IT_SUBMENU, "Addon Options...", "Set options relative to the addons menu.", + NULL, &OPTIONS_DataAddonDef, 0, 0}, + + {IT_STRING | IT_SUBMENU, "Replay Options...", "Set options relative to replays.", + NULL, &OPTIONS_DataReplayDef, 0, 0}, + +#ifdef HAVE_DISCORDRPC + {IT_STRING | IT_SUBMENU, "Discord Options...", "Set options relative to Discord Rich Presence.", + NULL, &OPTIONS_DataDiscordDef, 0, 0}, +#endif + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + // escape sequences don't like any letter from A to E following them... So let's also put E as an escape sequence lol. E is 69 (nice) which is 45 in hex. + {IT_STRING | IT_SUBMENU, "\x85\x45rase Data...", "Erase specific data. Be careful, what's deleted is gone forever!", + NULL, &OPTIONS_DataEraseDef, 0, 0}, + +}; + +menu_t OPTIONS_DataDef = { + sizeof (OPTIONS_Data) / sizeof (menuitem_t), + &OPTIONS_MainDef, + 0, + OPTIONS_Data, + 48, 80, + 2, 10, + M_DrawGenericOptions, + M_OptionsTick, + NULL, + NULL, +}; + +menuitem_t OPTIONS_DataAddon[] = +{ + + {IT_HEADER, "MENU", NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR, "Location", "Where to start searching addons from in the menu.", + NULL, &cv_addons_option, 0, 0}, + + {IT_STRING | IT_CVAR | IT_CV_STRING, "Custom Folder", "Specify which folder to start searching from if the location is set to custom.", + NULL, &cv_addons_folder, 24, 0}, + + {IT_STRING | IT_CVAR, "Identify Addons via", "Set whether to consider the extension or contents of a file.", + NULL, &cv_addons_md5, 0, 0}, + + {IT_STRING | IT_CVAR, "Show Unsupported Files", "Sets whether non-addon files should be shown.", + NULL, &cv_addons_showall, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_HEADER, "SEARCH", NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR, "Matching", "Set where to check for the text pattern when looking up addons via name.", + NULL, &cv_addons_search_type, 0, 0}, + + {IT_STRING | IT_CVAR, "Case Sensitivity", "Set whether to consider the case when searching for addons..", + NULL, &cv_addons_search_case, 0, 0}, + +}; + +menu_t OPTIONS_DataAddonDef = { + sizeof (OPTIONS_DataAddon) / sizeof (menuitem_t), + &OPTIONS_DataDef, + 0, + OPTIONS_DataAddon, + 48, 80, + 2, 10, + M_DrawGenericOptions, + M_OptionsTick, + NULL, + NULL, +}; + +menuitem_t OPTIONS_DataScreenshot[] = +{ + + {IT_HEADER, "SCREENSHOTS (F8)", NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR, "Storage Location", "Sets where to store screenshots.", + NULL, &cv_screenshot_option, 0, 0}, + + {IT_STRING | IT_CVAR | IT_CV_STRING, "Custom Folder", "Specify which folder to save screenshots in.", + NULL, &cv_screenshot_folder, 24, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_HEADER, "GIF RECORDING (F9)", NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR, "Storage Location", "Sets where to store GIFs", + NULL, &cv_movie_option, 0, 0}, + + {IT_STRING | IT_CVAR | IT_CV_STRING, "Custom Folder", "Specify which folder to save GIFs in.", + NULL, &cv_movie_folder, 24, 0}, + +}; + +menu_t OPTIONS_DataScreenshotDef = { + sizeof (OPTIONS_DataScreenshot) / sizeof (menuitem_t), + &OPTIONS_DataDef, + 0, + OPTIONS_DataScreenshot, + 48, 80, + 2, 10, + M_DrawGenericOptions, + M_OptionsTick, + NULL, + NULL, +}; + +menuitem_t OPTIONS_DataReplay[] = +{ + + {IT_STRING | IT_CVAR, "Rich Presence", "Allow Discord to display game info on your status.", + NULL, &cv_discordrp, 0, 0}, + + {IT_STRING | IT_CVAR, "Synch. Check Interval", "How often to check for synchronization while playing back a replay.", + NULL, &cv_netdemosyncquality, 0, 0}, +}; + +menu_t OPTIONS_DataReplayDef = { + sizeof (OPTIONS_DataReplay) / sizeof (menuitem_t), + &OPTIONS_DataDef, + 0, + OPTIONS_DataReplay, + 48, 80, + 2, 10, + M_DrawGenericOptions, + M_OptionsTick, + NULL, + NULL, +}; + +#ifdef HAVE_DISCORDRPC +menuitem_t OPTIONS_DataDiscord[] = +{ + + {IT_STRING | IT_CVAR, "Record Replays", "Select when to save replays.", + NULL, &cv_recordmultiplayerdemos, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_HEADER, "RICH PRESENCE SETTINGS", NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CVAR, "Streamer Mode", "Prevents the logging of some account information such as your tag in the console.", + NULL, &cv_discordstreamer, 0, 0}, + + {IT_STRING | IT_CVAR, "Allow Ask to Join", "Allow other people to request joining your game from Discord.", + NULL, &cv_discordasks, 0, 0}, + + {IT_STRING | IT_CVAR, "Allow Invites", "Set who is allowed to generate Discord invites to your game.", + NULL, &cv_discordinvites, 0, 0}, + +}; + +menu_t OPTIONS_DataDiscordDef = { + sizeof (OPTIONS_DataDiscord) / sizeof (menuitem_t), + &OPTIONS_DataDef, + 0, + OPTIONS_DataDiscord, + 48, 80, + 2, 10, + M_DrawGenericOptions, + M_OptionsTick, + NULL, + NULL, +}; +#endif + + +menuitem_t OPTIONS_DataErase[] = +{ + + {IT_STRING | IT_CALL, "Erase Time Attack Data", "Be careful! What's deleted is gone forever!", + NULL, M_EraseData, 0, 0}, + + {IT_STRING | IT_CALL, "Erase Unlockable Data", "Be careful! What's deleted is gone forever!", + NULL, M_EraseData, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CALL, "\x85\x45rase all Data", "Be careful! What's deleted is gone forever!", + NULL, M_EraseData, 0, 0}, + +}; + +menu_t OPTIONS_DataEraseDef = { + sizeof (OPTIONS_DataErase) / sizeof (menuitem_t), + &OPTIONS_DataDef, + 0, + OPTIONS_DataErase, + 48, 80, + 2, 10, + M_DrawGenericOptions, + M_OptionsTick, + NULL, + NULL, +}; + // ------------------- // In-game/pause menus // ------------------- @@ -968,6 +1186,26 @@ menu_t PAUSE_PlaybackMenuDef = { // Other misc menus: +// Manual +menuitem_t MISC_Manual[] = { + {IT_NOTHING | IT_KEYHANDLER, "MANUAL00", NULL, NULL, M_HandleImageDef, 0, 0}, + {IT_NOTHING | IT_KEYHANDLER, "MANUAL01", NULL, NULL, M_HandleImageDef, 1, 0}, + {IT_NOTHING | IT_KEYHANDLER, "MANUAL02", NULL, NULL, M_HandleImageDef, 1, 0}, + {IT_NOTHING | IT_KEYHANDLER, "MANUAL03", NULL, NULL, M_HandleImageDef, 1, 0}, + {IT_NOTHING | IT_KEYHANDLER, "MANUAL04", NULL, NULL, M_HandleImageDef, 1, 0}, + {IT_NOTHING | IT_KEYHANDLER, "MANUAL05", NULL, NULL, M_HandleImageDef, 1, 0}, + {IT_NOTHING | IT_KEYHANDLER, "MANUAL06", NULL, NULL, M_HandleImageDef, 1, 0}, + {IT_NOTHING | IT_KEYHANDLER, "MANUAL07", NULL, NULL, M_HandleImageDef, 1, 0}, + {IT_NOTHING | IT_KEYHANDLER, "MANUAL08", NULL, NULL, M_HandleImageDef, 1, 0}, + {IT_NOTHING | IT_KEYHANDLER, "MANUAL09", NULL, NULL, M_HandleImageDef, 1, 0}, + {IT_NOTHING | IT_KEYHANDLER, "MANUAL10", NULL, NULL, M_HandleImageDef, 1, 0}, + {IT_NOTHING | IT_KEYHANDLER, "MANUAL11", NULL, NULL, M_HandleImageDef, 1, 0}, + {IT_NOTHING | IT_KEYHANDLER, "MANUAL12", NULL, NULL, M_HandleImageDef, 1, 0}, + {IT_NOTHING | IT_KEYHANDLER, "MANUAL99", NULL, NULL, M_HandleImageDef, 0, 0}, +}; + +menu_t MISC_ManualDef = IMAGEDEF(MISC_Manual); + // Addons menu! (Just a straight port for now) menuitem_t MISC_AddonsMenu[] = { diff --git a/src/k_menudraw.c b/src/k_menudraw.c index e8e242c87..748a57c70 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1797,13 +1797,13 @@ void M_DrawGenericOptions(void) /* FALLTHRU */ case IT_DYLITLSPACE: case IT_SPACE: - y += SMALLLINEHEIGHT; + y += (currentMenu->menuitems[i].mvar1 ? : 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; + y += (currentMenu->menuitems[i].mvar1 ? : SMALLLINEHEIGHT); break; case IT_TRANSTEXT: if (currentMenu->menuitems[i].mvar1) @@ -2591,4 +2591,4 @@ void M_DrawAddons(void) if (modifiedgame) V_DrawSmallScaledPatch(x, y + 4, 0, addonsp[NUM_EXT+2]); -} +} \ No newline at end of file diff --git a/src/k_menufunc.c b/src/k_menufunc.c index b91276feb..86e5325bd 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -323,10 +323,10 @@ static void Dummystaff_OnChange(void) void Screenshot_option_Onchange(void) { -#if 0 - OP_ScreenshotOptionsMenu[op_screenshot_folder].status = + // Screenshot opt is at #3, 0 based array obv. + OPTIONS_DataScreenshot[2].status = (cv_screenshot_option.value == 3 ? IT_CVAR|IT_STRING|IT_CV_STRING : IT_DISABLED); -#endif + } void Moviemode_mode_Onchange(void) @@ -356,18 +356,17 @@ void Moviemode_mode_Onchange(void) void Moviemode_option_Onchange(void) { -#if 0 - OP_ScreenshotOptionsMenu[op_movie_folder].status = + // opt 7 in a 0 based array, you get the idea... + OPTIONS_DataScreenshot[6].status = (cv_movie_option.value == 3 ? IT_CVAR|IT_STRING|IT_CV_STRING : IT_DISABLED); -#endif } void Addons_option_Onchange(void) { -#if 0 - OP_AddonsOptionsMenu[op_addons_folder].status = + // Option 2 will always be the textbar. + // (keep in mind this is a 0 indexed array and the first element is a header...) + OPTIONS_DataAddon[2].status = (cv_addons_option.value == 3 ? IT_CVAR|IT_STRING|IT_CV_STRING : IT_DISABLED); -#endif } void M_SortServerList(void) @@ -399,6 +398,50 @@ void M_SortServerList(void) #endif } +static void M_EraseDataResponse(INT32 ch) +{ + UINT8 i; + + if (ch != 'y' && ch != KEY_ENTER) + return; + + S_StartSound(NULL, sfx_itrole); // bweh heh heh + + // Delete the data + if (optionsmenu.erasecontext == 2) + { + // SRB2Kart: This actually needs to be done FIRST, so that you don't immediately regain playtime/matches secrets + totalplaytime = 0; + matchesplayed = 0; + for (i = 0; i < PWRLV_NUMTYPES; i++) + vspowerlevel[i] = PWRLVRECORD_START; + } + if (optionsmenu.erasecontext != 1) + G_ClearRecords(); + if (optionsmenu.erasecontext != 0) + M_ClearSecrets(); + + F_StartIntro(); + M_ClearMenus(true); +} + +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"); + + optionsmenu.erasecontext = (UINT8)choice; + + if (choice == 0) + eschoice = M_GetText("Time 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); +} + + // ========================================================================= // BASIC MENU HANDLING // ========================================================================= @@ -2960,8 +3003,15 @@ void M_InitOptions(INT32 choice) optionsmenu.toptx = 0; optionsmenu.topty = 0; + // So that pause doesn't go to the main menu... OPTIONS_MainDef.prevMenu = currentMenu; + // This will disable or enable the textboxes of the affected menus before we get to them. + Screenshot_option_Onchange(); + Moviemode_mode_Onchange(); + Moviemode_option_Onchange(); + Addons_option_Onchange(); + M_SetupNextMenu(&OPTIONS_MainDef, false); } @@ -3937,3 +3987,12 @@ void M_HandleAddons(INT32 choice) M_ClearMenus(true); } } + +// Opening manual +void M_Manual(INT32 choice) +{ + (void)choice; + + MISC_ManualDef.prevMenu = (choice == INT32_MAX ? NULL : currentMenu); + M_SetupNextMenu(&MISC_ManualDef, true); +} \ No newline at end of file From bd8b960d5811e35b7a013a8b21851ad594c1b545 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 26 Nov 2021 14:30:04 -0500 Subject: [PATCH 043/379] New menus makefile --- src/Sourcefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Sourcefile b/src/Sourcefile index 384af8da7..4930ba312 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -29,7 +29,6 @@ m_bbox.c m_cheat.c m_cond.c m_fixed.c -m_menu.c m_misc.c m_perfstats.c m_random.c @@ -110,3 +109,6 @@ k_botitem.c k_botsearch.c k_grandprix.c k_hud.c +k_menudef.c +k_menufunc.c +k_menudraw.c From cd8862f0bc964dd30d9341495ae10e9a929d7df6 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 26 Nov 2021 14:56:25 -0500 Subject: [PATCH 044/379] New input handling Events have a player ID instead of adding billions of keys for separate gamepads. Axis movement (mouse movement, analog sticks) now are counted as keys, so axes don't need to be separately implemented for all controls. Game controls emulate a Saturn controller (some of the external functions like screenshot / gif should be readded, but I got lazy) This will allow later us to save a config for a controller that can be reused for any player slot, which is one of the main goals for profiles. Only just enough was made to use the new input system to make it compile. Menus in this branch should aim to move to using PlayerInputDown entirely, instead of using hardcoded keys & simply remapping to those --- src/am_map.c | 2 +- src/command.c | 57 +-- src/d_clisrv.c | 8 +- src/d_event.h | 4 +- src/d_main.c | 9 - src/d_netcmd.c | 12 - src/doomstat.h | 1 - src/f_finale.c | 9 +- src/g_game.c | 412 +++++++-------------- src/g_game.h | 31 +- src/g_input.c | 885 ++++++++++++-------------------------------- src/g_input.h | 116 ++---- src/hu_stuff.c | 12 +- src/k_menufunc.c | 17 +- src/m_misc.c | 18 +- src/p_tick.c | 2 +- src/p_user.c | 8 +- src/sdl/i_system.c | 2 +- src/sdl/i_video.c | 200 ++++------ src/win32/win_sys.c | 5 +- src/y_inter.c | 10 +- 21 files changed, 550 insertions(+), 1270 deletions(-) diff --git a/src/am_map.c b/src/am_map.c index 53a7480a5..04806de94 100644 --- a/src/am_map.c +++ b/src/am_map.c @@ -462,7 +462,7 @@ boolean AM_Responder(event_t *ev) { //faB: prevent alt-tab in win32 version to activate automap just before // minimizing the app; doesn't do any harm to the DOS version - if (!gamekeydown[KEY_LALT] && !gamekeydown[KEY_RALT]) + if (!gamekeydown[0][KEY_LALT] && !gamekeydown[0][KEY_RALT]) { bigstate = 0; //added : 24-01-98 : toggle off large view AM_Start(); diff --git a/src/command.c b/src/command.c index 6e551d24c..728aad1ba 100644 --- a/src/command.c +++ b/src/command.c @@ -2161,58 +2161,10 @@ static void CV_EnforceExecVersion(void) CV_StealthSetValue(&cv_execversion, EXECVERSION); } -static boolean CV_FilterJoyAxisVars(consvar_t *v, const char *valstr) -{ -#if 1 - // We don't have changed axis defaults yet - (void)v; - (void)valstr; -#else - UINT8 i; - - // If ALL axis settings are previous defaults, set them to the new defaults - // EXECVERSION < 26 (2.1.21) - - for (i = 0; i < 4; i++) - { - if (joyaxis_default[i]) - { - if (!stricmp(v->name, "joyaxis_fire")) - { - if (joyaxis_count[i] > 7) return false; - else if (joyaxis_count[i] == 7) return true; - - if (!stricmp(valstr, "None")) joyaxis_count[i]++; - else joyaxis_default[i] = false; - } - // reset all axis settings to defaults - if (joyaxis_count[i] == 7) - { - switch (i) - { - default: - COM_BufInsertText(va("%s \"%s\"\n", cv_turnaxis[0].name, cv_turnaxis[0].defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_moveaxis[0].name, cv_moveaxis[0].defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_brakeaxis[0].name, cv_brakeaxis[0].defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_aimaxis[0].name, cv_aimaxis[0].defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_lookaxis[0].name, cv_lookaxis[0].defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_fireaxis[0].name, cv_fireaxis[0].defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_driftaxis[0].name, cv_driftaxis[0].defaultvalue)); - break; - } - joyaxis_count[i]++; - return false; - } - } - } -#endif - - // we haven't reached our counts yet, or we're not default - return true; -} - static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr) { + (void)valstr; + // True means allow the CV change, False means block it // We only care about CV_SAVE because this filters the user's config files @@ -2230,11 +2182,6 @@ static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr) || !stricmp(v->name, "mousemove2")) return false; #endif - - // axis defaults were changed to be friendly to 360 controllers - // if ALL axis settings are defaults, then change them to new values - if (!CV_FilterJoyAxisVars(v, valstr)) - return false; } return true; diff --git a/src/d_clisrv.c b/src/d_clisrv.c index a9576bdcf..e39ef5227 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1494,7 +1494,7 @@ static void M_ConfirmConnect(event_t *ev) #ifndef NONET if (ev->type == ev_keydown) { - if (ev->data1 == ' ' || ev->data1 == 'y' || ev->data1 == KEY_ENTER || ev->data1 == gamecontrol[0][gc_accelerate][0] || ev->data1 == gamecontrol[0][gc_accelerate][1]) + if (ev->data1 == ' ' || ev->data1 == 'y' || ev->data1 == KEY_ENTER || ev->data1 == gamecontrol[0][gc_a][0] || ev->data1 == gamecontrol[0][gc_a][1]) { if (totalfilesrequestednum > 0) { @@ -1517,7 +1517,7 @@ static void M_ConfirmConnect(event_t *ev) M_ClearMenus(true); } - else if (ev->data1 == 'n' || ev->data1 == KEY_ESCAPE|| ev->data1 == gamecontrol[0][gc_brake][0] || ev->data1 == gamecontrol[0][gc_brake][1]) + else if (ev->data1 == 'n' || ev->data1 == KEY_ESCAPE|| ev->data1 == gamecontrol[0][gc_b][0] || ev->data1 == gamecontrol[0][gc_b][1]) { cl_mode = CL_ABORTED; M_ClearMenus(true); @@ -1923,7 +1923,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic if (cl_mode == CL_CONFIRMCONNECT) D_ProcessEvents(); //needed for menu system to receive inputs - if ((gamekeydown[KEY_ESCAPE] || gamekeydown[KEY_JOY1+1]) || cl_mode == CL_ABORTED) + if ((gamekeydown[0][KEY_ESCAPE] || gamekeydown[0][KEY_JOY1+1]) || cl_mode == CL_ABORTED) { CONS_Printf(M_GetText("Network game synchronization aborted.\n")); // M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING); @@ -1931,7 +1931,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic D_QuitNetGame(); CL_Reset(); D_StartTitle(); - memset(gamekeydown, 0, NUMKEYS); + memset(gamekeydown, 0, sizeof (gamekeydown)); return false; } diff --git a/src/d_event.h b/src/d_event.h index c69796573..5a8c915a3 100644 --- a/src/d_event.h +++ b/src/d_event.h @@ -25,9 +25,6 @@ typedef enum ev_console, ev_mouse, ev_joystick, - ev_joystick2, - ev_joystick3, - ev_joystick4, } evtype_t; // Event structure. @@ -37,6 +34,7 @@ typedef struct INT32 data1; // keys / mouse/joystick buttons INT32 data2; // mouse/joystick x move INT32 data3; // mouse/joystick y move + INT32 device; // which player's device it belongs to } event_t; // diff --git a/src/d_main.c b/src/d_main.c index 3a2259337..d71903d2e 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -908,15 +908,6 @@ void D_StartTitle(void) V_SetPaletteLump("PLAYPAL"); // The title screen is obviously not a tutorial! (Unless I'm mistaken) - /* - if (tutorialmode && tutorialgcs) - { - G_CopyControls(gamecontrol[0], gamecontroldefault[0][gcs_custom], gcl_full, num_gcl_full); // using gcs_custom as temp storage - M_StartMessage("Do you want to \x82save the recommended \x82movement controls?\x80\n\nPress 'Y' or 'Enter' to confirm\nPress 'N' or any key to keep \nyour current controls", - M_TutorialSaveControlResponse, MM_YESNO); - } - */ - tutorialmode = false; } diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 10e580d09..b5c3837df 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -925,15 +925,7 @@ void D_RegisterClientCommands(void) for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { CV_RegisterVar(&cv_kickstartaccel[i]); - CV_RegisterVar(&cv_turnaxis[i]); - CV_RegisterVar(&cv_moveaxis[i]); - CV_RegisterVar(&cv_brakeaxis[i]); - CV_RegisterVar(&cv_aimaxis[i]); - CV_RegisterVar(&cv_lookaxis[i]); - CV_RegisterVar(&cv_fireaxis[i]); - CV_RegisterVar(&cv_driftaxis[i]); CV_RegisterVar(&cv_deadzone[i]); - CV_RegisterVar(&cv_digitaldeadzone[i]); } // filesrch.c @@ -2766,10 +2758,6 @@ static void Command_Map_f(void) return; } - if (tutorialmode && tutorialgcs) - { - G_CopyControls(gamecontrol[0], gamecontroldefault[0][gcs_custom], gcl_full, num_gcl_full); // using gcs_custom as temp storage - } tutorialmode = false; // warping takes us out of tutorial mode D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, fromlevelselect); diff --git a/src/doomstat.h b/src/doomstat.h index 19359df3b..a4aba708b 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -183,7 +183,6 @@ extern INT16 bootmap; //bootmap for loading a map on startup extern INT16 tutorialmap; // map to load for tutorial extern boolean tutorialmode; // are we in a tutorial right now? -extern INT32 tutorialgcs; // which control scheme is loaded? extern boolean looptitle; diff --git a/src/f_finale.c b/src/f_finale.c index c43e17a28..d5450f2c7 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -2684,12 +2684,13 @@ void F_StartTextPrompt(INT32 promptnum, INT32 pagenum, mobj_t *mo, UINT16 postex static boolean F_GetTextPromptTutorialTag(char *tag, INT32 length) { - INT32 gcs = gcs_custom; + INT32 gcs = 0; boolean suffixed = true; if (!tag || !tag[0] || !tutorialmode) return false; + /* if (!strncmp(tag, "TAA", 3)) // Accelerate gcs = G_GetControlScheme(gamecontrol[0], gcl_accelerate, num_gcl_accelerate); else if (!strncmp(tag, "TAB", 3)) // Brake @@ -2704,14 +2705,10 @@ static boolean F_GetTextPromptTutorialTag(char *tag, INT32 length) gcs = G_GetControlScheme(gamecontrol[0], gcl_item, num_gcl_item); else gcs = G_GetControlScheme(gamecontrol[0], gcl_full, num_gcl_full); + */ switch (gcs) { - case gcs_kart: - // strncat(tag, "KART", length); - suffixed = false; - break; - default: strncat(tag, "CUSTOM", length); break; diff --git a/src/g_game.c b/src/g_game.c index 20f9b3b8e..fd42a4849 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -155,7 +155,6 @@ INT16 bootmap; //bootmap for loading a map on startup INT16 tutorialmap = 0; // map to load for tutorial boolean tutorialmode = false; // are we in a tutorial right now? -INT32 tutorialgcs = gcs_custom; // which control scheme is loaded? boolean looptitle = true; @@ -347,22 +346,6 @@ static void kickstartaccel3_OnChange(void); static void kickstartaccel4_OnChange(void); void SendWeaponPref(UINT8 n); -static CV_PossibleValue_t joyaxis_cons_t[] = {{0, "None"}, -{1, "X-Axis"}, {2, "Y-Axis"}, {-1, "X-Axis-"}, {-2, "Y-Axis-"}, -#if JOYAXISSET > 1 -{3, "Z-Axis"}, {4, "X-Rudder"}, {-3, "Z-Axis-"}, {-4, "X-Rudder-"}, -#endif -#if JOYAXISSET > 2 -{5, "Y-Rudder"}, {6, "Z-Rudder"}, {-5, "Y-Rudder-"}, {-6, "Z-Rudder-"}, -#endif -#if JOYAXISSET > 3 -{7, "U-Axis"}, {8, "V-Axis"}, {-7, "U-Axis-"}, {-8, "V-Axis-"}, -#endif - {0, NULL}}; -#if JOYAXISSET > 4 -"More Axis Sets" -#endif - // don't mind me putting these here, I was lazy to figure out where else I could put those without blowing up the compiler. // chat timer thingy @@ -412,55 +395,6 @@ consvar_t cv_kickstartaccel[MAXSPLITSCREENPLAYERS] = { CVAR_INIT ("kickstartaccel4", "Off", CV_SAVE|CV_CALL, CV_OnOff, kickstartaccel4_OnChange) }; -consvar_t cv_turnaxis[MAXSPLITSCREENPLAYERS] = { - CVAR_INIT ("joyaxis_turn", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis2_turn", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis3_turn", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis4_turn", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL) -}; - -consvar_t cv_moveaxis[MAXSPLITSCREENPLAYERS] = { - CVAR_INIT ("joyaxis_move", "None", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis_move2", "None", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis_move3", "None", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis_move4", "None", CV_SAVE, joyaxis_cons_t, NULL) -}; - -consvar_t cv_brakeaxis[MAXSPLITSCREENPLAYERS] = { - CVAR_INIT ("joyaxis_brake", "None", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis2_brake", "None", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis3_brake", "None", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis4_brake", "None", CV_SAVE, joyaxis_cons_t, NULL) -}; - -consvar_t cv_aimaxis[MAXSPLITSCREENPLAYERS] = { - CVAR_INIT ("joyaxis_aim", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis2_aim", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis3_aim", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis4_aim", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL) -}; - -consvar_t cv_lookaxis[MAXSPLITSCREENPLAYERS] = { - CVAR_INIT ("joyaxis_look", "None", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis2_look", "None", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis3_look", "None", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis4_look", "None", CV_SAVE, joyaxis_cons_t, NULL) -}; - -consvar_t cv_fireaxis[MAXSPLITSCREENPLAYERS] = { - CVAR_INIT ("joyaxis_fire", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis_fire2", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis_fire3", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis_fire4", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL) -}; - -consvar_t cv_driftaxis[MAXSPLITSCREENPLAYERS] = { - CVAR_INIT ("joyaxis_drift", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis2_drift", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis3_drift", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis4_drift", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL) -}; - static CV_PossibleValue_t zerotoone_cons_t[] = {{0, "MIN"}, {FRACUNIT, "MAX"}, {0, NULL}}; consvar_t cv_deadzone[MAXSPLITSCREENPLAYERS] = { CVAR_INIT ("joy_deadzone", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), @@ -469,13 +403,6 @@ consvar_t cv_deadzone[MAXSPLITSCREENPLAYERS] = { CVAR_INIT ("joy4_deadzone", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL) }; -consvar_t cv_digitaldeadzone[MAXSPLITSCREENPLAYERS] = { - CVAR_INIT ("joy_digdeadzone", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), - CVAR_INIT ("joy2_digdeadzone", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), - CVAR_INIT ("joy3_digdeadzone", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), - CVAR_INIT ("joy4_digdeadzone", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL) -}; - // now automatically allocated in D_RegisterClientCommands // so that it doesn't have to be updated depending on the value of MAXPLAYERS char player_names[MAXPLAYERS][MAXPLAYERNAME+1]; @@ -725,74 +652,94 @@ INT16 G_SoftwareClipAimingPitch(INT32 *aiming) return (INT16)((*aiming)>>16); } -INT32 PlayerJoyAxis(UINT8 player, axis_input_e axissel) +static INT32 KeyValue(UINT8 p, INT32 key) { - INT32 retaxis; - INT32 axisval; - boolean flp = false; - - //find what axis to get - switch (axissel) + if (key <= 0 || key >= NUMINPUTS) { - case AXISTURN: - axisval = cv_turnaxis[player-1].value; - break; - case AXISMOVE: - axisval = cv_moveaxis[player-1].value; - break; - case AXISBRAKE: - axisval = cv_brakeaxis[player-1].value; - break; - case AXISAIM: - axisval = cv_aimaxis[player-1].value; - break; - case AXISLOOK: - axisval = cv_lookaxis[player-1].value; - break; - case AXISFIRE: - axisval = cv_fireaxis[player-1].value; - break; - case AXISDRIFT: - axisval = cv_driftaxis[player-1].value; - break; - default: - return 0; - } - - if (axisval < 0) //odd -axises - { - axisval = -axisval; - flp = true; - } - if (axisval > JOYAXISSET*2 || axisval == 0) //not there in array or None return 0; - - if (axisval%2) - { - axisval /= 2; - retaxis = joyxmove[player-1][axisval]; - } - else - { - axisval--; - axisval /= 2; - retaxis = joyymove[player-1][axisval]; } - if (retaxis < (-JOYAXISRANGE)) - retaxis = -JOYAXISRANGE; - if (retaxis > (+JOYAXISRANGE)) - retaxis = +JOYAXISRANGE; + return gamekeydown[p][key]; +} - if (!Joystick[player-1].bGamepadStyle && axissel >= AXISDIGITAL) +INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean menu) +{ + INT32 i; + INT32 deadzone = 0; + boolean bound = false; + + if (p >= MAXSPLITSCREENPLAYERS) { - const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_digitaldeadzone[player-1].value) >> FRACBITS; - if (-jdeadzone < retaxis && retaxis < jdeadzone) - return 0; +#ifdef PARANOIA + CONS_Debug(DBG_GAMELOGIC, "G_PlayerInputAnalog: Invalid player ID %d\n", p); +#endif + return 0; } - if (flp) retaxis = -retaxis; //flip it around - return retaxis; + deadzone = (JOYAXISRANGE * cv_deadzone[p].value) / FRACUNIT; + + for (i = 0; i < MAXINPUTMAPPING; i++) + { + INT32 key = gamecontrol[p][gc][i]; + INT32 value = 0; + + if (key <= 0 || key >= NUMINPUTS) + { + continue; + } + + value = KeyValue(p, key); + bound = true; + + if (gc == gc_a || gc == gc_b || gc == gc_c) + { + // Handle spindash key + value = max(value, KeyValue(p, gamecontrol[p][gc_abc][i])); + } + + if (value >= deadzone) + { + return value; + } + } + + if (menu == true && bound == false) + { + // We don't want menus to become unnavigable if people unbind + // all of their controls, so use the default control scheme in + // this scenario. + + for (i = 0; i < MAXINPUTMAPPING; i++) + { + INT32 key = gamecontroldefault[gc][i]; + INT32 value = 0; + + if (key <= 0 || key >= NUMINPUTS) + { + continue; + } + + value = KeyValue(p, key); + + if (gc == gc_a || gc == gc_b || gc == gc_c) + { + // Handle spindash key + value = max(value, KeyValue(p, gamecontroldefault[gc_abc][i])); + } + + if (value >= deadzone) + { + return value; + } + } + } + + return 0; +} + +boolean G_PlayerInputDown(UINT8 p, INT32 gc, boolean menu) +{ + return (G_PlayerInputAnalog(p, gc, menu) != 0); } // Take a magnitude of two axes, and adjust it to take out the deadzone @@ -867,27 +814,15 @@ angle_t localangle[MAXSPLITSCREENPLAYERS]; void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) { const UINT8 forplayer = ssplayer-1; - - const INT32 lookaxis = cv_lookaxis[forplayer].value; - const boolean invertmouse = cv_invertmouse.value; - const boolean analogjoystickmove = cv_usejoystick[forplayer].value && !Joystick[forplayer].bGamepadStyle; - const boolean gamepadjoystickmove = cv_usejoystick[forplayer].value && Joystick[forplayer].bGamepadStyle; - const boolean usejoystick = (analogjoystickmove || gamepadjoystickmove); - - static boolean keyboard_look[MAXSPLITSCREENPLAYERS]; // true if lookup/down using keyboard - static boolean resetdown[MAXSPLITSCREENPLAYERS]; // don't cam reset every frame - - INT32 forward, axis; + INT32 forward; joystickvector2_t joystickvector; - boolean turnleft, turnright; - player_t *player = &players[g_localplayers[forplayer]]; - camera_t *thiscam = &camera[forplayer]; - boolean *kbl = &keyboard_look[forplayer]; - boolean *rd = &resetdown[forplayer]; - const boolean mouseaiming = player->spectator; + //camera_t *thiscam = &camera[forplayer]; + //boolean *kbl = &keyboard_look[forplayer]; + //boolean *rd = &resetdown[forplayer]; + //const boolean mouseaiming = player->spectator; (void)realtics; @@ -924,10 +859,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) return; } - turnright = PlayerInputDown(ssplayer, gc_turnright); - turnleft = PlayerInputDown(ssplayer, gc_turnleft); - - joystickvector.xaxis = PlayerJoyAxis(ssplayer, AXISTURN); + joystickvector.xaxis = G_PlayerInputAnalog(forplayer, gc_right, false) - G_PlayerInputAnalog(forplayer, gc_left, false); joystickvector.yaxis = 0; G_HandleAxisDeadZone(forplayer, &joystickvector); @@ -935,138 +867,92 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // use it for aiming to throw items forward/backward and the vote screen // This mean that the turn axis will still be gradient but up/down will be 0 // until the stick is pushed far enough - joystickvector.yaxis = PlayerJoyAxis(ssplayer, AXISAIM); + joystickvector.yaxis = G_PlayerInputAnalog(forplayer, gc_down, false) - G_PlayerInputAnalog(forplayer, gc_up, false); if (encoremode) { - turnright ^= turnleft; // swap these using three XORs - turnleft ^= turnright; - turnright ^= turnleft; joystickvector.xaxis = -joystickvector.xaxis; } - if (gamepadjoystickmove && joystickvector.xaxis != 0) - { - turnright = turnright || (joystickvector.xaxis > 0); - turnleft = turnleft || (joystickvector.xaxis < 0); - } forward = 0; - cmd->turning = 0; - // let movement keys cancel each other out - if (turnright && !(turnleft)) - { - cmd->turning -= KART_FULLTURN; - } - else if (turnleft && !(turnright)) - { - cmd->turning += KART_FULLTURN; - } - - if (analogjoystickmove && joystickvector.xaxis != 0) + if (joystickvector.xaxis != 0) { cmd->turning -= (joystickvector.xaxis * KART_FULLTURN) >> 10; } - // Specator mouse turning - if (player->spectator) - { - cmd->turning -= (mousex * 8) * (encoremode ? -1 : 1); - } - if (player->spectator || objectplacing) // SRB2Kart: spectators need special controls { - axis = PlayerJoyAxis(ssplayer, AXISMOVE); - if (PlayerInputDown(ssplayer, gc_accelerate) || (usejoystick && axis > 0)) + if (G_PlayerInputDown(forplayer, gc_a, false)) + { cmd->buttons |= BT_ACCELERATE; - axis = PlayerJoyAxis(ssplayer, AXISBRAKE); - if (PlayerInputDown(ssplayer, gc_brake) || (usejoystick && axis > 0)) + } + + if (G_PlayerInputDown(forplayer, gc_b, false)) + { cmd->buttons |= BT_BRAKE; - axis = PlayerJoyAxis(ssplayer, AXISAIM); - if (PlayerInputDown(ssplayer, gc_aimforward) || (usejoystick && axis < 0)) + } + + if (joystickvector.yaxis < 0) + { forward += MAXPLMOVE; - if (PlayerInputDown(ssplayer, gc_aimbackward) || (usejoystick && axis > 0)) + } + + if (joystickvector.yaxis > 0) + { forward -= MAXPLMOVE; + } } else { // forward with key or button // SRB2kart - we use an accel/brake instead of forward/backward. - axis = PlayerJoyAxis(ssplayer, AXISMOVE); - if (PlayerInputDown(ssplayer, gc_accelerate) || (gamepadjoystickmove && axis > 0)) + fixed_t value = G_PlayerInputAnalog(forplayer, gc_a, false); + if (value != 0) { cmd->buttons |= BT_ACCELERATE; - forward = MAXPLMOVE; // 50 - } - else if (analogjoystickmove && axis > 0) - { - cmd->buttons |= BT_ACCELERATE; - // JOYAXISRANGE is supposed to be 1023 (divide by 1024) - forward += ((axis * MAXPLMOVE) >> 10); + forward += ((value * MAXPLMOVE) >> 10); } - axis = PlayerJoyAxis(ssplayer, AXISBRAKE); - if (PlayerInputDown(ssplayer, gc_brake) || (gamepadjoystickmove && axis > 0)) + value = G_PlayerInputAnalog(forplayer, gc_b, false); + if (value != 0) { cmd->buttons |= BT_BRAKE; - if (cmd->buttons & BT_ACCELERATE || cmd->forwardmove <= 0) - forward -= MAXPLMOVE; - } - else if (analogjoystickmove && axis > 0) - { - cmd->buttons |= BT_BRAKE; - // JOYAXISRANGE is supposed to be 1023 (divide by 1024) - if (cmd->buttons & BT_ACCELERATE || cmd->forwardmove <= 0) - forward -= ((axis * MAXPLMOVE) >> 10); + forward -= ((value * MAXPLMOVE) >> 10); } // But forward/backward IS used for aiming. - if (PlayerInputDown(ssplayer, gc_aimforward) || (joystickvector.yaxis < 0)) + if (joystickvector.yaxis < 0) + { cmd->buttons |= BT_FORWARD; - if (PlayerInputDown(ssplayer, gc_aimbackward) || (joystickvector.yaxis > 0)) + } + + if (joystickvector.yaxis > 0) + { cmd->buttons |= BT_BACKWARD; + } } // fire with any button/key - axis = PlayerJoyAxis(ssplayer, AXISFIRE); - if (PlayerInputDown(ssplayer, gc_fire) || (usejoystick && axis > 0)) + if (G_PlayerInputDown(forplayer, gc_c, false)) + { cmd->buttons |= BT_ATTACK; + } // drift with any button/key - axis = PlayerJoyAxis(ssplayer, AXISDRIFT); - if (PlayerInputDown(ssplayer, gc_drift) || (usejoystick && axis > 0)) + if (G_PlayerInputDown(forplayer, gc_x, false)) + { cmd->buttons |= BT_DRIFT; - - // Spindash with any button/key - // Simply holds all of the inputs for you. - axis = PlayerJoyAxis(ssplayer, AXISSPINDASH); - if (PlayerInputDown(ssplayer, gc_spindash) || (usejoystick && axis > 0)) - cmd->buttons |= (BT_ACCELERATE|BT_BRAKE|BT_DRIFT); + } // rear view with any button/key - axis = PlayerJoyAxis(ssplayer, AXISLOOKBACK); - if (PlayerInputDown(ssplayer, gc_lookback) || (usejoystick && axis > 0)) - cmd->buttons |= BT_LOOKBACK; - - // Lua scriptable buttons - if (PlayerInputDown(ssplayer, gc_custom1)) - cmd->buttons |= BT_CUSTOM1; - if (PlayerInputDown(ssplayer, gc_custom2)) - cmd->buttons |= BT_CUSTOM2; - if (PlayerInputDown(ssplayer, gc_custom3)) - cmd->buttons |= BT_CUSTOM3; - - // Reset camera - if (PlayerInputDown(ssplayer, gc_camreset)) + if (G_PlayerInputDown(forplayer, gc_y, false)) { - if (thiscam->chase && *rd == false) - P_ResetCamera(player, thiscam); - *rd = true; + cmd->buttons |= BT_LOOKBACK; } - else - *rd = false; // spectator aiming shit, ahhhh... + /* { INT32 player_invert = invertmouse ? -1 : 1; INT32 screen_invert = @@ -1074,15 +960,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) && (!thiscam->chase)) //because chasecam's not inverted ? -1 : 1; // set to -1 or 1 to multiply - // mouse look stuff (mouse look is not the same as mouse aim) - if (mouseaiming && player->spectator) - { - *kbl = false; - - // looking up/down - cmd->aiming += (mlooky<<19)*player_invert*screen_invert; - } - axis = PlayerJoyAxis(ssplayer, AXISLOOK); if (analogjoystickmove && axis != 0 && lookaxis && player->spectator) cmd->aiming += (axis<<16) * screen_invert; @@ -1108,11 +985,9 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) if (PlayerInputDown(ssplayer, gc_centerview)) // No need to put a spectator limit on this one though :V cmd->aiming = 0; } - - mousex = mousey = mlooky = 0; + */ cmd->forwardmove += (SINT8)forward; - cmd->flags = 0; if (chat_on || CON_Ready()) @@ -1210,7 +1085,7 @@ static void kickstartaccel4_OnChange(void) // void G_DoLoadLevel(boolean resetplayer) { - INT32 i, j; + INT32 i; // Make sure objectplace is OFF when you first start the level! OP_ResetObjectplace(); @@ -1270,12 +1145,6 @@ void G_DoLoadLevel(boolean resetplayer) // clear cmd building stuff memset(gamekeydown, 0, sizeof (gamekeydown)); - for (i = 0;i < JOYAXISSET; i++) - { - for (j = 0; j < MAXSPLITSCREENPLAYERS; j++) - joyxmove[j][i] = joyymove[j][i] = 0; - } - mousex = mousey = 0; // clear hud messages remains (usually from game startup) CON_ClearHUD(); @@ -1361,7 +1230,7 @@ static INT32 camtoggledelay[MAXSPLITSCREENPLAYERS]; // boolean G_Responder(event_t *ev) { - UINT8 i; + //INT32 i; // any other key pops up menu if in demos if (gameaction == ga_nothing && !demo.quitafterplaying && @@ -1456,7 +1325,7 @@ boolean G_Responder(event_t *ev) // allow spy mode changes even during the demo if (gamestate == GS_LEVEL && ev->type == ev_keydown - && (ev->data1 == KEY_F12 || ev->data1 == gamecontrol[0][gc_viewpoint][0] || ev->data1 == gamecontrol[0][gc_viewpoint][1])) + && (ev->data1 == KEY_F12 /*|| ev->data1 == gamecontrol[0][gc_viewpoint][0] || ev->data1 == gamecontrol[0][gc_viewpoint][1]*/)) { if (!demo.playback && (r_splitscreen || !netgame)) g_localplayers[0] = consoleplayer; @@ -1474,6 +1343,7 @@ boolean G_Responder(event_t *ev) if (gamestate == GS_LEVEL && ev->type == ev_keydown && multiplayer && demo.playback && !demo.freecam) { + /* if (ev->data1 == gamecontrol[1][gc_viewpoint][0] || ev->data1 == gamecontrol[1][gc_viewpoint][1]) { G_AdjustView(2, 1, true); @@ -1489,12 +1359,13 @@ boolean G_Responder(event_t *ev) G_AdjustView(4, 1, true); return true; } + */ // Allow pausing if ( - ev->data1 == gamecontrol[0][gc_pause][0] - || ev->data1 == gamecontrol[0][gc_pause][1] - || ev->data1 == KEY_PAUSE + //ev->data1 == gamecontrol[0][gc_pause][0] + //|| ev->data1 == gamecontrol[0][gc_pause][1] + ev->data1 == KEY_PAUSE ) { paused = !paused; @@ -1528,9 +1399,9 @@ boolean G_Responder(event_t *ev) switch (ev->type) { case ev_keydown: - if (ev->data1 == gamecontrol[0][gc_pause][0] - || ev->data1 == gamecontrol[0][gc_pause][1] - || ev->data1 == KEY_PAUSE) + if (//ev->data1 == gamecontrol[0][gc_pause][0] + //|| ev->data1 == gamecontrol[0][gc_pause][1] + ev->data1 == KEY_PAUSE) { if (modeattacking && !demo.playback && (gamestate == GS_LEVEL)) { @@ -1560,6 +1431,7 @@ boolean G_Responder(event_t *ev) } } + /* for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { if (ev->data1 == gamecontrol[i][gc_camtoggle][0] @@ -1572,6 +1444,7 @@ boolean G_Responder(event_t *ev) } } } + */ return true; @@ -1584,15 +1457,6 @@ boolean G_Responder(event_t *ev) case ev_joystick: return true; // eat events - case ev_joystick2: - return true; // eat events - - case ev_joystick3: - return true; // eat events - - case ev_joystick4: - return true; // eat events - default: break; } diff --git a/src/g_game.h b/src/g_game.h index 9e4ef0159..5f66b6ff6 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -55,15 +55,7 @@ extern consvar_t cv_pauseifunfocused; extern consvar_t cv_invertmouse; extern consvar_t cv_kickstartaccel[MAXSPLITSCREENPLAYERS]; -extern consvar_t cv_turnaxis[MAXSPLITSCREENPLAYERS]; -extern consvar_t cv_moveaxis[MAXSPLITSCREENPLAYERS]; -extern consvar_t cv_brakeaxis[MAXSPLITSCREENPLAYERS]; -extern consvar_t cv_aimaxis[MAXSPLITSCREENPLAYERS]; -extern consvar_t cv_lookaxis[MAXSPLITSCREENPLAYERS]; -extern consvar_t cv_fireaxis[MAXSPLITSCREENPLAYERS]; -extern consvar_t cv_driftaxis[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_deadzone[MAXSPLITSCREENPLAYERS]; -extern consvar_t cv_digitaldeadzone[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_ghost_besttime, cv_ghost_bestlap, cv_ghost_last, cv_ghost_guest, cv_ghost_staff; @@ -93,29 +85,12 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n); INT32 G_ClipAimingPitch(INT32 *aiming); INT16 G_SoftwareClipAimingPitch(INT32 *aiming); -typedef enum -{ - AXISNONE = 0, - - AXISTURN, - AXISMOVE, - AXISBRAKE, - AXISLOOK, - - AXISDIGITAL, // axes below this use digital deadzone - - AXISFIRE = AXISDIGITAL, - AXISDRIFT, - AXISSPINDASH, - AXISLOOKBACK, - AXISAIM, -} axis_input_e; - -INT32 PlayerJoyAxis(UINT8 player, axis_input_e axissel); - extern angle_t localangle[MAXSPLITSCREENPLAYERS]; extern INT32 localaiming[MAXSPLITSCREENPLAYERS]; // should be an angle_t but signed +INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean menu); +boolean G_PlayerInputDown(UINT8 p, INT32 gc, boolean menu); + // // GAME // diff --git a/src/g_input.c b/src/g_input.c index 72c7033fa..0cd044f67 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -31,32 +31,28 @@ consvar_t cv_mouseysens = CVAR_INIT ("mouseysens", "20", CV_SAVE, mousesens_cons consvar_t cv_mouseysens2 = CVAR_INIT ("mouseysens2", "20", CV_SAVE, mousesens_cons_t, NULL); consvar_t cv_controlperkey = CVAR_INIT ("controlperkey", "One", CV_SAVE, onecontrolperkey_cons_t, NULL); -INT32 mousex, mousey; -INT32 mlooky; // like mousey but with a custom sensitivity for mlook - -// joystick values are repeated -INT32 joyxmove[MAXSPLITSCREENPLAYERS][JOYAXISSET], joyymove[MAXSPLITSCREENPLAYERS][JOYAXISSET]; - -// current state of the keys: true if pushed -UINT8 gamekeydown[NUMINPUTS]; +// current state of the keys +// FRACUNIT for fully pressed, 0 for not pressed +INT32 gamekeydown[MAXSPLITSCREENPLAYERS][NUMINPUTS]; // two key codes (or virtual key) per game control -INT32 gamecontrol[MAXSPLITSCREENPLAYERS][num_gamecontrols][2]; -INT32 gamecontroldefault[MAXSPLITSCREENPLAYERS][num_gamecontrolschemes][num_gamecontrols][2]; // default control storage, use 0 (gcs_custom) for memory retention +INT32 gamecontrol[MAXSPLITSCREENPLAYERS][num_gamecontrols][MAXINPUTMAPPING]; +INT32 gamecontroldefault[num_gamecontrols][MAXINPUTMAPPING]; // default control storage // lists of GC codes for selective operation -const INT32 gcl_accelerate[num_gcl_accelerate] = { gc_accelerate }; +/* +const INT32 gcl_accelerate[num_gcl_accelerate] = { gc_a }; -const INT32 gcl_brake[num_gcl_brake] = { gc_brake }; +const INT32 gcl_brake[num_gcl_brake] = { gc_b }; -const INT32 gcl_drift[num_gcl_drift] = { gc_drift }; +const INT32 gcl_drift[num_gcl_drift] = { gc_c }; const INT32 gcl_spindash[num_gcl_spindash] = { - gc_accelerate, gc_drift, gc_brake, gc_spindash + gc_a, gc_b, gc_c, gc_abc }; const INT32 gcl_movement[num_gcl_movement] = { - gc_accelerate, gc_drift, gc_brake, gc_spindash, gc_turnleft, gc_turnright + gc_a, gc_b, gc_c, gc_abc, gc_left, gc_right }; const INT32 gcl_item[num_gcl_item] = { @@ -64,22 +60,11 @@ const INT32 gcl_item[num_gcl_item] = { }; const INT32 gcl_full[num_gcl_full] = { - gc_accelerate, gc_drift, gc_brake, gc_spindash, gc_turnleft, gc_turnright, + gc_a, gc_drift, gc_b, gc_spindash, gc_turnleft, gc_turnright, gc_fire, gc_aimforward, gc_aimbackward, gc_lookback }; - -typedef struct -{ - UINT8 time; - UINT8 state; - UINT8 clicks; -} dclick_t; -static dclick_t mousedclicks[MOUSEBUTTONS]; -static dclick_t joydclicks[MAXSPLITSCREENPLAYERS][JOYBUTTONS + JOYHATS*4]; - -// protos -static UINT8 G_CheckDoubleClick(UINT8 state, dclick_t *dt); +*/ // // Remaps the inputs to game controls. @@ -91,137 +76,131 @@ static UINT8 G_CheckDoubleClick(UINT8 state, dclick_t *dt); void G_MapEventsToControls(event_t *ev) { INT32 i; - UINT8 flag; switch (ev->type) { case ev_keydown: if (ev->data1 < NUMINPUTS) - gamekeydown[ev->data1] = 1; + { + gamekeydown[ev->device][ev->data1] = FRACUNIT; + } #ifdef PARANOIA else { - CONS_Debug(DBG_GAMELOGIC, "Bad downkey input %d\n",ev->data1); + CONS_Debug(DBG_GAMELOGIC, "Bad downkey input %d\n", ev->data1); } - #endif break; case ev_keyup: if (ev->data1 < NUMINPUTS) - gamekeydown[ev->data1] = 0; + { + gamekeydown[ev->device][ev->data1] = 0; + } #ifdef PARANOIA else { - CONS_Debug(DBG_GAMELOGIC, "Bad upkey input %d\n",ev->data1); + CONS_Debug(DBG_GAMELOGIC, "Bad upkey input %d\n", ev->data1); } #endif break; case ev_mouse: // buttons are virtual keys - if (menuactive || CON_Ready() || chat_on) + if (menuactive) + { break; - mousex = (INT32)(ev->data2*((cv_mousesens.value*cv_mousesens.value)/110.0f + 0.1f)); - mousey = (INT32)(ev->data3*((cv_mousesens.value*cv_mousesens.value)/110.0f + 0.1f)); - mlooky = (INT32)(ev->data3*((cv_mouseysens.value*cv_mousesens.value)/110.0f + 0.1f)); + } + + // X axis + if (ev->data2 < 0) + { + // Left + gamekeydown[ev->device][KEY_MOUSEMOVE + 2] = abs(ev->data2); + gamekeydown[ev->device][KEY_MOUSEMOVE + 3] = 0; + } + else + { + // Right + gamekeydown[ev->device][KEY_MOUSEMOVE + 2] = 0; + gamekeydown[ev->device][KEY_MOUSEMOVE + 3] = abs(ev->data2); + } + + // Y axis + if (ev->data3 < 0) + { + // Up + gamekeydown[ev->device][KEY_MOUSEMOVE] = abs(ev->data3); + gamekeydown[ev->device][KEY_MOUSEMOVE + 1] = 0; + } + else + { + // Down + gamekeydown[ev->device][KEY_MOUSEMOVE] = 0; + gamekeydown[ev->device][KEY_MOUSEMOVE + 1] = abs(ev->data3); + } break; case ev_joystick: // buttons are virtual keys - i = ev->data1; - if (i >= JOYAXISSET || menuactive || CON_Ready() || chat_on) + if (menuactive) + { break; - if (ev->data2 != INT32_MAX) joyxmove[0][i] = ev->data2; - if (ev->data3 != INT32_MAX) joyymove[0][i] = ev->data3; - break; + } - case ev_joystick2: // buttons are virtual keys - i = ev->data1; - if (i >= JOYAXISSET || menuactive) + if (ev->data1 >= JOYAXISSET) + { +#ifdef PARANOIA + CONS_Debug(DBG_GAMELOGIC, "Bad joystick axis event %d\n", ev->data1); +#endif break; - if (ev->data2 != INT32_MAX) joyxmove[1][i] = ev->data2; - if (ev->data3 != INT32_MAX) joyymove[1][i] = ev->data3; - break; + } - case ev_joystick3: - i = ev->data1; - if (i >= JOYAXISSET) - break; - if (ev->data2 != INT32_MAX) joyxmove[2][i] = ev->data2; - if (ev->data3 != INT32_MAX) joyymove[2][i] = ev->data3; - break; + i = ev->data1 * 4; + + if (ev->device == 0) + { + if (CON_Ready() || chat_on) + break; + } + + if (ev->data2 != INT32_MAX) + { + // X axis + if (ev->data2 < 0) + { + // Left + gamekeydown[ev->device][KEY_AXIS1 + i] = abs(ev->data2); + gamekeydown[ev->device][KEY_AXIS1 + i + 1] = 0; + } + else + { + // Right + gamekeydown[ev->device][KEY_AXIS1 + i] = 0; + gamekeydown[ev->device][KEY_AXIS1 + i + 1] = abs(ev->data2); + } + } + + if (ev->data3 != INT32_MAX) + { + // Y axis + if (ev->data3 < 0) + { + // Up + gamekeydown[ev->device][KEY_AXIS1 + i + 2] = abs(ev->data3); + gamekeydown[ev->device][KEY_AXIS1 + i + 3] = 0; + } + else + { + // Down + gamekeydown[ev->device][KEY_AXIS1 + i + 2] = 0; + gamekeydown[ev->device][KEY_AXIS1 + i + 3] = abs(ev->data3); + } + } - case ev_joystick4: - i = ev->data1; - if (i >= JOYAXISSET) - break; - if (ev->data2 != INT32_MAX) joyxmove[3][i] = ev->data2; - if (ev->data3 != INT32_MAX) joyymove[3][i] = ev->data3; break; default: break; } - - // ALWAYS check for mouse & joystick double-clicks even if no mouse event - for (i = 0; i < MOUSEBUTTONS; i++) - { - flag = G_CheckDoubleClick(gamekeydown[KEY_MOUSE1+i], &mousedclicks[i]); - gamekeydown[KEY_DBLMOUSE1+i] = flag; - } - - for (i = 0; i < JOYBUTTONS + JOYHATS*4; i++) - { - flag = G_CheckDoubleClick(gamekeydown[KEY_JOY1+i], &joydclicks[0][i]); - gamekeydown[KEY_DBLJOY1+i] = flag; - } - - for (i = 0; i < JOYBUTTONS + JOYHATS*4; i++) - { - flag = G_CheckDoubleClick(gamekeydown[KEY_2JOY1+i], &joydclicks[1][i]); - gamekeydown[KEY_DBL2JOY1+i] = flag; - } - - for (i = 0; i < JOYBUTTONS + JOYHATS*4; i++) - { - flag = G_CheckDoubleClick(gamekeydown[KEY_3JOY1+i], &joydclicks[2][i]); - gamekeydown[KEY_DBL3JOY1+i] = flag; - } - - for (i = 0; i < JOYBUTTONS + JOYHATS*4; i++) - { - flag = G_CheckDoubleClick(gamekeydown[KEY_4JOY1+i], &joydclicks[3][i]); - gamekeydown[KEY_DBL4JOY1+i] = flag; - } -} - -// -// General double-click detection routine for any kind of input. -// -static UINT8 G_CheckDoubleClick(UINT8 state, dclick_t *dt) -{ - if (state != dt->state && dt->time > 1) - { - dt->state = state; - if (state) - dt->clicks++; - if (dt->clicks == 2) - { - dt->clicks = 0; - return true; - } - else - dt->time = 0; - } - else - { - dt->time++; - if (dt->time > 20) - { - dt->clicks = 0; - dt->state = 0; - } - } - return false; } typedef struct @@ -239,12 +218,12 @@ static keyname_t keynames[] = {KEY_ESCAPE, "ESCAPE"}, {KEY_BACKSPACE, "BACKSPACE"}, - {KEY_NUMLOCK, "NUMLOCK"}, - {KEY_SCROLLLOCK, "SCROLLLOCK"}, + {KEY_NUMLOCK, "NUM LOCK"}, + {KEY_SCROLLLOCK, "SCROLL LOCK"}, // bill gates keys - {KEY_LEFTWIN, "LEFTWIN"}, - {KEY_RIGHTWIN, "RIGHTWIN"}, + {KEY_LEFTWIN, "LWINDOWS"}, + {KEY_RIGHTWIN, "RWINDOWS"}, {KEY_MENU, "MENU"}, {KEY_LSHIFT, "LSHIFT"}, @@ -276,14 +255,14 @@ static keyname_t keynames[] = // extended keys (not keypad) {KEY_HOME, "HOME"}, {KEY_UPARROW, "UP ARROW"}, - {KEY_PGUP, "PGUP"}, + {KEY_PGUP, "PAGE UP"}, {KEY_LEFTARROW, "LEFT ARROW"}, {KEY_RIGHTARROW, "RIGHT ARROW"}, {KEY_END, "END"}, {KEY_DOWNARROW, "DOWN ARROW"}, - {KEY_PGDN, "PGDN"}, - {KEY_INS, "INS"}, - {KEY_DEL, "DEL"}, + {KEY_PGDN, "PAGE DOWN"}, + {KEY_INS, "INSERT"}, + {KEY_DEL, "DELETE"}, // other keys {KEY_F1, "F1"}, @@ -312,10 +291,12 @@ static keyname_t keynames[] = {KEY_MOUSE1+5,"MOUSE6"}, {KEY_MOUSE1+6,"MOUSE7"}, {KEY_MOUSE1+7,"MOUSE8"}, - {KEY_MOUSEWHEELUP, "Wheel 1 UP"}, - {KEY_MOUSEWHEELDOWN, "Wheel 1 Down"}, - {KEY_2MOUSEWHEELUP, "Wheel 2 UP"}, - {KEY_2MOUSEWHEELDOWN, "Wheel 2 Down"}, + {KEY_MOUSEMOVE+0,"Mouse Up"}, + {KEY_MOUSEMOVE+1,"Mouse Down"}, + {KEY_MOUSEMOVE+2,"Mouse Left"}, + {KEY_MOUSEMOVE+3,"Mouse Right"}, + {KEY_MOUSEWHEELUP, "Wheel Up"}, + {KEY_MOUSEWHEELDOWN, "Wheel Down"}, {KEY_JOY1+0, "JOY1"}, {KEY_JOY1+1, "JOY2"}, @@ -352,6 +333,7 @@ static keyname_t keynames[] = {KEY_JOY1+30, "JOY31"}, {KEY_JOY1+31, "JOY32"}, #endif + // the DOS version uses Allegro's joystick support {KEY_HAT1+0, "HATUP"}, {KEY_HAT1+1, "HATDOWN"}, @@ -370,404 +352,42 @@ static keyname_t keynames[] = {KEY_HAT1+14, "HATLEFT4"}, {KEY_HAT1+15, "HATRIGHT4"}, - {KEY_DBLMOUSE1+0, "DBLMOUSE1"}, - {KEY_DBLMOUSE1+1, "DBLMOUSE2"}, - {KEY_DBLMOUSE1+2, "DBLMOUSE3"}, - {KEY_DBLMOUSE1+3, "DBLMOUSE4"}, - {KEY_DBLMOUSE1+4, "DBLMOUSE5"}, - {KEY_DBLMOUSE1+5, "DBLMOUSE6"}, - {KEY_DBLMOUSE1+6, "DBLMOUSE7"}, - {KEY_DBLMOUSE1+7, "DBLMOUSE8"}, - - {KEY_DBLJOY1+0, "DBLJOY1"}, - {KEY_DBLJOY1+1, "DBLJOY2"}, - {KEY_DBLJOY1+2, "DBLJOY3"}, - {KEY_DBLJOY1+3, "DBLJOY4"}, - {KEY_DBLJOY1+4, "DBLJOY5"}, - {KEY_DBLJOY1+5, "DBLJOY6"}, - {KEY_DBLJOY1+6, "DBLJOY7"}, - {KEY_DBLJOY1+7, "DBLJOY8"}, -#if !defined (NOMOREJOYBTN_1DBL) - {KEY_DBLJOY1+8, "DBLJOY9"}, - {KEY_DBLJOY1+9, "DBLJOY10"}, - {KEY_DBLJOY1+10, "DBLJOY11"}, - {KEY_DBLJOY1+11, "DBLJOY12"}, - {KEY_DBLJOY1+12, "DBLJOY13"}, - {KEY_DBLJOY1+13, "DBLJOY14"}, - {KEY_DBLJOY1+14, "DBLJOY15"}, - {KEY_DBLJOY1+15, "DBLJOY16"}, - {KEY_DBLJOY1+16, "DBLJOY17"}, - {KEY_DBLJOY1+17, "DBLJOY18"}, - {KEY_DBLJOY1+18, "DBLJOY19"}, - {KEY_DBLJOY1+19, "DBLJOY20"}, - {KEY_DBLJOY1+20, "DBLJOY21"}, - {KEY_DBLJOY1+21, "DBLJOY22"}, - {KEY_DBLJOY1+22, "DBLJOY23"}, - {KEY_DBLJOY1+23, "DBLJOY24"}, - {KEY_DBLJOY1+24, "DBLJOY25"}, - {KEY_DBLJOY1+25, "DBLJOY26"}, - {KEY_DBLJOY1+26, "DBLJOY27"}, - {KEY_DBLJOY1+27, "DBLJOY28"}, - {KEY_DBLJOY1+28, "DBLJOY29"}, - {KEY_DBLJOY1+29, "DBLJOY30"}, - {KEY_DBLJOY1+30, "DBLJOY31"}, - {KEY_DBLJOY1+31, "DBLJOY32"}, -#endif - {KEY_DBLHAT1+0, "DBLHATUP"}, - {KEY_DBLHAT1+1, "DBLHATDOWN"}, - {KEY_DBLHAT1+2, "DBLHATLEFT"}, - {KEY_DBLHAT1+3, "DBLHATRIGHT"}, - {KEY_DBLHAT1+4, "DBLHATUP2"}, - {KEY_DBLHAT1+5, "DBLHATDOWN2"}, - {KEY_DBLHAT1+6, "DBLHATLEFT2"}, - {KEY_DBLHAT1+7, "DBLHATRIGHT2"}, - {KEY_DBLHAT1+8, "DBLHATUP3"}, - {KEY_DBLHAT1+9, "DBLHATDOWN3"}, - {KEY_DBLHAT1+10, "DBLHATLEFT3"}, - {KEY_DBLHAT1+11, "DBLHATRIGHT3"}, - {KEY_DBLHAT1+12, "DBLHATUP4"}, - {KEY_DBLHAT1+13, "DBLHATDOWN4"}, - {KEY_DBLHAT1+14, "DBLHATLEFT4"}, - {KEY_DBLHAT1+15, "DBLHATRIGHT4"}, - - {KEY_2JOY1+0, "SEC_JOY1"}, - {KEY_2JOY1+1, "SEC_JOY2"}, - {KEY_2JOY1+2, "SEC_JOY3"}, - {KEY_2JOY1+3, "SEC_JOY4"}, - {KEY_2JOY1+4, "SEC_JOY5"}, - {KEY_2JOY1+5, "SEC_JOY6"}, - {KEY_2JOY1+6, "SEC_JOY7"}, - {KEY_2JOY1+7, "SEC_JOY8"}, -#if !defined (NOMOREJOYBTN_2S) - // we use up to 32 buttons in DirectInput - {KEY_2JOY1+8, "SEC_JOY9"}, - {KEY_2JOY1+9, "SEC_JOY10"}, - {KEY_2JOY1+10, "SEC_JOY11"}, - {KEY_2JOY1+11, "SEC_JOY12"}, - {KEY_2JOY1+12, "SEC_JOY13"}, - {KEY_2JOY1+13, "SEC_JOY14"}, - {KEY_2JOY1+14, "SEC_JOY15"}, - {KEY_2JOY1+15, "SEC_JOY16"}, - {KEY_2JOY1+16, "SEC_JOY17"}, - {KEY_2JOY1+17, "SEC_JOY18"}, - {KEY_2JOY1+18, "SEC_JOY19"}, - {KEY_2JOY1+19, "SEC_JOY20"}, - {KEY_2JOY1+20, "SEC_JOY21"}, - {KEY_2JOY1+21, "SEC_JOY22"}, - {KEY_2JOY1+22, "SEC_JOY23"}, - {KEY_2JOY1+23, "SEC_JOY24"}, - {KEY_2JOY1+24, "SEC_JOY25"}, - {KEY_2JOY1+25, "SEC_JOY26"}, - {KEY_2JOY1+26, "SEC_JOY27"}, - {KEY_2JOY1+27, "SEC_JOY28"}, - {KEY_2JOY1+28, "SEC_JOY29"}, - {KEY_2JOY1+29, "SEC_JOY30"}, - {KEY_2JOY1+30, "SEC_JOY31"}, - {KEY_2JOY1+31, "SEC_JOY32"}, -#endif - // the DOS version uses Allegro's joystick support - {KEY_2HAT1+0, "SEC_HATUP"}, - {KEY_2HAT1+1, "SEC_HATDOWN"}, - {KEY_2HAT1+2, "SEC_HATLEFT"}, - {KEY_2HAT1+3, "SEC_HATRIGHT"}, - {KEY_2HAT1+4, "SEC_HATUP2"}, - {KEY_2HAT1+5, "SEC_HATDOWN2"}, - {KEY_2HAT1+6, "SEC_HATLEFT2"}, - {KEY_2HAT1+7, "SEC_HATRIGHT2"}, - {KEY_2HAT1+8, "SEC_HATUP3"}, - {KEY_2HAT1+9, "SEC_HATDOWN3"}, - {KEY_2HAT1+10, "SEC_HATLEFT3"}, - {KEY_2HAT1+11, "SEC_HATRIGHT3"}, - {KEY_2HAT1+12, "SEC_HATUP4"}, - {KEY_2HAT1+13, "SEC_HATDOWN4"}, - {KEY_2HAT1+14, "SEC_HATLEFT4"}, - {KEY_2HAT1+15, "SEC_HATRIGHT4"}, - - {KEY_DBL2JOY1+0, "DBLSEC_JOY1"}, - {KEY_DBL2JOY1+1, "DBLSEC_JOY2"}, - {KEY_DBL2JOY1+2, "DBLSEC_JOY3"}, - {KEY_DBL2JOY1+3, "DBLSEC_JOY4"}, - {KEY_DBL2JOY1+4, "DBLSEC_JOY5"}, - {KEY_DBL2JOY1+5, "DBLSEC_JOY6"}, - {KEY_DBL2JOY1+6, "DBLSEC_JOY7"}, - {KEY_DBL2JOY1+7, "DBLSEC_JOY8"}, -#if !defined (NOMOREJOYBTN_2DBL) - {KEY_DBL2JOY1+8, "DBLSEC_JOY9"}, - {KEY_DBL2JOY1+9, "DBLSEC_JOY10"}, - {KEY_DBL2JOY1+10, "DBLSEC_JOY11"}, - {KEY_DBL2JOY1+11, "DBLSEC_JOY12"}, - {KEY_DBL2JOY1+12, "DBLSEC_JOY13"}, - {KEY_DBL2JOY1+13, "DBLSEC_JOY14"}, - {KEY_DBL2JOY1+14, "DBLSEC_JOY15"}, - {KEY_DBL2JOY1+15, "DBLSEC_JOY16"}, - {KEY_DBL2JOY1+16, "DBLSEC_JOY17"}, - {KEY_DBL2JOY1+17, "DBLSEC_JOY18"}, - {KEY_DBL2JOY1+18, "DBLSEC_JOY19"}, - {KEY_DBL2JOY1+19, "DBLSEC_JOY20"}, - {KEY_DBL2JOY1+20, "DBLSEC_JOY21"}, - {KEY_DBL2JOY1+21, "DBLSEC_JOY22"}, - {KEY_DBL2JOY1+22, "DBLSEC_JOY23"}, - {KEY_DBL2JOY1+23, "DBLSEC_JOY24"}, - {KEY_DBL2JOY1+24, "DBLSEC_JOY25"}, - {KEY_DBL2JOY1+25, "DBLSEC_JOY26"}, - {KEY_DBL2JOY1+26, "DBLSEC_JOY27"}, - {KEY_DBL2JOY1+27, "DBLSEC_JOY28"}, - {KEY_DBL2JOY1+28, "DBLSEC_JOY29"}, - {KEY_DBL2JOY1+29, "DBLSEC_JOY30"}, - {KEY_DBL2JOY1+30, "DBLSEC_JOY31"}, - {KEY_DBL2JOY1+31, "DBLSEC_JOY32"}, -#endif - {KEY_DBL2HAT1+0, "DBLSEC_HATUP"}, - {KEY_DBL2HAT1+1, "DBLSEC_HATDOWN"}, - {KEY_DBL2HAT1+2, "DBLSEC_HATLEFT"}, - {KEY_DBL2HAT1+3, "DBLSEC_HATRIGHT"}, - {KEY_DBL2HAT1+4, "DBLSEC_HATUP2"}, - {KEY_DBL2HAT1+5, "DBLSEC_HATDOWN2"}, - {KEY_DBL2HAT1+6, "DBLSEC_HATLEFT2"}, - {KEY_DBL2HAT1+7, "DBLSEC_HATRIGHT2"}, - {KEY_DBL2HAT1+8, "DBLSEC_HATUP3"}, - {KEY_DBL2HAT1+9, "DBLSEC_HATDOWN3"}, - {KEY_DBL2HAT1+10, "DBLSEC_HATLEFT3"}, - {KEY_DBL2HAT1+11, "DBLSEC_HATRIGHT3"}, - {KEY_DBL2HAT1+12, "DBLSEC_HATUP4"}, - {KEY_DBL2HAT1+13, "DBLSEC_HATDOWN4"}, - {KEY_DBL2HAT1+14, "DBLSEC_HATLEFT4"}, - {KEY_DBL2HAT1+15, "DBLSEC_HATRIGHT4"}, - - - {KEY_3JOY1+0, "TRD_JOY1"}, - {KEY_3JOY1+1, "TRD_JOY2"}, - {KEY_3JOY1+2, "TRD_JOY3"}, - {KEY_3JOY1+3, "TRD_JOY4"}, - {KEY_3JOY1+4, "TRD_JOY5"}, - {KEY_3JOY1+5, "TRD_JOY6"}, - {KEY_3JOY1+6, "TRD_JOY7"}, - {KEY_3JOY1+7, "TRD_JOY8"}, - {KEY_3JOY1+8, "TRD_JOY9"}, - {KEY_3JOY1+9, "TRD_JOY10"}, - {KEY_3JOY1+10, "TRD_JOY11"}, - {KEY_3JOY1+11, "TRD_JOY12"}, - {KEY_3JOY1+12, "TRD_JOY13"}, - {KEY_3JOY1+13, "TRD_JOY14"}, - {KEY_3JOY1+14, "TRD_JOY15"}, - {KEY_3JOY1+15, "TRD_JOY16"}, - {KEY_3JOY1+16, "TRD_JOY17"}, - {KEY_3JOY1+17, "TRD_JOY18"}, - {KEY_3JOY1+18, "TRD_JOY19"}, - {KEY_3JOY1+19, "TRD_JOY20"}, - {KEY_3JOY1+20, "TRD_JOY21"}, - {KEY_3JOY1+21, "TRD_JOY22"}, - {KEY_3JOY1+22, "TRD_JOY23"}, - {KEY_3JOY1+23, "TRD_JOY24"}, - {KEY_3JOY1+24, "TRD_JOY25"}, - {KEY_3JOY1+25, "TRD_JOY26"}, - {KEY_3JOY1+26, "TRD_JOY27"}, - {KEY_3JOY1+27, "TRD_JOY28"}, - {KEY_3JOY1+28, "TRD_JOY29"}, - {KEY_3JOY1+29, "TRD_JOY30"}, - {KEY_3JOY1+30, "TRD_JOY31"}, - {KEY_3JOY1+31, "TRD_JOY32"}, - - {KEY_DBL3JOY1+0, "DBLTRD_JOY1"}, - {KEY_DBL3JOY1+1, "DBLTRD_JOY2"}, - {KEY_DBL3JOY1+2, "DBLTRD_JOY3"}, - {KEY_DBL3JOY1+3, "DBLTRD_JOY4"}, - {KEY_DBL3JOY1+4, "DBLTRD_JOY5"}, - {KEY_DBL3JOY1+5, "DBLTRD_JOY6"}, - {KEY_DBL3JOY1+6, "DBLTRD_JOY7"}, - {KEY_DBL3JOY1+7, "DBLTRD_JOY8"}, - {KEY_DBL3JOY1+8, "DBLTRD_JOY9"}, - {KEY_DBL3JOY1+9, "DBLTRD_JOY10"}, - {KEY_DBL3JOY1+10, "DBLTRD_JOY11"}, - {KEY_DBL3JOY1+11, "DBLTRD_JOY12"}, - {KEY_DBL3JOY1+12, "DBLTRD_JOY13"}, - {KEY_DBL3JOY1+13, "DBLTRD_JOY14"}, - {KEY_DBL3JOY1+14, "DBLTRD_JOY15"}, - {KEY_DBL3JOY1+15, "DBLTRD_JOY16"}, - {KEY_DBL3JOY1+16, "DBLTRD_JOY17"}, - {KEY_DBL3JOY1+17, "DBLTRD_JOY18"}, - {KEY_DBL3JOY1+18, "DBLTRD_JOY19"}, - {KEY_DBL3JOY1+19, "DBLTRD_JOY20"}, - {KEY_DBL3JOY1+20, "DBLTRD_JOY21"}, - {KEY_DBL3JOY1+21, "DBLTRD_JOY22"}, - {KEY_DBL3JOY1+22, "DBLTRD_JOY23"}, - {KEY_DBL3JOY1+23, "DBLTRD_JOY24"}, - {KEY_DBL3JOY1+24, "DBLTRD_JOY25"}, - {KEY_DBL3JOY1+25, "DBLTRD_JOY26"}, - {KEY_DBL3JOY1+26, "DBLTRD_JOY27"}, - {KEY_DBL3JOY1+27, "DBLTRD_JOY28"}, - {KEY_DBL3JOY1+28, "DBLTRD_JOY29"}, - {KEY_DBL3JOY1+29, "DBLTRD_JOY30"}, - {KEY_DBL3JOY1+30, "DBLTRD_JOY31"}, - {KEY_DBL3JOY1+31, "DBLTRD_JOY32"}, - - {KEY_3HAT1+0, "TRD_HATUP"}, - {KEY_3HAT1+1, "TRD_HATDOWN"}, - {KEY_3HAT1+2, "TRD_HATLEFT"}, - {KEY_3HAT1+3, "TRD_HATRIGHT"}, - {KEY_3HAT1+4, "TRD_HATUP2"}, - {KEY_3HAT1+5, "TRD_HATDOWN2"}, - {KEY_3HAT1+6, "TRD_HATLEFT2"}, - {KEY_3HAT1+7, "TRD_HATRIGHT2"}, - {KEY_3HAT1+8, "TRD_HATUP3"}, - {KEY_3HAT1+9, "TRD_HATDOWN3"}, - {KEY_3HAT1+10, "TRD_HATLEFT3"}, - {KEY_3HAT1+11, "TRD_HATRIGHT3"}, - {KEY_3HAT1+12, "TRD_HATUP4"}, - {KEY_3HAT1+13, "TRD_HATDOWN4"}, - {KEY_3HAT1+14, "TRD_HATLEFT4"}, - {KEY_3HAT1+15, "TRD_HATRIGHT4"}, - - {KEY_DBL3HAT1+0, "DBLTRD_HATUP"}, - {KEY_DBL3HAT1+1, "DBLTRD_HATDOWN"}, - {KEY_DBL3HAT1+2, "DBLTRD_HATLEFT"}, - {KEY_DBL3HAT1+3, "DBLTRD_HATRIGHT"}, - {KEY_DBL3HAT1+4, "DBLTRD_HATUP2"}, - {KEY_DBL3HAT1+5, "DBLTRD_HATDOWN2"}, - {KEY_DBL3HAT1+6, "DBLTRD_HATLEFT2"}, - {KEY_DBL3HAT1+7, "DBLTRD_HATRIGHT2"}, - {KEY_DBL3HAT1+8, "DBLTRD_HATUP3"}, - {KEY_DBL3HAT1+9, "DBLTRD_HATDOWN3"}, - {KEY_DBL3HAT1+10, "DBLTRD_HATLEFT3"}, - {KEY_DBL3HAT1+11, "DBLTRD_HATRIGHT3"}, - {KEY_DBL3HAT1+12, "DBLTRD_HATUP4"}, - {KEY_DBL3HAT1+13, "DBLTRD_HATDOWN4"}, - {KEY_DBL3HAT1+14, "DBLTRD_HATLEFT4"}, - {KEY_DBL3HAT1+15, "DBLTRD_HATRIGHT4"}, - - {KEY_4JOY1+0, "FOR_JOY1"}, - {KEY_4JOY1+1, "FOR_JOY2"}, - {KEY_4JOY1+2, "FOR_JOY3"}, - {KEY_4JOY1+3, "FOR_JOY4"}, - {KEY_4JOY1+4, "FOR_JOY5"}, - {KEY_4JOY1+5, "FOR_JOY6"}, - {KEY_4JOY1+6, "FOR_JOY7"}, - {KEY_4JOY1+7, "FOR_JOY8"}, - {KEY_4JOY1+8, "FOR_JOY9"}, - {KEY_4JOY1+9, "FOR_JOY10"}, - {KEY_4JOY1+10, "FOR_JOY11"}, - {KEY_4JOY1+11, "FOR_JOY12"}, - {KEY_4JOY1+12, "FOR_JOY13"}, - {KEY_4JOY1+13, "FOR_JOY14"}, - {KEY_4JOY1+14, "FOR_JOY15"}, - {KEY_4JOY1+15, "FOR_JOY16"}, - {KEY_4JOY1+16, "FOR_JOY17"}, - {KEY_4JOY1+17, "FOR_JOY18"}, - {KEY_4JOY1+18, "FOR_JOY19"}, - {KEY_4JOY1+19, "FOR_JOY20"}, - {KEY_4JOY1+20, "FOR_JOY21"}, - {KEY_4JOY1+21, "FOR_JOY22"}, - {KEY_4JOY1+22, "FOR_JOY23"}, - {KEY_4JOY1+23, "FOR_JOY24"}, - {KEY_4JOY1+24, "FOR_JOY25"}, - {KEY_4JOY1+25, "FOR_JOY26"}, - {KEY_4JOY1+26, "FOR_JOY27"}, - {KEY_4JOY1+27, "FOR_JOY28"}, - {KEY_4JOY1+28, "FOR_JOY29"}, - {KEY_4JOY1+29, "FOR_JOY30"}, - {KEY_4JOY1+30, "FOR_JOY31"}, - {KEY_4JOY1+31, "FOR_JOY32"}, - - {KEY_DBL4JOY1+0, "DBLFOR_JOY1"}, - {KEY_DBL4JOY1+1, "DBLFOR_JOY2"}, - {KEY_DBL4JOY1+2, "DBLFOR_JOY3"}, - {KEY_DBL4JOY1+3, "DBLFOR_JOY4"}, - {KEY_DBL4JOY1+4, "DBLFOR_JOY5"}, - {KEY_DBL4JOY1+5, "DBLFOR_JOY6"}, - {KEY_DBL4JOY1+6, "DBLFOR_JOY7"}, - {KEY_DBL4JOY1+7, "DBLFOR_JOY8"}, - {KEY_DBL4JOY1+8, "DBLFOR_JOY9"}, - {KEY_DBL4JOY1+9, "DBLFOR_JOY10"}, - {KEY_DBL4JOY1+10, "DBLFOR_JOY11"}, - {KEY_DBL4JOY1+11, "DBLFOR_JOY12"}, - {KEY_DBL4JOY1+12, "DBLFOR_JOY13"}, - {KEY_DBL4JOY1+13, "DBLFOR_JOY14"}, - {KEY_DBL4JOY1+14, "DBLFOR_JOY15"}, - {KEY_DBL4JOY1+15, "DBLFOR_JOY16"}, - {KEY_DBL4JOY1+16, "DBLFOR_JOY17"}, - {KEY_DBL4JOY1+17, "DBLFOR_JOY18"}, - {KEY_DBL4JOY1+18, "DBLFOR_JOY19"}, - {KEY_DBL4JOY1+19, "DBLFOR_JOY20"}, - {KEY_DBL4JOY1+20, "DBLFOR_JOY21"}, - {KEY_DBL4JOY1+21, "DBLFOR_JOY22"}, - {KEY_DBL4JOY1+22, "DBLFOR_JOY23"}, - {KEY_DBL4JOY1+23, "DBLFOR_JOY24"}, - {KEY_DBL4JOY1+24, "DBLFOR_JOY25"}, - {KEY_DBL4JOY1+25, "DBLFOR_JOY26"}, - {KEY_DBL4JOY1+26, "DBLFOR_JOY27"}, - {KEY_DBL4JOY1+27, "DBLFOR_JOY28"}, - {KEY_DBL4JOY1+28, "DBLFOR_JOY29"}, - {KEY_DBL4JOY1+29, "DBLFOR_JOY30"}, - {KEY_DBL4JOY1+30, "DBLFOR_JOY31"}, - {KEY_DBL4JOY1+31, "DBLFOR_JOY32"}, - - {KEY_4HAT1+0, "FOR_HATUP"}, - {KEY_4HAT1+1, "FOR_HATDOWN"}, - {KEY_4HAT1+2, "FOR_HATLEFT"}, - {KEY_4HAT1+3, "FOR_HATRIGHT"}, - {KEY_4HAT1+4, "FOR_HATUP2"}, - {KEY_4HAT1+5, "FOR_HATDOWN2"}, - {KEY_4HAT1+6, "FOR_HATLEFT2"}, - {KEY_4HAT1+7, "FOR_HATRIGHT2"}, - {KEY_4HAT1+8, "FOR_HATUP3"}, - {KEY_4HAT1+9, "FOR_HATDOWN3"}, - {KEY_4HAT1+10, "FOR_HATLEFT3"}, - {KEY_4HAT1+11, "FOR_HATRIGHT3"}, - {KEY_4HAT1+12, "FOR_HATUP4"}, - {KEY_4HAT1+13, "FOR_HATDOWN4"}, - {KEY_4HAT1+14, "FOR_HATLEFT4"}, - {KEY_4HAT1+15, "FOR_HATRIGHT4"}, - - {KEY_DBL4HAT1+0, "DBLFOR_HATUP"}, - {KEY_DBL4HAT1+1, "DBLFOR_HATDOWN"}, - {KEY_DBL4HAT1+2, "DBLFOR_HATLEFT"}, - {KEY_DBL4HAT1+3, "DBLFOR_HATRIGHT"}, - {KEY_DBL4HAT1+4, "DBLFOR_HATUP2"}, - {KEY_DBL4HAT1+5, "DBLFOR_HATDOWN2"}, - {KEY_DBL4HAT1+6, "DBLFOR_HATLEFT2"}, - {KEY_DBL4HAT1+7, "DBLFOR_HATRIGHT2"}, - {KEY_DBL4HAT1+8, "DBLFOR_HATUP3"}, - {KEY_DBL4HAT1+9, "DBLFOR_HATDOWN3"}, - {KEY_DBL4HAT1+10, "DBLFOR_HATLEFT3"}, - {KEY_DBL4HAT1+11, "DBLFOR_HATRIGHT3"}, - {KEY_DBL4HAT1+12, "DBLFOR_HATUP4"}, - {KEY_DBL4HAT1+13, "DBLFOR_HATDOWN4"}, - {KEY_DBL4HAT1+14, "DBLFOR_HATLEFT4"}, - {KEY_DBL4HAT1+15, "DBLFOR_HATRIGHT4"}, - + {KEY_AXIS1+0, "AXISX-"}, + {KEY_AXIS1+1, "AXISX+"}, + {KEY_AXIS1+2, "AXISY-"}, + {KEY_AXIS1+3, "AXISY+"}, + {KEY_AXIS1+4, "AXISZ-"}, + {KEY_AXIS1+5, "AXISZ+"}, + {KEY_AXIS1+6, "AXISXRUDDER-"}, + {KEY_AXIS1+7, "AXISXRUDDER+"}, + {KEY_AXIS1+8, "AXISYRUDDER-"}, + {KEY_AXIS1+9, "AXISYRUDDER+"}, + {KEY_AXIS1+10, "AXISZRUDDER-"}, + {KEY_AXIS1+11, "AXISZRUDDER+"}, + {KEY_AXIS1+12, "AXISU-"}, + {KEY_AXIS1+13, "AXISU+"}, + {KEY_AXIS1+14, "AXISV-"}, + {KEY_AXIS1+15, "AXISV+"}, }; static const char *gamecontrolname[num_gamecontrols] = { - "nothing", // a key/button mapped to gc_null has no effect - "aimforward", - "aimbackward", - "turnleft", - "turnright", - "accelerate", - "drift", - "brake", - "spindash", - "fire", - "lookback", - "camreset", - "camtoggle", - "spectate", - "lookup", - "lookdown", - "centerview", - "talkkey", - "teamtalkkey", - "scores", + "null", // a key/button mapped to gc_null has no effect + "up", + "down", + "left", + "right", + "a", + "b", + "c", + "x", + "y", + "z", + "l", + "r", + "start", + "abc", "console", - "pause", - "systemmenu", - "screenshot", - "recordgif", - "viewpoint", - "custom1", - "custom2", - "custom3", }; #define NUMKEYNAMES (sizeof (keynames)/sizeof (keyname_t)) @@ -775,19 +395,24 @@ static const char *gamecontrolname[num_gamecontrols] = // // Detach any keys associated to the given game control // - pass the pointer to the gamecontrol table for the player being edited -void G_ClearControlKeys(INT32 (*setupcontrols)[2], INT32 control) +void G_ClearControlKeys(INT32 (*setupcontrols)[MAXINPUTMAPPING], INT32 control) { - setupcontrols[control][0] = KEY_NULL; - setupcontrols[control][1] = KEY_NULL; + INT32 i; + for (i = 0; i < MAXINPUTMAPPING; i++) + { + setupcontrols[control][i] = KEY_NULL; + } } void G_ClearAllControlKeys(void) { INT32 i, j; - for (i = 0; i < num_gamecontrols; i++) + for (j = 0; j < MAXSPLITSCREENPLAYERS; j++) { - for (j = 0; j < MAXSPLITSCREENPLAYERS; j++) + for (i = 0; i < num_gamecontrols; i++) + { G_ClearControlKeys(gamecontrol[j], i); + } } } @@ -844,121 +469,75 @@ INT32 G_KeyStringtoNum(const char *keystr) void G_DefineDefaultControls(void) { - INT32 i; - + // These defaults are bad & temporary. // Keyboard controls - gamecontroldefault[0][gcs_kart][gc_aimforward ][0] = KEY_UPARROW; - gamecontroldefault[0][gcs_kart][gc_aimbackward][0] = KEY_DOWNARROW; - gamecontroldefault[0][gcs_kart][gc_turnleft ][0] = KEY_LEFTARROW; - gamecontroldefault[0][gcs_kart][gc_turnright ][0] = KEY_RIGHTARROW; - gamecontroldefault[0][gcs_kart][gc_accelerate ][0] = 'a'; - gamecontroldefault[0][gcs_kart][gc_drift ][0] = 's'; - gamecontroldefault[0][gcs_kart][gc_brake ][0] = 'd'; - gamecontroldefault[0][gcs_kart][gc_fire ][0] = KEY_SPACE; - gamecontroldefault[0][gcs_kart][gc_lookback ][0] = KEY_LSHIFT; + gamecontroldefault[gc_up ][0] = KEY_UPARROW; + gamecontroldefault[gc_down ][0] = KEY_DOWNARROW; + gamecontroldefault[gc_left ][0] = KEY_LEFTARROW; + gamecontroldefault[gc_right][0] = KEY_RIGHTARROW; + gamecontroldefault[gc_a ][0] = 'z'; + gamecontroldefault[gc_b ][0] = 'x'; + gamecontroldefault[gc_c ][0] = 'c'; + gamecontroldefault[gc_x ][0] = 'a'; + gamecontroldefault[gc_y ][0] = 's'; + gamecontroldefault[gc_z ][0] = 'd'; + gamecontroldefault[gc_l ][0] = 'q'; + gamecontroldefault[gc_r ][0] = 'e'; + gamecontroldefault[gc_start][0] = 'e'; - gamecontroldefault[0][gcs_kart][gc_pause ][0] = KEY_PAUSE; - gamecontroldefault[0][gcs_kart][gc_console ][0] = KEY_CONSOLE; - gamecontroldefault[0][gcs_kart][gc_screenshot ][0] = KEY_F8; - gamecontroldefault[0][gcs_kart][gc_recordgif ][0] = KEY_F9; - gamecontroldefault[0][gcs_kart][gc_viewpoint ][0] = KEY_F12; - gamecontroldefault[0][gcs_kart][gc_talkkey ][0] = 't'; - //gamecontroldefault[0][gcs_kart][gc_teamkey ][0] = 'y'; - gamecontroldefault[0][gcs_kart][gc_scores ][0] = KEY_TAB; - gamecontroldefault[0][gcs_kart][gc_spectate ][0] = '\''; - gamecontroldefault[0][gcs_kart][gc_lookup ][0] = KEY_PGUP; - gamecontroldefault[0][gcs_kart][gc_lookdown ][0] = KEY_PGDN; - gamecontroldefault[0][gcs_kart][gc_centerview ][0] = KEY_END; - gamecontroldefault[0][gcs_kart][gc_camreset ][0] = KEY_HOME; - gamecontroldefault[0][gcs_kart][gc_camtoggle ][0] = KEY_BACKSPACE; - - for (i = gcs_custom+1; i < num_gamecontrolschemes; i++) // skip gcs_custom - { - // Gamepad controls -- same for all schemes - gamecontroldefault[0][i][gc_accelerate ][1] = KEY_JOY1+0; // A - gamecontroldefault[0][i][gc_lookback ][1] = KEY_JOY1+2; // X - gamecontroldefault[0][i][gc_brake ][1] = KEY_JOY1+1; // B - gamecontroldefault[0][i][gc_fire ][1] = KEY_JOY1+4; // LB - gamecontroldefault[0][i][gc_drift ][1] = KEY_JOY1+5; // RB - - gamecontroldefault[0][i][gc_viewpoint ][1] = KEY_JOY1+3; // Y - gamecontroldefault[0][i][gc_pause ][1] = KEY_JOY1+6; // Back - gamecontroldefault[0][i][gc_systemmenu ][0] = KEY_JOY1+7; // Start - gamecontroldefault[0][i][gc_talkkey ][1] = KEY_HAT1+1; // D-Pad Down - gamecontroldefault[0][i][gc_scores ][1] = KEY_HAT1+0; // D-Pad Up - - gamecontroldefault[1][i][gc_accelerate ][0] = KEY_2JOY1+0; // A - gamecontroldefault[1][i][gc_lookback ][0] = KEY_2JOY1+2; // X - gamecontroldefault[1][i][gc_brake ][0] = KEY_2JOY1+1; // B - gamecontroldefault[1][i][gc_fire ][0] = KEY_2JOY1+4; // LB - gamecontroldefault[1][i][gc_drift ][0] = KEY_2JOY1+5; // RB - - gamecontroldefault[2][i][gc_accelerate ][0] = KEY_3JOY1+0; // A - gamecontroldefault[2][i][gc_lookback ][0] = KEY_3JOY1+2; // X - gamecontroldefault[2][i][gc_brake ][0] = KEY_3JOY1+1; // B - gamecontroldefault[2][i][gc_fire ][0] = KEY_3JOY1+4; // LB - gamecontroldefault[2][i][gc_drift ][0] = KEY_3JOY1+5; // RB - - gamecontroldefault[3][i][gc_accelerate ][0] = KEY_3JOY1+0; // A - gamecontroldefault[3][i][gc_lookback ][0] = KEY_3JOY1+2; // X - gamecontroldefault[3][i][gc_brake ][0] = KEY_3JOY1+1; // B - gamecontroldefault[3][i][gc_fire ][0] = KEY_3JOY1+4; // LB - gamecontroldefault[3][i][gc_drift ][0] = KEY_3JOY1+5; // RB - } + // Gamepad controls + gamecontroldefault[gc_up ][1] = KEY_HAT1+0; // D-Pad Up + gamecontroldefault[gc_down ][1] = KEY_HAT1+1; // D-Pad Down + gamecontroldefault[gc_left ][1] = KEY_HAT1+2; // D-Pad Left + gamecontroldefault[gc_right][1] = KEY_HAT1+3; // D-Pad Right + gamecontroldefault[gc_a ][1] = KEY_JOY1+0; // ?? + gamecontroldefault[gc_b ][1] = KEY_JOY1+1; + gamecontroldefault[gc_c ][1] = KEY_JOY1+2; + gamecontroldefault[gc_x ][1] = KEY_JOY1+3; + gamecontroldefault[gc_y ][1] = KEY_JOY1+6; + gamecontroldefault[gc_z ][1] = KEY_JOY1+8; + gamecontroldefault[gc_l ][1] = KEY_JOY1+4; // LB + gamecontroldefault[gc_r ][1] = KEY_JOY1+5; // RB + gamecontroldefault[gc_start][1] = KEY_JOY1+7; // Start } -INT32 G_GetControlScheme(INT32 (*fromcontrols)[2], const INT32 *gclist, INT32 gclen) +void G_CopyControls(INT32 (*setupcontrols)[MAXINPUTMAPPING], INT32 (*fromcontrols)[MAXINPUTMAPPING], const INT32 *gclist, INT32 gclen) { INT32 i, j, gc; - boolean skipscheme; - - for (i = 1; i < num_gamecontrolschemes; i++) // skip gcs_custom (0) - { - skipscheme = false; - for (j = 0; j < (gclist && gclen ? gclen : num_gamecontrols); j++) - { - gc = (gclist && gclen) ? gclist[j] : j; - if (((fromcontrols[gc][0] && gamecontroldefault[0][i][gc][0]) ? fromcontrols[gc][0] != gamecontroldefault[0][i][gc][0] : true) && - ((fromcontrols[gc][0] && gamecontroldefault[0][i][gc][1]) ? fromcontrols[gc][0] != gamecontroldefault[0][i][gc][1] : true) && - ((fromcontrols[gc][1] && gamecontroldefault[0][i][gc][0]) ? fromcontrols[gc][1] != gamecontroldefault[0][i][gc][0] : true) && - ((fromcontrols[gc][1] && gamecontroldefault[0][i][gc][1]) ? fromcontrols[gc][1] != gamecontroldefault[0][i][gc][1] : true)) - { - skipscheme = true; - break; - } - } - if (!skipscheme) - return i; - } - - return gcs_custom; -} - -void G_CopyControls(INT32 (*setupcontrols)[2], INT32 (*fromcontrols)[2], const INT32 *gclist, INT32 gclen) -{ - INT32 i, gc; for (i = 0; i < (gclist && gclen ? gclen : num_gamecontrols); i++) { gc = (gclist && gclen) ? gclist[i] : i; - setupcontrols[gc][0] = fromcontrols[gc][0]; - setupcontrols[gc][1] = fromcontrols[gc][1]; + + for (j = 0; j < MAXINPUTMAPPING; j++) + { + setupcontrols[gc][j] = fromcontrols[gc][j]; + } } } -void G_SaveKeySetting(FILE *f, INT32 (*fromcontrolsa)[2], INT32 (*fromcontrolsb)[2], INT32 (*fromcontrolsc)[2], INT32 (*fromcontrolsd)[2]) +void G_SaveKeySetting(FILE *f, INT32 (*fromcontrolsa)[MAXINPUTMAPPING], INT32 (*fromcontrolsb)[MAXINPUTMAPPING], INT32 (*fromcontrolsc)[MAXINPUTMAPPING], INT32 (*fromcontrolsd)[MAXINPUTMAPPING]) { - INT32 i; + INT32 i, j; + // TODO: would be nice to get rid of this code duplication for (i = 1; i < num_gamecontrols; i++) { fprintf(f, "setcontrol \"%s\" \"%s\"", gamecontrolname[i], G_KeynumToString(fromcontrolsa[i][0])); - if (fromcontrolsa[i][1]) - fprintf(f, " \"%s\"\n", G_KeynumToString(fromcontrolsa[i][1])); - else - fprintf(f, "\n"); + for (j = 1; j < MAXINPUTMAPPING+1; j++) + { + if (j < MAXINPUTMAPPING && fromcontrolsa[i][j]) + { + fprintf(f, " \"%s\"", G_KeynumToString(fromcontrolsa[i][j])); + } + else + { + fprintf(f, "\n"); + break; + } + } } for (i = 1; i < num_gamecontrols; i++) @@ -966,10 +545,18 @@ void G_SaveKeySetting(FILE *f, INT32 (*fromcontrolsa)[2], INT32 (*fromcontrolsb) fprintf(f, "setcontrol2 \"%s\" \"%s\"", gamecontrolname[i], G_KeynumToString(fromcontrolsb[i][0])); - if (fromcontrolsb[i][1]) - fprintf(f, " \"%s\"\n", G_KeynumToString(fromcontrolsb[i][1])); - else - fprintf(f, "\n"); + for (j = 1; j < MAXINPUTMAPPING+1; j++) + { + if (j < MAXINPUTMAPPING && fromcontrolsb[i][j]) + { + fprintf(f, " \"%s\"", G_KeynumToString(fromcontrolsb[i][j])); + } + else + { + fprintf(f, "\n"); + break; + } + } } for (i = 1; i < num_gamecontrols; i++) @@ -977,10 +564,18 @@ void G_SaveKeySetting(FILE *f, INT32 (*fromcontrolsa)[2], INT32 (*fromcontrolsb) fprintf(f, "setcontrol3 \"%s\" \"%s\"", gamecontrolname[i], G_KeynumToString(fromcontrolsc[i][0])); - if (fromcontrolsc[i][1]) - fprintf(f, " \"%s\"\n", G_KeynumToString(fromcontrolsc[i][1])); - else - fprintf(f, "\n"); + for (j = 1; j < MAXINPUTMAPPING+1; j++) + { + if (j < MAXINPUTMAPPING && fromcontrolsc[i][j]) + { + fprintf(f, " \"%s\"", G_KeynumToString(fromcontrolsc[i][j])); + } + else + { + fprintf(f, "\n"); + break; + } + } } for (i = 1; i < num_gamecontrols; i++) @@ -988,10 +583,18 @@ void G_SaveKeySetting(FILE *f, INT32 (*fromcontrolsa)[2], INT32 (*fromcontrolsb) fprintf(f, "setcontrol4 \"%s\" \"%s\"", gamecontrolname[i], G_KeynumToString(fromcontrolsd[i][0])); - if (fromcontrolsd[i][1]) - fprintf(f, " \"%s\"\n", G_KeynumToString(fromcontrolsd[i][1])); - else - fprintf(f, "\n"); + for (j = 1; j < MAXINPUTMAPPING+1; j++) + { + if (j < MAXINPUTMAPPING && fromcontrolsd[i][j]) + { + fprintf(f, " \"%s\"", G_KeynumToString(fromcontrolsd[i][j])); + } + else + { + fprintf(f, "\n"); + break; + } + } } } @@ -1141,7 +744,7 @@ static INT32 G_FilterKeyByVersion(INT32 numctrl, INT32 keyidx, INT32 player, INT return *keynum1; } -static void setcontrol(INT32 (*gc)[2]) +static void setcontrol(INT32 (*gc)[MAXINPUTMAPPING]) { INT32 numctrl; const char *namectrl; diff --git a/src/g_input.h b/src/g_input.h index 57941d6c1..e976f0e52 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -27,106 +27,65 @@ #define JOYHATS 4 // 4 hats #define JOYAXISSET 4 // 4 Sets of 2 axises +#define MAXINPUTMAPPING 4 + // // mouse and joystick buttons are handled as 'virtual' keys // typedef enum { - KEY_MOUSE1 = NUMKEYS, - KEY_JOY1 = KEY_MOUSE1 + MOUSEBUTTONS, + KEY_JOY1 = NUMKEYS, KEY_HAT1 = KEY_JOY1 + JOYBUTTONS, + KEY_AXIS1 = KEY_HAT1 + JOYHATS*4, - KEY_DBLMOUSE1 =KEY_HAT1 + JOYHATS*4, // double clicks - KEY_DBLJOY1 = KEY_DBLMOUSE1 + MOUSEBUTTONS, - KEY_DBLHAT1 = KEY_DBLJOY1 + JOYBUTTONS, - - KEY_2MOUSE1 = KEY_DBLHAT1 + JOYHATS*4, - KEY_2JOY1 = KEY_2MOUSE1 + MOUSEBUTTONS, - KEY_2HAT1 = KEY_2JOY1 + JOYBUTTONS, - - KEY_DBL2MOUSE1 = KEY_2HAT1 + JOYHATS*4, - KEY_DBL2JOY1 = KEY_DBL2MOUSE1 + MOUSEBUTTONS, - KEY_DBL2HAT1 = KEY_DBL2JOY1 + JOYBUTTONS, - - KEY_3JOY1 = KEY_DBL2HAT1 + JOYHATS*4, - KEY_3HAT1 = KEY_3JOY1 + JOYBUTTONS, - - KEY_DBL3JOY1 = KEY_3HAT1 + JOYHATS*4, - KEY_DBL3HAT1 = KEY_DBL3JOY1 + JOYBUTTONS, - - KEY_4JOY1 = KEY_DBL3HAT1 + JOYHATS*4, - KEY_4HAT1 = KEY_4JOY1 + JOYBUTTONS, - - KEY_DBL4JOY1 = KEY_4HAT1 + JOYHATS*4, - KEY_DBL4HAT1 = KEY_DBL4JOY1 + JOYBUTTONS, - - KEY_MOUSEWHEELUP = KEY_DBL4HAT1 + JOYHATS*4, + KEY_MOUSE1 = KEY_AXIS1 + JOYAXISSET*4, + KEY_MOUSEMOVE = KEY_MOUSE1 + MOUSEBUTTONS, + KEY_MOUSEWHEELUP = KEY_MOUSEMOVE + 4, KEY_MOUSEWHEELDOWN = KEY_MOUSEWHEELUP + 1, - KEY_2MOUSEWHEELUP = KEY_MOUSEWHEELDOWN + 1, - KEY_2MOUSEWHEELDOWN = KEY_2MOUSEWHEELUP + 1, - NUMINPUTS = KEY_2MOUSEWHEELDOWN + 1, + NUMINPUTS = KEY_MOUSEWHEELDOWN + 1, } key_input_e; typedef enum { gc_null = 0, // a key/button mapped to gc_null has no effect - gc_aimforward, - gc_aimbackward, - gc_turnleft, - gc_turnright, - gc_accelerate, - gc_drift, - gc_brake, - gc_spindash, - gc_fire, - gc_lookback, - gc_camreset, - gc_camtoggle, - gc_spectate, - gc_lookup, - gc_lookdown, - gc_centerview, - gc_talkkey, - gc_teamkey, - gc_scores, + + // The actual gamepad + gc_up, + gc_down, + gc_left, + gc_right, + gc_a, + gc_b, + gc_c, + gc_x, + gc_y, + gc_z, + gc_l, + gc_r, + gc_start, + + // special keys + gc_abc, gc_console, - gc_pause, - gc_systemmenu, - gc_screenshot, - gc_recordgif, - gc_viewpoint, - gc_custom1, // Lua scriptable - gc_custom2, // Lua scriptable - gc_custom3, // Lua scriptable + num_gamecontrols } gamecontrols_e; -typedef enum -{ - gcs_custom, - gcs_kart, // Kart doesn't really need this code, like, at all? But I don't feel like removing it. - num_gamecontrolschemes -} gamecontrolschemes_e; - // mouse values are used once extern consvar_t cv_mousesens, cv_mouseysens; extern consvar_t cv_mousesens2, cv_mouseysens2; extern consvar_t cv_controlperkey; -extern INT32 mousex, mousey; -extern INT32 mlooky; //mousey with mlookSensitivity - -extern INT32 joyxmove[MAXSPLITSCREENPLAYERS][JOYAXISSET], joyymove[MAXSPLITSCREENPLAYERS][JOYAXISSET]; - -// current state of the keys: true if pushed -extern UINT8 gamekeydown[NUMINPUTS]; +// current state of the keys: JOYAXISRANGE or 0 when boolean. +// Or anything inbetween for analog values +extern INT32 gamekeydown[MAXSPLITSCREENPLAYERS][NUMINPUTS]; // two key codes (or virtual key) per game control -extern INT32 gamecontrol[MAXSPLITSCREENPLAYERS][num_gamecontrols][2]; -extern INT32 gamecontroldefault[MAXSPLITSCREENPLAYERS][num_gamecontrolschemes][num_gamecontrols][2]; // default control storage, use 0 (gcs_custom) for memory retention -#define PlayerInputDown(p, gc) (gamekeydown[gamecontrol[p-1][gc][0]] || gamekeydown[gamecontrol[p-1][gc][1]]) +extern INT32 gamecontrol[MAXSPLITSCREENPLAYERS][num_gamecontrols][MAXINPUTMAPPING]; +extern INT32 gamecontroldefault[num_gamecontrols][MAXINPUTMAPPING]; // default control storage +/* #define num_gcl_accelerate 1 #define num_gcl_brake 1 #define num_gcl_drift 1 @@ -142,6 +101,7 @@ extern const INT32 gcl_spindash[num_gcl_spindash]; extern const INT32 gcl_movement[num_gcl_movement]; extern const INT32 gcl_item[num_gcl_item]; extern const INT32 gcl_full[num_gcl_full]; +*/ // peace to my little coder fingers! // check a gamecontrol being active or not @@ -154,16 +114,16 @@ const char *G_KeynumToString(INT32 keynum); INT32 G_KeyStringtoNum(const char *keystr); // detach any keys associated to the given game control -void G_ClearControlKeys(INT32 (*setupcontrols)[2], INT32 control); +void G_ClearControlKeys(INT32 (*setupcontrols)[MAXINPUTMAPPING], INT32 control); void G_ClearAllControlKeys(void); void Command_Setcontrol_f(void); void Command_Setcontrol2_f(void); void Command_Setcontrol3_f(void); void Command_Setcontrol4_f(void); void G_DefineDefaultControls(void); -INT32 G_GetControlScheme(INT32 (*fromcontrols)[2], const INT32 *gclist, INT32 gclen); -void G_CopyControls(INT32 (*setupcontrols)[2], INT32 (*fromcontrols)[2], const INT32 *gclist, INT32 gclen); -void G_SaveKeySetting(FILE *f, INT32 (*fromcontrolsa)[2], INT32 (*fromcontrolsb)[2], INT32 (*fromcontrolsc)[2], INT32 (*fromcontrolsd)[2]); +INT32 G_GetControlScheme(INT32 (*fromcontrols)[MAXINPUTMAPPING], const INT32 *gclist, INT32 gclen); +void G_CopyControls(INT32 (*setupcontrols)[MAXINPUTMAPPING], INT32 (*fromcontrols)[MAXINPUTMAPPING], const INT32 *gclist, INT32 gclen); +void G_SaveKeySetting(FILE *f, INT32 (*fromcontrolsa)[MAXINPUTMAPPING], INT32 (*fromcontrolsb)[MAXINPUTMAPPING], INT32 (*fromcontrolsc)[MAXINPUTMAPPING], INT32 (*fromcontrolsd)[MAXINPUTMAPPING]); INT32 G_CheckDoubleUsage(INT32 keynum, boolean modify); #endif diff --git a/src/hu_stuff.c b/src/hu_stuff.c index a1e5b27c7..71fead721 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -916,10 +916,12 @@ void HU_Ticker(void) hu_tick++; hu_tick &= 7; // currently only to blink chat input cursor + /* if (PlayerInputDown(1, gc_scores)) hu_showscores = !chat_on; else hu_showscores = false; + */ hu_keystrokes = false; } @@ -1112,6 +1114,7 @@ boolean HU_Responder(event_t *ev) if (!chat_on) { // enter chat mode +#if 0 if ((ev->data1 == gamecontrol[0][gc_talkkey][0] || ev->data1 == gamecontrol[0][gc_talkkey][1]) && netgame && !OLD_MUTE) // check for old chat mute, still let the players open the chat incase they want to scroll otherwise. { @@ -1132,6 +1135,7 @@ boolean HU_Responder(event_t *ev) typelines = 1; return true; } +#endif } else // if chat_on { @@ -1147,8 +1151,8 @@ boolean HU_Responder(event_t *ev) // Ignore non-keyboard keys, except when the talk key is bound if (ev->data1 >= KEY_MOUSE1 - && (ev->data1 != gamecontrol[0][gc_talkkey][0] - && ev->data1 != gamecontrol[0][gc_talkkey][1])) + /*&& (ev->data1 != gamecontrol[0][gc_talkkey][0] + && ev->data1 != gamecontrol[0][gc_talkkey][1])*/) return false; c = CON_ShiftChar(c); @@ -1210,9 +1214,9 @@ boolean HU_Responder(event_t *ev) I_UpdateMouseGrab(); } else if (c == KEY_ESCAPE - || ((c == gamecontrol[0][gc_talkkey][0] || c == gamecontrol[0][gc_talkkey][1] + /*|| ((c == gamecontrol[0][gc_talkkey][0] || c == gamecontrol[0][gc_talkkey][1] || c == gamecontrol[0][gc_teamkey][0] || c == gamecontrol[0][gc_teamkey][1]) - && c >= KEY_MOUSE1)) // If it's not a keyboard key, then the chat button is used as a toggle. + && c >= KEY_MOUSE1)*/) // If it's not a keyboard key, then the chat button is used as a toggle. { chat_on = false; c_input = 0; // reset input cursor diff --git a/src/k_menufunc.c b/src/k_menufunc.c index b91276feb..171fc2c49 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -835,9 +835,9 @@ boolean M_Responder(event_t *ev) if (ch == -1) return false; - else if (ch == gamecontrol[0][gc_systemmenu][0] || ch == gamecontrol[0][gc_systemmenu][1]) // allow remappable ESC key - ch = KEY_ESCAPE; - else if ((ch == gamecontrol[0][gc_accelerate][0] || ch == gamecontrol[0][gc_accelerate][1]) && ch >= KEY_MOUSE1) + /*else if (ch == gamecontrol[0][gc_systemmenu][0] || ch == gamecontrol[0][gc_systemmenu][1]) // allow remappable ESC key + ch = KEY_ESCAPE;*/ + else if ((ch == gamecontrol[0][gc_a][0] || ch == gamecontrol[0][gc_a][1]) && ch >= KEY_MOUSE1) ch = KEY_ENTER; // F-Keys @@ -917,7 +917,7 @@ boolean M_Responder(event_t *ev) return false; } - if ((ch == gamecontrol[0][gc_brake][0] || ch == gamecontrol[0][gc_brake][1]) && ch >= KEY_MOUSE1) // do this here, otherwise brake opens the menu mid-game + if ((ch == gamecontrol[0][gc_b][0] || ch == gamecontrol[0][gc_b][1]) && ch >= KEY_MOUSE1) // do this here, otherwise brake opens the menu mid-game ch = KEY_ESCAPE; routine = currentMenu->menuitems[itemOn].itemaction; @@ -961,12 +961,11 @@ boolean M_Responder(event_t *ev) else { // dirty hack: for customising controls, I want only buttons/keys, not moves - if (ev->type == ev_mouse - || ev->type == ev_joystick - || ev->type == ev_joystick2 - || ev->type == ev_joystick3 - || ev->type == ev_joystick4) + if (ev->type == ev_mouse || ev->type == ev_joystick) + { return true; + } + if (routine) { void (*otherroutine)(event_t *sev) = currentMenu->menuitems[itemOn].itemaction; diff --git a/src/m_misc.c b/src/m_misc.c index 32a1efc32..5d048e695 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -624,7 +624,7 @@ void Command_LoadConfig_f(void) for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { - G_CopyControls(gamecontrol[i], gamecontroldefault[i][gcs_kart], NULL, 0); + G_CopyControls(gamecontrol[i], gamecontroldefault, NULL, 0); } // temporarily reset execversion to default @@ -678,7 +678,7 @@ void M_FirstLoadConfig(void) for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { - G_CopyControls(gamecontrol[i], gamecontroldefault[i][gcs_kart], NULL, 0); + G_CopyControls(gamecontrol[i], gamecontroldefault, NULL, 0); } // register execversion here before we load any configs @@ -780,15 +780,7 @@ void M_SaveConfig(const char *filename) if (!dedicated) { - if (tutorialmode && tutorialgcs) - { - // using gcs_custom as temp storage - G_SaveKeySetting(f, gamecontroldefault[0][gcs_custom], gamecontrol[1], gamecontrol[2], gamecontrol[3]); - } - else - { - G_SaveKeySetting(f, gamecontrol[0], gamecontrol[1], gamecontrol[2], gamecontrol[3]); - } + G_SaveKeySetting(f, gamecontrol[0], gamecontrol[1], gamecontrol[2], gamecontrol[3]); } fclose(f); @@ -1771,9 +1763,9 @@ boolean M_ScreenshotResponder(event_t *ev) if (ch >= KEY_MOUSE1 && menuactive) // If it's not a keyboard key, then don't allow it in the menus! return false; - if (ch == KEY_F8 || ch == gamecontrol[0][gc_screenshot][0] || ch == gamecontrol[0][gc_screenshot][1]) // remappable F8 + if (ch == KEY_F8 /*|| ch == gamecontrol[0][gc_screenshot][0] || ch == gamecontrol[0][gc_screenshot][1]*/) // remappable F8 M_ScreenShot(); - else if (ch == KEY_F9 || ch == gamecontrol[0][gc_recordgif][0] || ch == gamecontrol[0][gc_recordgif][1]) // remappable F9 + else if (ch == KEY_F9 /*|| ch == gamecontrol[0][gc_recordgif][0] || ch == gamecontrol[0][gc_recordgif][1]*/) // remappable F9 ((moviemode) ? M_StopMovie : M_StartMovie)(); else return false; diff --git a/src/p_tick.c b/src/p_tick.c index 5e92649cb..1c8a69dd4 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -676,7 +676,7 @@ void P_Ticker(boolean run) G_WriteAllGhostTics(); if (cv_recordmultiplayerdemos.value && (demo.savemode == DSM_NOTSAVING || demo.savemode == DSM_WILLAUTOSAVE)) - if (demo.savebutton && demo.savebutton + 3*TICRATE < leveltime && PlayerInputDown(1, gc_lookback)) + if (demo.savebutton && demo.savebutton + 3*TICRATE < leveltime && G_PlayerInputDown(0, gc_y, false)) demo.savemode = DSM_TITLEENTRY; } else if (demo.playback) // Use Ghost data for consistency checks. diff --git a/src/p_user.c b/src/p_user.c index c58e61b4d..d47718719 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2583,12 +2583,14 @@ void P_InitCameraCmd(void) static ticcmd_t *P_CameraCmd(camera_t *cam) { + /* INT32 forward, axis; //i // these ones used for multiple conditions boolean turnleft, turnright, mouseaiming; boolean invertmouse, lookaxis, usejoystick, kbl; INT32 player_invert; INT32 screen_invert; + */ ticcmd_t *cmd = &cameracmd; (void)cam; @@ -2596,6 +2598,7 @@ static ticcmd_t *P_CameraCmd(camera_t *cam) if (!demo.playback) return cmd; // empty cmd, no. + /* kbl = democam.keyboardlook; G_CopyTiccmd(cmd, I_BaseTiccmd(), 1); // empty, or external driver @@ -2640,7 +2643,7 @@ static ticcmd_t *P_CameraCmd(camera_t *cam) cmd->turning -= (mousex * 8) * (encoremode ? -1 : 1); axis = PlayerJoyAxis(1, AXISMOVE); - if (PlayerInputDown(1, gc_accelerate) || (usejoystick && axis > 0)) + if (PlayerInputDown(1, gc_a) || (usejoystick && axis > 0)) cmd->buttons |= BT_ACCELERATE; axis = PlayerJoyAxis(1, AXISBRAKE); if (PlayerInputDown(1, gc_brake) || (usejoystick && axis > 0)) @@ -2686,8 +2689,6 @@ static ticcmd_t *P_CameraCmd(camera_t *cam) if (PlayerInputDown(1, gc_centerview)) // No need to put a spectator limit on this one though :V cmd->aiming = 0; - mousex = mousey = mlooky = 0; - cmd->forwardmove += (SINT8)forward; if (cmd->forwardmove > MAXPLMOVE) @@ -2701,6 +2702,7 @@ static ticcmd_t *P_CameraCmd(camera_t *cam) cmd->turning = -KART_FULLTURN; democam.keyboardlook = kbl; + */ return cmd; } diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index fbac5e7a7..ea020afa5 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1079,7 +1079,7 @@ void I_ShutdownJoystick(UINT8 index) void I_GetJoystickEvents(UINT8 index) { - static event_t event = {0,0,0,0}; + static event_t event = {0,0,0,0,0}; INT32 i = 0; UINT64 joyhats = 0; #if 0 diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index a493b9edf..ad5b3723d 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -526,13 +526,14 @@ static inline void SDLJoyRemap(event_t *event) (void)event; } -static INT32 SDLJoyAxis(const Sint16 axis, evtype_t which) +static INT32 SDLJoyAxis(const Sint16 axis, evtype_t which, UINT8 pid) { // -32768 to 32767 INT32 raxis = axis/32; + if (which == ev_joystick) { - if (Joystick[0].bGamepadStyle) + if (Joystick[pid].bGamepadStyle) { // gamepad control type, on or off, live or die if (raxis < -(JOYAXISRANGE/2)) @@ -544,7 +545,7 @@ static INT32 SDLJoyAxis(const Sint16 axis, evtype_t which) } else { - raxis = JoyInfo[0].scale!=1?((raxis/JoyInfo[0].scale)*JoyInfo[0].scale):raxis; + raxis = JoyInfo[pid].scale!=1?((raxis/JoyInfo[pid].scale)*JoyInfo[pid].scale):raxis; #ifdef SDL_JDEADZONE if (-SDL_JDEADZONE <= raxis && raxis <= SDL_JDEADZONE) @@ -552,69 +553,7 @@ static INT32 SDLJoyAxis(const Sint16 axis, evtype_t which) #endif } } - else if (which == ev_joystick2) - { - if (Joystick[1].bGamepadStyle) - { - // gamepad control type, on or off, live or die - if (raxis < -(JOYAXISRANGE/2)) - raxis = -1; - else if (raxis > (JOYAXISRANGE/2)) - raxis = 1; - else raxis = 0; - } - else - { - raxis = JoyInfo[1].scale!=1?((raxis/JoyInfo[1].scale)*JoyInfo[1].scale):raxis; -#ifdef SDL_JDEADZONE - if (-SDL_JDEADZONE <= raxis && raxis <= SDL_JDEADZONE) - raxis = 0; -#endif - } - } - else if (which == ev_joystick3) - { - if (Joystick[2].bGamepadStyle) - { - // gamepad control type, on or off, live or die - if (raxis < -(JOYAXISRANGE/2)) - raxis = -1; - else if (raxis > (JOYAXISRANGE/2)) - raxis = 1; - else raxis = 0; - } - else - { - raxis = JoyInfo[2].scale!=1?((raxis/JoyInfo[2].scale)*JoyInfo[2].scale):raxis; - -#ifdef SDL_JDEADZONE - if (-SDL_JDEADZONE <= raxis && raxis <= SDL_JDEADZONE) - raxis = 0; -#endif - } - } - else if (which == ev_joystick4) - { - if (Joystick[3].bGamepadStyle) - { - // gamepad control type, on or off, live or die - if (raxis < -(JOYAXISRANGE/2)) - raxis = -1; - else if (raxis > (JOYAXISRANGE/2)) - raxis = 1; - else raxis = 0; - } - else - { - raxis = JoyInfo[3].scale!=1?((raxis/JoyInfo[3].scale)*JoyInfo[3].scale):raxis; - -#ifdef SDL_JDEADZONE - if (-SDL_JDEADZONE <= raxis && raxis <= SDL_JDEADZONE) - raxis = 0; -#endif - } - } return raxis; } @@ -679,7 +618,7 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt) { SDLforceUngrabMouse(); } - memset(gamekeydown, 0, NUMKEYS); // TODO this is a scary memset + memset(gamekeydown, 0, sizeof(gamekeydown)); // TODO this is a scary memset if (MOUSE_MENU) { @@ -692,6 +631,9 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt) static void Impl_HandleKeyboardEvent(SDL_KeyboardEvent evt, Uint32 type) { event_t event; + + event.device = 0; // TODO: properly set a device + if (type == SDL_KEYUP) { event.type = ev_keyup; @@ -773,6 +715,8 @@ static void Impl_HandleMouseButtonEvent(SDL_MouseButtonEvent evt, Uint32 type) /// \todo inputEvent.button.which if (USE_MOUSEINPUT) { + event.device = 0; // TODO: properly set a device + if (type == SDL_MOUSEBUTTONUP) { event.type = ev_keyup; @@ -805,6 +749,8 @@ static void Impl_HandleMouseWheelEvent(SDL_MouseWheelEvent evt) SDL_memset(&event, 0, sizeof(event_t)); + event.device = 0; // TODO: properly set a device + if (evt.y > 0) { event.data1 = KEY_MOUSEWHEELUP; @@ -832,45 +778,46 @@ static void Impl_HandleJoystickAxisEvent(SDL_JoyAxisEvent evt) SDL_JoystickID joyid[MAXSPLITSCREENPLAYERS]; UINT8 i; - // Determine the Joystick IDs for each current open joystick - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) - joyid[i] = SDL_JoystickInstanceID(JoyInfo[i].dev); - - evt.axis++; + event.device = INT32_MAX; event.data1 = event.data2 = event.data3 = INT32_MAX; - if (evt.which == joyid[0]) + // Determine the Joystick IDs for each current open joystick + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { - event.type = ev_joystick; + joyid[i] = SDL_JoystickInstanceID(JoyInfo[i].dev); + + if (evt.which == joyid[i]) + { + event.device = i; + } } - else if (evt.which == joyid[1]) + + evt.axis++; + + if (event.device == INT32_MAX) { - event.type = ev_joystick2; + return; } - else if (evt.which == joyid[2]) - { - event.type = ev_joystick3; - } - else if (evt.which == joyid[3]) - { - event.type = ev_joystick4; - } - else return; + //axis if (evt.axis > JOYAXISSET*2) + { return; - //vaule + } + + //vaule[sic] if (evt.axis%2) { event.data1 = evt.axis / 2; - event.data2 = SDLJoyAxis(evt.value, event.type); + event.data2 = SDLJoyAxis(evt.value, event.type, event.device); } else { evt.axis--; event.data1 = evt.axis / 2; - event.data3 = SDLJoyAxis(evt.value, event.type); + event.data3 = SDLJoyAxis(evt.value, event.type, event.device); } + D_PostEvent(&event); } @@ -881,30 +828,30 @@ static void Impl_HandleJoystickHatEvent(SDL_JoyHatEvent evt) SDL_JoystickID joyid[MAXSPLITSCREENPLAYERS]; UINT8 i; + event.device = INT32_MAX; + // Determine the Joystick IDs for each current open joystick for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { joyid[i] = SDL_JoystickInstanceID(JoyInfo[i].dev); - if (evt.hat >= JOYHATS) - return; // ignore hats with too high an index + if (evt.which == joyid[i]) + { + event.device = i; + } + } - if (evt.which == joyid[0]) + if (event.device == INT32_MAX) { - event.data1 = KEY_HAT1 + (evt.hat*4); + return; } - else if (evt.which == joyid[1]) + + if (evt.hat >= JOYHATS) { - event.data1 = KEY_2HAT1 + (evt.hat*4); + return; // ignore hats with too high an index } - else if (evt.which == joyid[2]) - { - event.data1 = KEY_3HAT1 + (evt.hat*4); - } - else if (evt.which == joyid[3]) - { - event.data1 = KEY_4HAT1 + (evt.hat*4); - } - else return; + + event.data1 = KEY_HAT1 + (evt.hat*4); // NOTE: UNFINISHED } @@ -916,27 +863,26 @@ static void Impl_HandleJoystickButtonEvent(SDL_JoyButtonEvent evt, Uint32 type) SDL_JoystickID joyid[MAXSPLITSCREENPLAYERS]; UINT8 i; + event.device = INT32_MAX; + // Determine the Joystick IDs for each current open joystick for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { joyid[i] = SDL_JoystickInstanceID(JoyInfo[i].dev); - if (evt.which == joyid[0]) - { - event.data1 = KEY_JOY1; + if (evt.which == joyid[i]) + { + event.device = i; + } } - else if (evt.which == joyid[1]) + + if (event.device == INT32_MAX) { - event.data1 = KEY_2JOY1; + return; } - else if (evt.which == joyid[2]) - { - event.data1 = KEY_3JOY1; - } - else if (evt.which == joyid[3]) - { - event.data1 = KEY_4JOY1; - } - else return; + + event.data1 = KEY_JOY1; + if (type == SDL_JOYBUTTONUP) { event.type = ev_keyup; @@ -945,15 +891,26 @@ static void Impl_HandleJoystickButtonEvent(SDL_JoyButtonEvent evt, Uint32 type) { event.type = ev_keydown; } - else return; + else + { + return; + } + if (evt.button < JOYBUTTONS) { event.data1 += evt.button; } - else return; + else + { + return; + } SDLJoyRemap(&event); - if (event.type != ev_console) D_PostEvent(&event); + + if (event.type != ev_console) + { + D_PostEvent(&event); + } } @@ -1175,7 +1132,10 @@ void I_GetEvent(void) // In order to make wheels act like buttons, we have to set their state to Up. // This is because wheel messages don't have an up/down state. - gamekeydown[KEY_MOUSEWHEELDOWN] = gamekeydown[KEY_MOUSEWHEELUP] = 0; + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + gamekeydown[i][KEY_MOUSEWHEELDOWN] = gamekeydown[i][KEY_MOUSEWHEELUP] = 0; + } } void I_StartupMouse(void) diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c index 2609c3e31..c365237cb 100644 --- a/src/win32/win_sys.c +++ b/src/win32/win_sys.c @@ -393,6 +393,7 @@ void I_OsPolling(void) { MSG msg; HANDLE ci = GetStdHandle(STD_INPUT_HANDLE); + INT32 i; // we need to dispatch messages to the window // so the window procedure can respond to messages and PostEvent() for keys @@ -419,8 +420,8 @@ void I_OsPolling(void) I_GetEvent(); // reset "emulated keys" - gamekeydown[KEY_MOUSEWHEELUP] = 0; - gamekeydown[KEY_MOUSEWHEELDOWN] = 0; + gamekeydown[0][KEY_MOUSEWHEELUP] = 0; + gamekeydown[0][KEY_MOUSEWHEELDOWN] = 0; } // =========================================================================================== diff --git a/src/y_inter.c b/src/y_inter.c index e840565c1..2d472e19e 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -40,7 +40,7 @@ #include "lua_hud.h" #include "m_random.h" // M_RandomKey -#include "g_input.h" // PlayerInputDown +#include "g_input.h" // G_PlayerInputDown #include "k_battle.h" #include "k_pwrlv.h" #include "k_grandprix.h" @@ -619,7 +619,7 @@ void Y_Ticker(void) if (demo.recording) { - if (demo.savemode == DSM_NOTSAVING && PlayerInputDown(1, gc_lookback)) + if (demo.savemode == DSM_NOTSAVING && G_PlayerInputDown(0, gc_y, false)) demo.savemode = DSM_TITLEENTRY; if (demo.savemode == DSM_WILLSAVE || demo.savemode == DSM_WILLAUTOSAVE) @@ -1478,13 +1478,13 @@ void Y_VoteTicker(void) && !voteclient.playerinfo[i].delay && pickedvote == -1 && votes[p] == -1) { - if (PlayerInputDown(i+1, gc_aimforward) || PlayerJoyAxis(i+1, AXISAIM) < 0) + if (G_PlayerInputDown(i, gc_up, false)) { voteclient.playerinfo[i].selection--; pressed = true; } - if ((PlayerInputDown(i+1, gc_aimbackward) || PlayerJoyAxis(i+1, AXISAIM) > 0) && !pressed) + if (G_PlayerInputDown(i, gc_down, false) && pressed == false) { voteclient.playerinfo[i].selection++; pressed = true; @@ -1495,7 +1495,7 @@ void Y_VoteTicker(void) if (voteclient.playerinfo[i].selection > 3) voteclient.playerinfo[i].selection = 0; - if ((PlayerInputDown(i+1, gc_accelerate) || PlayerJoyAxis(i+1, AXISMOVE) > 0) && !pressed) + if (G_PlayerInputDown(i, gc_a, false) && pressed == false) { D_ModifyClientVote(consoleplayer, voteclient.playerinfo[i].selection, i); pressed = true; From 2e875eb3f09e2e6ba6024f971a5520a02b68cabb Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 27 Nov 2021 21:11:00 +0100 Subject: [PATCH 045/379] Options menu background + sounds --- src/k_menu.h | 23 +++++++++++++++++++++++ src/k_menudef.c | 31 ++++++++++++++++++++++++++++++- src/k_menudraw.c | 36 ++++++++++++++++++++++++++++++++---- src/k_menufunc.c | 42 ++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 125 insertions(+), 7 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 99f651713..f5f4f20e7 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -126,6 +126,7 @@ typedef struct menu_s menuitem_t *menuitems; // menu items INT16 x, y; // x, y of menu + INT16 extra1, extra2; // Can be whatever really! Options menu uses extra1 for bg colour. INT16 transitionID; // only transition if IDs match INT16 transitionTics; // tics for transitions out @@ -200,6 +201,19 @@ extern menu_t PLAY_BattleGamemodesDef; extern menuitem_t OPTIONS_Main[]; extern menu_t OPTIONS_MainDef; +// We'll need this since we're gonna have to dynamically enable and disable options depending on which state we're in. +typedef enum +{ + mopt_controls = 0, + mopt_video, + mopt_sound, + mopt_hud, + mopt_gameplay, + mopt_server, + mopt_data, + mopt_manual, +} mopt_e; + extern menuitem_t OPTIONS_Video[]; extern menu_t OPTIONS_VideoDef; @@ -530,12 +544,19 @@ extern struct optionsmenu_s { modedesc_t modedescs[MAXMODEDESCS]; UINT8 erasecontext; + + // background: + INT16 currcolour; + INT16 lastcolour; + tic_t fade; } optionsmenu; void M_InitOptions(INT32 choice); // necessary for multiplayer since there's some options we won't want to access void M_OptionsTick(void); boolean M_OptionsInputs(INT32 ch); boolean M_OptionsQuit(void); // resets buttons when you quit the options. +void M_OptionsChangeBGColour(INT16 newcolour); // changes the background colour for options + void M_HandleItemToggles(INT32 choice); // For item toggling void M_EraseData(INT32 choice); // For data erasing @@ -667,6 +688,7 @@ void M_DrawAddons(void); 0,\ source,\ 0, 0,\ + 0, 0, \ 1, 10,\ M_DrawKartGamemodeMenu,\ NULL,\ @@ -681,6 +703,7 @@ void M_DrawAddons(void); 0,\ source,\ 0, 0,\ + 0, 0, \ 1, 10,\ M_DrawImageDef,\ NULL,\ diff --git a/src/k_menudef.c b/src/k_menudef.c index 824facbd6..0e2e26111 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -62,6 +62,7 @@ menu_t PLAY_CharSelectDef = { PLAY_CharSelect, 0, 0, 0, 0, + 0, 0, M_DrawCharacterSelect, M_CharacterSelectTick, M_CharacterSelectQuit, @@ -123,6 +124,7 @@ menu_t PLAY_CupSelectDef = { 0, PLAY_CupSelect, 0, 0, + 0, 0, 2, 10, M_DrawCupSelect, M_CupSelectTick, @@ -141,6 +143,7 @@ menu_t PLAY_LevelSelectDef = { 0, PLAY_LevelSelect, 0, 0, + 0, 0, 2, 10, M_DrawLevelSelect, M_LevelSelectTick, @@ -162,6 +165,7 @@ menu_t PLAY_TimeAttackDef = { 0, PLAY_TimeAttack, 0, 0, + 0, 0, 2, 10, M_DrawTimeAttack, NULL, @@ -204,6 +208,7 @@ menu_t PLAY_MP_OptSelectDef = { 0, PLAY_MP_OptSelect, 0, 0, + 0, 0, -1, 1, M_DrawMPOptSelect, M_MPOptSelectTick, @@ -239,6 +244,7 @@ menu_t PLAY_MP_HostDef = { 0, PLAY_MP_Host, 0, 0, + 0, 0, -1, 1, // 1 frame transition.... This is really just because I don't want the black fade when we press esc, hehe M_DrawMPHost, M_MPOptSelectTick, // This handles the unfolding options @@ -274,6 +280,7 @@ menu_t PLAY_MP_JoinIPDef = { 0, PLAY_MP_JoinIP, 0, 0, + 0, 0, -1, 1, // 1 frame transition.... This is really just because I don't want the black fade when we press esc, hehe M_DrawMPJoinIP, M_MPOptSelectTick, // This handles the unfolding options @@ -294,6 +301,7 @@ menu_t PLAY_MP_RoomSelectDef = { PLAY_MP_RoomSelect, 0, 0, 0, 0, + 0, 0, M_DrawMPRoomSelect, M_MPRoomSelectTick, NULL, @@ -304,7 +312,7 @@ menu_t PLAY_MP_RoomSelectDef = { menuitem_t OPTIONS_Main[] = { - {IT_STRING | IT_SUBMENU, "Control Setup", "Remap keys & buttons to your likings.", + {IT_STRING | IT_TRANSTEXT, "Profile Setup", "Remap keys & buttons to your likings.", NULL, NULL, 0, 0}, {IT_STRING | IT_SUBMENU, "Video Options", "Change video settings such as the resolution.", @@ -329,12 +337,14 @@ menuitem_t OPTIONS_Main[] = NULL, M_Manual, 0, 0}, }; +// For options menu, the 'extra1' field will determine the background colour to use for... the background! (What a concept!) menu_t OPTIONS_MainDef = { sizeof (OPTIONS_Main) / sizeof (menuitem_t), &MainDef, 0, OPTIONS_Main, 0, 0, + SKINCOLOR_SLATE, 0, 2, 10, M_DrawOptions, M_OptionsTick, @@ -395,6 +405,7 @@ menu_t OPTIONS_VideoDef = { 0, OPTIONS_Video, 32, 80, + SKINCOLOR_PLAGUE, 0, 2, 10, M_DrawGenericOptions, M_OptionsTick, @@ -415,6 +426,7 @@ menu_t OPTIONS_VideoModesDef = { 0, OPTIONS_VideoModes, 48, 80, + SKINCOLOR_PLAGUE, 0, 2, 10, M_DrawVideoModes, M_OptionsTick, @@ -472,6 +484,7 @@ menu_t OPTIONS_VideoOGLDef = { 0, OPTIONS_VideoOGL, 32, 80, + SKINCOLOR_PLAGUE, 0, 2, 10, M_DrawGenericOptions, M_OptionsTick, @@ -534,6 +547,7 @@ menu_t OPTIONS_SoundDef = { 0, OPTIONS_Sound, 48, 80, + SKINCOLOR_THUNDER, 0, 2, 10, M_DrawGenericOptions, M_OptionsTick, @@ -588,6 +602,7 @@ menu_t OPTIONS_HUDDef = { 0, OPTIONS_HUD, 48, 80, + SKINCOLOR_SUNSLAM, 0, 2, 10, M_DrawGenericOptions, M_OptionsTick, @@ -636,6 +651,7 @@ menu_t OPTIONS_HUDOnlineDef = { 0, OPTIONS_HUDOnline, 48, 80, + SKINCOLOR_SUNSLAM, 0, 2, 10, M_DrawGenericOptions, M_OptionsTick, @@ -688,6 +704,7 @@ menu_t OPTIONS_GameplayDef = { 0, OPTIONS_Gameplay, 48, 80, + SKINCOLOR_SCARLET, 0, 2, 10, M_DrawGenericOptions, M_OptionsTick, @@ -732,6 +749,7 @@ menu_t OPTIONS_GameplayItemsDef = { 0, OPTIONS_GameplayItems, 0, 75, + SKINCOLOR_SCARLET, 0, 2, 10, M_DrawItemToggles, M_OptionsTick, @@ -795,6 +813,7 @@ menu_t OPTIONS_ServerDef = { 0, OPTIONS_Server, 48, 70, // This menu here is slightly higher because there's a lot of options... + SKINCOLOR_VIOLET, 0, 2, 10, M_DrawGenericOptions, M_OptionsTick, @@ -852,6 +871,7 @@ menu_t OPTIONS_ServerAdvancedDef = { 0, OPTIONS_ServerAdvanced, 48, 70, // This menu here is slightly higher because there's a lot of options... + SKINCOLOR_VIOLET, 0, 2, 10, M_DrawGenericOptions, M_OptionsTick, @@ -892,6 +912,7 @@ menu_t OPTIONS_DataDef = { 0, OPTIONS_Data, 48, 80, + SKINCOLOR_BLUEBERRY, 0, 2, 10, M_DrawGenericOptions, M_OptionsTick, @@ -937,6 +958,7 @@ menu_t OPTIONS_DataAddonDef = { 0, OPTIONS_DataAddon, 48, 80, + SKINCOLOR_BLUEBERRY, 0, 2, 10, M_DrawGenericOptions, M_OptionsTick, @@ -976,6 +998,7 @@ menu_t OPTIONS_DataScreenshotDef = { 0, OPTIONS_DataScreenshot, 48, 80, + SKINCOLOR_BLUEBERRY, 0, 2, 10, M_DrawGenericOptions, M_OptionsTick, @@ -999,6 +1022,7 @@ menu_t OPTIONS_DataReplayDef = { 0, OPTIONS_DataReplay, 48, 80, + SKINCOLOR_BLUEBERRY, 0, 2, 10, M_DrawGenericOptions, M_OptionsTick, @@ -1036,6 +1060,7 @@ menu_t OPTIONS_DataDiscordDef = { 0, OPTIONS_DataDiscord, 48, 80, + SKINCOLOR_BLUEBERRY, 0, 2, 10, M_DrawGenericOptions, M_OptionsTick, @@ -1068,6 +1093,7 @@ menu_t OPTIONS_DataEraseDef = { 0, OPTIONS_DataErase, 48, 80, + SKINCOLOR_BLUEBERRY, 0, 2, 10, M_DrawGenericOptions, M_OptionsTick, @@ -1127,6 +1153,7 @@ menu_t PAUSE_MainDef = { 0, PAUSE_Main, 0, 0, + 0, 0, 1, 10, // For transition with some menus! M_DrawPause, M_PauseTick, @@ -1177,6 +1204,7 @@ menu_t PAUSE_PlaybackMenuDef = { PAUSE_PlaybackMenu, BASEVIDWIDTH/2 - 88, 2, 0, 0, + 0, 0, M_DrawPlaybackMenu, NULL, NULL, @@ -1220,6 +1248,7 @@ menu_t MISC_AddonsDef = { MISC_AddonsMenu, 50, 28, 0, 0, + 0, 0, M_DrawAddons, NULL, NULL, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 748a57c70..f529585d2 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1652,10 +1652,34 @@ void M_DrawMPRoomSelect(void) // OPTIONS MENU +// Draws the cogs and also the options background! static void M_DrawOptionsCogs(void) { - patch_t *back[3] = {W_CachePatchName("OPT_BAK1", PU_CACHE), W_CachePatchName("OPT_BAK2", PU_CACHE), W_CachePatchName("OPT_BAK3", PU_CACHE)}; - V_DrawFixedPatch(0, 0, FRACUNIT, 0, back[(optionsmenu.ticker/10) %3], NULL); + // the background isn't drawn outside of being in the main menu state. + if (gamestate == GS_MENU) + { + patch_t *back[3] = {W_CachePatchName("OPT_BG1", PU_CACHE), W_CachePatchName("OPT_BG2", PU_CACHE), W_CachePatchName("OPT_BG3", PU_CACHE)}; + INT32 tflag = 0; + UINT8 *c; + UINT8 *c2; // colormap for the one we're changing + + if (optionsmenu.fade) + { + c2 = R_GetTranslationColormap(TC_DEFAULT, optionsmenu.lastcolour, GTC_CACHE); + V_DrawFixedPatch(0, 0, FRACUNIT, 0, back[(optionsmenu.ticker/10) %3], c2); + + // prepare fade flag: + tflag = min(V_90TRANS, (optionsmenu.fade)<menuitems[i].status & IT_TRANSTEXT) + tflag = V_TRANSLUCENT; + if (!(menutransition.tics && i == itemOn)) { V_DrawFixedPatch(px*FRACUNIT, py*FRACUNIT, FRACUNIT, 0, buttback, c); - V_DrawCenteredGamemodeString(px-3, py - 16, V_ALLOWLOWERCASE, (i == itemOn ? c : NULL), currentMenu->menuitems[i].text); + V_DrawCenteredGamemodeString(px-3, py - 16, V_ALLOWLOWERCASE|tflag, (i == itemOn ? c : NULL), currentMenu->menuitems[i].text); } y += 48; @@ -2076,7 +2104,7 @@ void M_DrawItemToggles(void) if (shitsfree) shitsfree--; - V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y, highlightflags, va("* %s *", currentMenu->menuitems[itemOn].text)); + V_DrawCenteredString(BASEVIDWIDTH/2 + menutransition.tics*48, currentMenu->y, highlightflags, va("* %s *", currentMenu->menuitems[itemOn].text)); } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 86e5325bd..7adff19d6 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1663,6 +1663,7 @@ menu_t MessageDef = 0, // lastOn, flags (TO HACK) MessageMenu, // menuitem_t -> 0, 0, // x, y (TO HACK) + 0, 0, // extra1, extra2 0, 0, // transition tics M_DrawMessageMenu, // drawing routine -> NULL, // ticker routine @@ -2993,7 +2994,15 @@ void M_InitOptions(INT32 choice) { (void)choice; - // @TODO: Change options when you do them from a netgame. + OPTIONS_MainDef.menuitems[mopt_gameplay].status = IT_STRING | IT_SUBMENU; + OPTIONS_MainDef.menuitems[mopt_server].status = IT_STRING | IT_SUBMENU; + + // disable gameplay & server options if you aren't an admin in netgames. (GS_MENU check maybe unecessary but let's not take any chances) + if (netgame && gamestate != GS_MENU && !IsPlayerAdmin(consoleplayer)) + { + OPTIONS_MainDef.menuitems[mopt_gameplay].status = IT_STRING | IT_TRANSTEXT; + OPTIONS_MainDef.menuitems[mopt_server].status = IT_STRING | IT_TRANSTEXT; + } optionsmenu.ticker = 0; optionsmenu.offset = 0; @@ -3003,6 +3012,11 @@ void M_InitOptions(INT32 choice) optionsmenu.toptx = 0; optionsmenu.topty = 0; + // BG setup: + optionsmenu.currcolour = OPTIONS_MainDef.extra1; + optionsmenu.lastcolour = 0; + optionsmenu.fade = 0; + // So that pause doesn't go to the main menu... OPTIONS_MainDef.prevMenu = currentMenu; @@ -3015,6 +3029,14 @@ void M_InitOptions(INT32 choice) M_SetupNextMenu(&OPTIONS_MainDef, false); } +// Prepares changing the colour of the background +void M_OptionsChangeBGColour(INT16 newcolour) +{ + optionsmenu.fade = 10; + optionsmenu.lastcolour = optionsmenu.currcolour; + optionsmenu.currcolour = newcolour; +} + boolean M_OptionsQuit(void) { optionsmenu.toptx = 140-1; @@ -3037,7 +3059,7 @@ void M_OptionsTick(void) optionsmenu.opty = optionsmenu.topty; // Avoid awkward 1 px errors. } - // Garbage: + // Move the button for cool animations if (currentMenu == &OPTIONS_MainDef) { M_OptionsQuit(); // ...So now this is used here. @@ -3048,6 +3070,14 @@ void M_OptionsTick(void) optionsmenu.topty = 50; } + // Handle the background stuff: + if (optionsmenu.fade) + optionsmenu.fade--; + + // change the colour if we aren't matching the current menu colour + if (optionsmenu.currcolour != currentMenu->extra1) + M_OptionsChangeBGColour(currentMenu->extra1); + } boolean M_OptionsInputs(INT32 ch) @@ -3059,6 +3089,7 @@ boolean M_OptionsInputs(INT32 ch) { optionsmenu.offset += 48; M_NextOpt(); + S_StartSound(NULL, sfx_menu1); if (itemOn == 0) optionsmenu.offset -= currentMenu->numitems*48; @@ -3069,6 +3100,7 @@ boolean M_OptionsInputs(INT32 ch) { optionsmenu.offset -= 48; M_PrevOpt(); + S_StartSound(NULL, sfx_menu1); if (itemOn == currentMenu->numitems-1) optionsmenu.offset += currentMenu->numitems*48; @@ -3077,6 +3109,10 @@ boolean M_OptionsInputs(INT32 ch) } case KEY_ENTER: { + + if (currentMenu->menuitems[itemOn].status & IT_TRANSTEXT) + return true; // No. + optionsmenu.optx = 140; optionsmenu.opty = 70; // Default position for the currently selected option. @@ -3450,6 +3486,7 @@ boolean M_PauseInputs(INT32 ch) case KEY_UPARROW: { pausemenu.offset -= 50; // Each item is spaced by 50 px + S_StartSound(NULL, sfx_menu1); M_PrevOpt(); return true; } @@ -3457,6 +3494,7 @@ boolean M_PauseInputs(INT32 ch) case KEY_DOWNARROW: { pausemenu.offset += 50; // Each item is spaced by 50 px + S_StartSound(NULL, sfx_menu1); M_NextOpt(); return true; } From f17b94849143c2d7e465e1f694363408180369fb Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 29 Nov 2021 12:02:56 -0500 Subject: [PATCH 046/379] Update how input is handled in menus to use key bindings VERY broken right now, it lost the delay between key presses. Kinda already sick of the reorganization needed though --- src/console.c | 2 +- src/d_clisrv.c | 6 +- src/g_demo.c | 2 +- src/g_input.c | 13 +- src/g_input.h | 7 +- src/hu_stuff.c | 20 +- src/k_menu.h | 4 +- src/k_menudef.c | 4 +- src/k_menufunc.c | 641 ++++++++++++++++++----------------------------- src/m_misc.c | 2 +- 10 files changed, 277 insertions(+), 424 deletions(-) diff --git a/src/console.c b/src/console.c index d8b777e75..dd9349696 100644 --- a/src/console.c +++ b/src/console.c @@ -915,7 +915,7 @@ boolean CON_Responder(event_t *ev) if (modeattacking || metalrecording || marathonmode) return false; - if (ev->data1 >= KEY_MOUSE1) // See also: HUD_Responder + if (ev->data1 >= KEY_JOY1) // See also: HUD_Responder { INT32 i; for (i = 0; i < num_gamecontrols; i++) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index e39ef5227..cf8cf750c 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1494,7 +1494,7 @@ static void M_ConfirmConnect(event_t *ev) #ifndef NONET if (ev->type == ev_keydown) { - if (ev->data1 == ' ' || ev->data1 == 'y' || ev->data1 == KEY_ENTER || ev->data1 == gamecontrol[0][gc_a][0] || ev->data1 == gamecontrol[0][gc_a][1]) + if (G_PlayerInputDown(0, gc_a, true)) { if (totalfilesrequestednum > 0) { @@ -1517,7 +1517,7 @@ static void M_ConfirmConnect(event_t *ev) M_ClearMenus(true); } - else if (ev->data1 == 'n' || ev->data1 == KEY_ESCAPE|| ev->data1 == gamecontrol[0][gc_b][0] || ev->data1 == gamecontrol[0][gc_b][1]) + else if (G_PlayerInputDown(0, gc_b, true)) { cl_mode = CL_ABORTED; M_ClearMenus(true); @@ -1923,7 +1923,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic if (cl_mode == CL_CONFIRMCONNECT) D_ProcessEvents(); //needed for menu system to receive inputs - if ((gamekeydown[0][KEY_ESCAPE] || gamekeydown[0][KEY_JOY1+1]) || cl_mode == CL_ABORTED) + if (G_PlayerInputDown(0, gc_b, true) || cl_mode == CL_ABORTED) { CONS_Printf(M_GetText("Network game synchronization aborted.\n")); // M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING); diff --git a/src/g_demo.c b/src/g_demo.c index 827dda8b6..b3b74852a 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -3818,7 +3818,7 @@ boolean G_DemoTitleResponder(event_t *ev) return true; } - if (ch == KEY_ENTER || ch >= KEY_MOUSE1) + if (ch == KEY_ENTER || ch >= KEY_JOY1) { demo.savemode = DSM_WILLSAVE; return true; diff --git a/src/g_input.c b/src/g_input.c index 0cd044f67..9ecb24e38 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -483,22 +483,27 @@ void G_DefineDefaultControls(void) gamecontroldefault[gc_z ][0] = 'd'; gamecontroldefault[gc_l ][0] = 'q'; gamecontroldefault[gc_r ][0] = 'e'; - gamecontroldefault[gc_start][0] = 'e'; + gamecontroldefault[gc_start][0] = KEY_ENTER; // Gamepad controls gamecontroldefault[gc_up ][1] = KEY_HAT1+0; // D-Pad Up gamecontroldefault[gc_down ][1] = KEY_HAT1+1; // D-Pad Down gamecontroldefault[gc_left ][1] = KEY_HAT1+2; // D-Pad Left gamecontroldefault[gc_right][1] = KEY_HAT1+3; // D-Pad Right - gamecontroldefault[gc_a ][1] = KEY_JOY1+0; // ?? - gamecontroldefault[gc_b ][1] = KEY_JOY1+1; - gamecontroldefault[gc_c ][1] = KEY_JOY1+2; + gamecontroldefault[gc_a ][1] = KEY_JOY1+0; // A + gamecontroldefault[gc_b ][1] = KEY_JOY1+1; // B + gamecontroldefault[gc_c ][1] = KEY_JOY1+2; // ? gamecontroldefault[gc_x ][1] = KEY_JOY1+3; gamecontroldefault[gc_y ][1] = KEY_JOY1+6; gamecontroldefault[gc_z ][1] = KEY_JOY1+8; gamecontroldefault[gc_l ][1] = KEY_JOY1+4; // LB gamecontroldefault[gc_r ][1] = KEY_JOY1+5; // RB gamecontroldefault[gc_start][1] = KEY_JOY1+7; // Start + + gamecontroldefault[gc_up ][2] = KEY_AXIS1+2; // Axis Y- + gamecontroldefault[gc_down ][2] = KEY_AXIS1+3; // Axis Y+ + gamecontroldefault[gc_left ][2] = KEY_AXIS1+0; // Axis X- + gamecontroldefault[gc_right][2] = KEY_AXIS1+1; // Axis X+ } void G_CopyControls(INT32 (*setupcontrols)[MAXINPUTMAPPING], INT32 (*fromcontrols)[MAXINPUTMAPPING], const INT32 *gclist, INT32 gclen) diff --git a/src/g_input.h b/src/g_input.h index e976f0e52..3542d9ede 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -68,6 +68,11 @@ typedef enum // special keys gc_abc, gc_console, + gc_talk, + gc_teamtalk, + gc_screenshot, + gc_recordgif, + num_gamecontrols } gamecontrols_e; @@ -81,7 +86,7 @@ extern consvar_t cv_controlperkey; // Or anything inbetween for analog values extern INT32 gamekeydown[MAXSPLITSCREENPLAYERS][NUMINPUTS]; -// two key codes (or virtual key) per game control +// several key codes (or virtual key) per game control extern INT32 gamecontrol[MAXSPLITSCREENPLAYERS][num_gamecontrols][MAXINPUTMAPPING]; extern INT32 gamecontroldefault[num_gamecontrols][MAXINPUTMAPPING]; // default control storage diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 71fead721..92ce9fa97 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -1097,16 +1097,24 @@ boolean HU_Responder(event_t *ev) // (Unless if you're sharing a keyboard, since you probably establish when you start chatting that you have dibs on it...) // (Ahhh, the good ol days when I was a kid who couldn't afford an extra USB controller...) - if (ev->data1 >= KEY_MOUSE1) + if (ev->data1 >= KEY_JOY1) { - INT32 i; + INT32 i, j; for (i = 0; i < num_gamecontrols; i++) { - if (gamecontrol[0][i][0] == ev->data1 || gamecontrol[0][i][1] == ev->data1) + for (j = 0; j < MAXINPUTMAPPING; j++) + { + if (gamecontrol[0][i][j] == ev->data1) + break; + } + + if (j < MAXINPUTMAPPING) + { break; + } } - if (i == num_gamecontrols) + if (i == num_gamecontrols && j == MAXINPUTMAPPING) return false; } @@ -1150,7 +1158,7 @@ boolean HU_Responder(event_t *ev) return true; // Ignore non-keyboard keys, except when the talk key is bound - if (ev->data1 >= KEY_MOUSE1 + if (ev->data1 >= KEY_JOY1 /*&& (ev->data1 != gamecontrol[0][gc_talkkey][0] && ev->data1 != gamecontrol[0][gc_talkkey][1])*/) return false; @@ -1216,7 +1224,7 @@ boolean HU_Responder(event_t *ev) else if (c == KEY_ESCAPE /*|| ((c == gamecontrol[0][gc_talkkey][0] || c == gamecontrol[0][gc_talkkey][1] || c == gamecontrol[0][gc_teamkey][0] || c == gamecontrol[0][gc_teamkey][1]) - && c >= KEY_MOUSE1)*/) // If it's not a keyboard key, then the chat button is used as a toggle. + && c >= KEY_JOY1)*/) // If it's not a keyboard key, then the chat button is used as a toggle. { chat_on = false; c_input = 0; // reset input cursor diff --git a/src/k_menu.h b/src/k_menu.h index fea9b40e5..135cee3a1 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -295,6 +295,8 @@ extern char dummystaffname[22]; extern INT16 itemOn; // menu item skull is on, Hack by Tails 09-18-2002 extern INT16 skullAnimCounter; // skull animation counter +extern INT32 menuKey; // keyboard key pressed for menu + extern struct menutransition_s { INT16 tics; INT16 dest; @@ -393,7 +395,7 @@ typedef enum extern consvar_t *setup_playercvars[MAXSPLITSCREENPLAYERS][SPLITCV_MAX]; void M_CharacterSelectInit(INT32 choice); -void M_CharacterSelectHandler(INT32 choice); +boolean M_CharacterSelectHandler(INT32 choice); void M_CharacterSelectTick(void); boolean M_CharacterSelectQuit(void); diff --git a/src/k_menudef.c b/src/k_menudef.c index cf4f131d8..dc0fd1c51 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -49,7 +49,7 @@ menu_t MainDef = KARTGAMEMODEMENU(MainMenu, NULL); menuitem_t PLAY_CharSelect[] = { - {IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, M_CharacterSelectHandler, 0, 0}, + {IT_NOTHING, NULL, NULL, NULL, NULL, 0, 0}, }; menu_t PLAY_CharSelectDef = { @@ -62,7 +62,7 @@ menu_t PLAY_CharSelectDef = { M_DrawCharacterSelect, M_CharacterSelectTick, M_CharacterSelectQuit, - NULL + M_CharacterSelectHandler }; menuitem_t PLAY_MainMenu[] = diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 171fc2c49..688200ec9 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -93,6 +93,8 @@ INT16 itemOn = 0; // menu item skull is on, Hack by Tails 09-18-2002 INT16 skullAnimCounter = 8; // skull animation counter struct menutransition_s menutransition; // Menu transition properties +INT32 menuKey = -1; // keyboard key pressed for menu + // finish wipes between screens boolean menuwipe = false; @@ -660,6 +662,7 @@ static boolean M_ChangeStringCvar(INT32 choice) } break; } + return false; } @@ -702,20 +705,21 @@ static void M_PrevOpt(void) // // M_Responder // +static boolean M_BasicMenuInput(INT32 gc) +{ + return G_PlayerInputDown(0, gc, true); +} + 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 + menuKey = -1; if (dedicated || (demo.playback && demo.title) - || gamestate == GS_INTRO || gamestate == GS_CUTSCENE || gamestate == GS_GAMEEND - || gamestate == GS_CREDITS || gamestate == GS_EVALUATION) + || gamestate == GS_INTRO || gamestate == GS_CUTSCENE || gamestate == GS_GAMEEND + || gamestate == GS_CREDITS || gamestate == GS_EVALUATION) + { return false; + } if (noFurtherInput) { @@ -723,131 +727,25 @@ boolean M_Responder(event_t *ev) // (but still allow shift keyup so caps doesn't get stuck) return false; } - else if (ev->type == ev_keydown) + + if (ev->type == ev_keydown && ev->data1 < KEY_JOY1) { - 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-1) * cv_deadzone[0].value) >> FRACBITS; - if (ev->data3 != INT32_MAX) - { - if (Joystick[0].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[0].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; - } - } + // Record keyboard presses + menuKey = ev->data1; } - if (ch == -1) - return false; - /*else if (ch == gamecontrol[0][gc_systemmenu][0] || ch == gamecontrol[0][gc_systemmenu][1]) // allow remappable ESC key - ch = KEY_ESCAPE;*/ - else if ((ch == gamecontrol[0][gc_a][0] || ch == gamecontrol[0][gc_a][1]) && ch >= KEY_MOUSE1) - ch = KEY_ENTER; + // update keys current state + G_MapEventsToControls(ev); - // F-Keys - if (!menuactive) + // Handle menu handling in-game. + if (menuactive == false) { noFurtherInput = true; - switch (ch) - { #if 0 + // The Fx keys. + switch (menuKey) + { case KEY_F1: // Help key Command_Manual_f(); return true; @@ -868,7 +766,6 @@ boolean M_Responder(event_t *ev) itemOn = 0; return true; -#ifndef DC case KEY_F5: // Video Mode if (modeattacking) return true; @@ -876,7 +773,6 @@ boolean M_Responder(event_t *ev) M_Options(0); M_VideoModeMenu(0); return true; -#endif case KEY_F6: // Empty return true; @@ -901,288 +797,30 @@ boolean M_Responder(event_t *ev) return true; // Spymode on F12 handled in game logic + } #endif - case KEY_ESCAPE: // Pop up menu - if (chat_on) - { - HU_clearChatChars(); - chat_on = false; - } - else - M_StartControlPanel(); - return true; + if (M_BasicMenuInput(gc_start) == true) + { + 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[0][gc_b][0] || ch == gamecontrol[0][gc_b][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; - } - - // Handle menu-specific input handling. If this returns true we skip regular input handling. - if (currentMenu->inputroutine) - { - INT32 res = 0; - if (shiftdown && ch >= 32 && ch <= 127) - ch = shiftxform[ch]; - - res = currentMenu->inputroutine(ch); - - if (res) - return true; - } - - if (currentMenu->menuitems[itemOn].status == IT_MSGHANDLER) - { - if (currentMenu->menuitems[itemOn].mvar1 != 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_joystick) - { - 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) - { - - if (shiftdown && ch >= 32 && ch <= 127) - ch = shiftxform[ch]; - if (M_ChangeStringCvar(ch)) - return true; - else - routine = NULL; - } - else - routine = M_ChangeCvar; - } - - if (currentMenu == &PAUSE_PlaybackMenuDef && !con_destlines) - { - playback_last_menu_interaction_leveltime = leveltime; - // Flip left/right with up/down for the playback menu, since it's a horizontal icon row. - switch (ch) - { - case KEY_LEFTARROW: ch = KEY_UPARROW; break; - case KEY_UPARROW: ch = KEY_RIGHTARROW; break; - case KEY_RIGHTARROW: ch = KEY_DOWNARROW; break; - case KEY_DOWNARROW: ch = KEY_LEFTARROW; break; - - // arbitrary keyboard shortcuts because fuck you - - case '\'': // toggle freecam - M_PlaybackToggleFreecam(0); - break; - - case ']': // ffw / advance frame (depends on if paused or not) - if (paused) - M_PlaybackAdvance(0); - else - M_PlaybackFastForward(0); - break; - - case '[': // rewind /backupframe, uses the same function - M_PlaybackRewind(0); - break; - - case '\\': // pause - M_PlaybackPause(0); - break; - - // viewpoints, an annoyance (tm) - case '-': // viewpoint minus - M_PlaybackSetViews(-1); // yeah lol. - break; - - case '=': // viewpoint plus - M_PlaybackSetViews(1); // yeah lol. - break; - - // switch viewpoints: - case '1': // viewpoint for p1 (also f12) - // maximum laziness: - if (!demo.freecam) - G_AdjustView(1, 1, true); - break; - case '2': // viewpoint for p2 - if (!demo.freecam) - G_AdjustView(2, 1, true); - break; - case '3': // viewpoint for p3 - if (!demo.freecam) - G_AdjustView(3, 1, true); - break; - case '4': // viewpoint for p4 - if (!demo.freecam) - G_AdjustView(4, 1, true); - break; - - default: break; - } - } - - // Keys usable within menu - switch (ch) - { - case KEY_DOWNARROW: - M_NextOpt(); - S_StartSound(NULL, sfx_s3k5b); - return true; - - case KEY_UPARROW: - M_PrevOpt(); - S_StartSound(NULL, sfx_s3k5b); - return true; - - case KEY_LEFTARROW: - if (routine && ((currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_ARROWS - || (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_CVAR)) - { -#if 0 - if (currentMenu != &OP_SoundOptionsDef || itemOn > 3) -#endif - S_StartSound(NULL, sfx_s3k5b); - 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 0 - if (currentMenu != &OP_SoundOptionsDef || itemOn > 3) -#endif - S_StartSound(NULL, sfx_s3k5b); - routine(1); - } - return true; - - case KEY_ENTER: - noFurtherInput = true; - currentMenu->lastOn = itemOn; - -#if 0 - if (currentMenu == &PAUSE_PlaybackMenuDef) - { - boolean held = (boolean)playback_enterheld; - if (held) - return true; - playback_enterheld = 3; - } -#endif - - if (routine) - { - S_StartSound(NULL, sfx_s3k5b); - - 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) - { - M_StartMessage(M_GetText("This cannot be done with complex addons\nor in a cheated game.\n\n(Press a key)\n"), NULL, MM_NOTHING); - return true; - } - } - - 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, false); - break; - } - } - return true; - - case KEY_ESCAPE: - //case KEY_JOY1 + 2: - M_GoBack(0); - return true; - - case KEY_BACKSPACE: -#if 0 - if ((currentMenu->menuitems[itemOn].status) == IT_CONTROL) - { - // detach any keys associated with the game control - G_ClearControlKeys(setupcontrols, currentMenu->menuitems[itemOn].mvar1); - S_StartSound(NULL, sfx_shldls); - return true; - } -#endif - - 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 0 - if (currentMenu != &OP_SoundOptionsDef || itemOn > 3) -#endif - S_StartSound(NULL, sfx_s3k5b); - routine(-1); - return true; - } - - return false; - - default: - break; - } - + // We're in the menu itself now. + // M_Ticker will take care of the rest. return true; } @@ -1479,6 +1117,190 @@ void M_GoBack(INT32 choice) // // M_Ticker // +static boolean M_HandleMenuInput(void) +{ + void (*routine)(INT32 choice); // for some casting problem + + // Handle menu-specific input handling. If this returns true, we skip regular input handling. + if (currentMenu->inputroutine) + { + if (currentMenu->inputroutine(menuKey)) + { + return true; + } + } + + routine = currentMenu->menuitems[itemOn].itemaction; + + // Handle menuitems which need a specific key handling + + /* + // NOPE, we need a generic "typing" menu + // (sort of like the generic message menu) + // so that it can be gamepad friendly. + if (routine && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_KEYHANDLER) + { + routine(-1); + return true; + } + */ + + // TODO: Move this to message menu code + if (currentMenu->menuitems[itemOn].status == IT_MSGHANDLER) + { + if (currentMenu->menuitems[itemOn].mvar1 != MM_EVENTHANDLER) + { + if (M_BasicMenuInput(gc_a) || M_BasicMenuInput(gc_b) || M_BasicMenuInput(gc_start)) + { + if (routine) + { + routine(menuKey); + } + + M_StopMessage(0); + noFurtherInput = true; + return true; + } + + return true; + } + else + { +#if 0 // this shit is crazy + if (routine) + { + void (*otherroutine)(event_t *sev) = currentMenu->menuitems[itemOn].itemaction; + otherroutine(ev); //Alam: what a hack + } +#endif + + 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) + { + // As mentioned earlier, we need a typing submenu. + routine = NULL; + } + else + { + routine = M_ChangeCvar; + } + } + + // Keys usable within menu + if (M_BasicMenuInput(gc_down) == true) + { + M_NextOpt(); + S_StartSound(NULL, sfx_s3k5b); + return true; + } + else if (M_BasicMenuInput(gc_up) == true) + { + M_PrevOpt(); + S_StartSound(NULL, sfx_s3k5b); + return true; + } + else if (M_BasicMenuInput(gc_left) == true) + { + if (routine && ((currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_ARROWS + || (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_CVAR)) + { + S_StartSound(NULL, sfx_s3k5b); + routine(0); + } + + return true; + } + else if (M_BasicMenuInput(gc_right) == true) + { + if (routine && ((currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_ARROWS + || (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_CVAR)) + { + S_StartSound(NULL, sfx_s3k5b); + routine(1); + } + + return true; + } + else if (M_BasicMenuInput(gc_a) == true) + { + noFurtherInput = true; + currentMenu->lastOn = itemOn; + + if (routine) + { + S_StartSound(NULL, sfx_s3k5b); + + 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) + { + M_StartMessage(M_GetText("This cannot be done with complex addons\nor in a cheated game.\n\n(Press a key)\n"), NULL, MM_NOTHING); + return true; + } + } + + 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, false); + break; + } + } + + return true; + } + else if (M_BasicMenuInput(gc_b) == true) + { + M_GoBack(0); + return true; + } + else if (M_BasicMenuInput(gc_c) == 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; + + // Make these CVar options? + if (cv == &cv_chooseskin + || cv == &cv_dummystaff + /* + || cv == &cv_nextmap + || cv == &cv_newgametype + */ + ) + { + return true; + } + + S_StartSound(NULL, sfx_s3k5b); + + routine(-1); + return true; + } + + return false; + } + + return true; +} + void M_Ticker(void) { if (menutransition.tics != 0 || menutransition.dest != 0) @@ -1532,11 +1354,20 @@ void M_Ticker(void) } } + if (noFurtherInput == false) + { + M_HandleMenuInput(); + } + if (currentMenu->tickroutine) + { currentMenu->tickroutine(); + } if (dedicated) + { return; + } if (--skullAnimCounter <= 0) skullAnimCounter = 8; @@ -2103,7 +1934,7 @@ static void M_HandleColorRotate(INT32 choice, setup_player_t *p) } } -void M_CharacterSelectHandler(INT32 choice) +boolean M_CharacterSelectHandler(INT32 choice) { UINT8 i; @@ -2172,6 +2003,8 @@ void M_CharacterSelectHandler(INT32 choice) else M_ClearMenus(true); } + + return true; } // Apply character skin and colour changes while ingame (we just call the skin / color commands.) diff --git a/src/m_misc.c b/src/m_misc.c index 5d048e695..969c63227 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -1760,7 +1760,7 @@ boolean M_ScreenshotResponder(event_t *ev) ch = ev->data1; - if (ch >= KEY_MOUSE1 && menuactive) // If it's not a keyboard key, then don't allow it in the menus! + if (ch >= KEY_JOY1 && menuactive) // If it's not a keyboard key, then don't allow it in the menus! return false; if (ch == KEY_F8 /*|| ch == gamecontrol[0][gc_screenshot][0] || ch == gamecontrol[0][gc_screenshot][1]*/) // remappable F8 From 74288a4de1ad52cc8b88560b0a0052962bba0432 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 3 Dec 2021 14:59:22 -0800 Subject: [PATCH 047/379] Fix compiling errors - Replace itoa with sprintf (itoa is a nonstandard function). - Guard one instance of Discord Rich Presence. --- src/k_menudef.c | 2 ++ src/k_menufunc.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/k_menudef.c b/src/k_menudef.c index 0e2e26111..9754ae366 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -1009,8 +1009,10 @@ menu_t OPTIONS_DataScreenshotDef = { menuitem_t OPTIONS_DataReplay[] = { +#ifdef HAVE_DISCORDRPC {IT_STRING | IT_CVAR, "Rich Presence", "Allow Discord to display game info on your status.", NULL, &cv_discordrp, 0, 0}, +#endif {IT_STRING | IT_CVAR, "Synch. Check Interval", "How often to check for synchronization while playing back a replay.", NULL, &cv_netdemosyncquality, 0, 0}, diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 7adff19d6..902a38d33 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2243,7 +2243,7 @@ static void M_MPConfirmCharacterSelection(void) // colour // (convert the number that's saved to a string we can use) col = setup_player[i].color; - itoa(col, colstr, 10); + sprintf(colstr, "%d", col); strcpy(cmd, commandnames[i][1]); strcat(cmd, colstr); @@ -4033,4 +4033,4 @@ void M_Manual(INT32 choice) MISC_ManualDef.prevMenu = (choice == INT32_MAX ? NULL : currentMenu); M_SetupNextMenu(&MISC_ManualDef, true); -} \ No newline at end of file +} From 4cef6ee0df0a10f228dee9d04ae425987a2a285a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 4 Dec 2021 16:23:21 -0500 Subject: [PATCH 048/379] More menu inputting (delays + partial support for character select) Suddenly stopped compiling as I was working on this, with the same error it gave when before james merged the makefile changes, even though it was fine a minute ago and those changes are distinctly in here, so I guess it ends here. --- src/k_menu.h | 5 +- src/k_menufunc.c | 147 ++++++++++++++++++++++++++------------------ src/win32/win_sys.c | 2 +- 3 files changed, 93 insertions(+), 61 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index c1703e2e6..73df79521 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -335,6 +335,9 @@ extern INT16 skullAnimCounter; // skull animation counter extern INT32 menuKey; // keyboard key pressed for menu +#define MENUDELAYTIME 3 +extern INT16 menuInputDelay; + extern struct menutransition_s { INT16 tics; INT16 dest; @@ -620,7 +623,7 @@ char *M_AddonsHeaderPath(void); void M_Manual(INT32 choice); void M_HandleImageDef(INT32 choice); -// M_MENUDRAW.C +// K_MENUDRAW.C // flags for text highlights #define highlightflags V_ORANGEMAP diff --git a/src/k_menufunc.c b/src/k_menufunc.c index c79ca8cbd..75e582348 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -94,6 +94,7 @@ INT16 skullAnimCounter = 8; // skull animation counter struct menutransition_s menutransition; // Menu transition properties INT32 menuKey = -1; // keyboard key pressed for menu +INT16 menuInputDelay = 0; // Delay before registering multiple inputs. // finish wipes between screens boolean menuwipe = false; @@ -1164,12 +1165,17 @@ static boolean M_HandleMenuInput(void) { void (*routine)(INT32 choice); // for some casting problem + if (menuInputDelay > 0) + { + return false; + } + // Handle menu-specific input handling. If this returns true, we skip regular input handling. if (currentMenu->inputroutine) { if (currentMenu->inputroutine(menuKey)) { - return true; + return false; } } @@ -1205,7 +1211,7 @@ static boolean M_HandleMenuInput(void) return true; } - return true; + return false; } else { @@ -1217,7 +1223,7 @@ static boolean M_HandleMenuInput(void) } #endif - return true; + return false; } } @@ -1255,9 +1261,10 @@ static boolean M_HandleMenuInput(void) { S_StartSound(NULL, sfx_s3k5b); routine(0); + return true; } - return true; + return false; } else if (M_BasicMenuInput(gc_right) == true) { @@ -1266,9 +1273,10 @@ static boolean M_HandleMenuInput(void) { S_StartSound(NULL, sfx_s3k5b); routine(1); + return true; } - return true; + return false; } else if (M_BasicMenuInput(gc_a) == true) { @@ -1341,7 +1349,7 @@ static boolean M_HandleMenuInput(void) return false; } - return true; + return false; } void M_Ticker(void) @@ -1397,9 +1405,17 @@ void M_Ticker(void) } } + if (menuInputDelay > 0) + { + menuInputDelay--; + } + if (noFurtherInput == false) { - M_HandleMenuInput(); + if (M_HandleMenuInput() == true) + { + menuInputDelay = MENUDELAYTIME; + } } if (currentMenu->tickroutine) @@ -1848,60 +1864,71 @@ static void M_SetupReadyExplosions(setup_player_t *p) } } -static void M_HandleCharacterGrid(INT32 choice, setup_player_t *p, UINT8 num) +static void M_HandleCharacterGrid(setup_player_t *p, UINT8 num) { - switch (choice) + if (G_PlayerInputDown(num, gc_down, true) == true) { - case KEY_DOWNARROW: - p->gridy++; - if (p->gridy > 8) - p->gridy = 0; - S_StartSound(NULL, sfx_s3k5b); - break; - case KEY_UPARROW: - p->gridy--; - if (p->gridy < 0) - p->gridy = 8; - S_StartSound(NULL, sfx_s3k5b); - break; - case KEY_RIGHTARROW: - p->gridx++; - if (p->gridx > 8) - p->gridx = 0; - S_StartSound(NULL, sfx_s3k5b); - break; - case KEY_LEFTARROW: - p->gridx--; - if (p->gridx < 0) - p->gridx = 8; - S_StartSound(NULL, sfx_s3k5b); - break; - case KEY_ENTER: - if (setup_chargrid[p->gridx][p->gridy].numskins == 0) - S_StartSound(NULL, sfx_s3k7b); //sfx_s3kb2 + p->gridy++; + if (p->gridy > 8) + p->gridy = 0; + S_StartSound(NULL, sfx_s3k5b); + menuInputDelay = MENUDELAYTIME; + } + else if (G_PlayerInputDown(num, gc_up, true) == true) + { + p->gridy--; + if (p->gridy < 0) + p->gridy = 8; + S_StartSound(NULL, sfx_s3k5b); + menuInputDelay = MENUDELAYTIME; + } + else if (G_PlayerInputDown(num, gc_right, true) == true) + { + p->gridx++; + if (p->gridx > 8) + p->gridx = 0; + S_StartSound(NULL, sfx_s3k5b); + menuInputDelay = MENUDELAYTIME; + } + else if (G_PlayerInputDown(num, gc_left, true) == true) + { + p->gridx--; + if (p->gridx < 0) + p->gridx = 8; + S_StartSound(NULL, sfx_s3k5b); + menuInputDelay = MENUDELAYTIME; + } + else if (G_PlayerInputDown(num, gc_a, true) == true || G_PlayerInputDown(num, gc_start, true) == true) + { + if (setup_chargrid[p->gridx][p->gridy].numskins == 0) + { + S_StartSound(NULL, sfx_s3k7b); //sfx_s3kb2 + } + else + { + if (setup_chargrid[p->gridx][p->gridy].numskins == 1) + p->mdepth = CSSTEP_COLORS; // Skip clones menu else - { - if (setup_chargrid[p->gridx][p->gridy].numskins == 1) - p->mdepth = CSSTEP_COLORS; // Skip clones menu - else - p->mdepth = CSSTEP_ALTS; + p->mdepth = CSSTEP_ALTS; - S_StartSound(NULL, sfx_s3k63); - } - break; - case KEY_ESCAPE: - if (num == setup_numplayers-1) - { - p->mdepth = CSSTEP_NONE; - S_StartSound(NULL, sfx_s3k5b); - } - else - { - S_StartSound(NULL, sfx_s3kb2); - } - break; - default: - break; + S_StartSound(NULL, sfx_s3k63); + } + + menuInputDelay = MENUDELAYTIME; + } + else if (G_PlayerInputDown(num, gc_b, true) == true) + { + if (num == setup_numplayers-1) + { + p->mdepth = CSSTEP_NONE; + S_StartSound(NULL, sfx_s3k5b); + } + else + { + S_StartSound(NULL, sfx_s3kb2); + } + + menuInputDelay = MENUDELAYTIME; } } @@ -1994,14 +2021,16 @@ boolean M_CharacterSelectHandler(INT32 choice) switch (p->mdepth) { case CSSTEP_NONE: // Enter Game - if (choice == KEY_ENTER && i == setup_numplayers) + if (i == setup_numplayers) { + //I_DetectNewControllers(); // Look through all joysticks to see if any have pressed start. + p->mdepth = CSSTEP_CHARS; S_StartSound(NULL, sfx_s3k65); } break; case CSSTEP_CHARS: // Character Select grid - M_HandleCharacterGrid(choice, p, i); + M_HandleCharacterGrid(p, i); break; case CSSTEP_ALTS: // Select clone M_HandleCharRotate(choice, p); diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c index ea580d67c..b3f4f0f1a 100644 --- a/src/win32/win_sys.c +++ b/src/win32/win_sys.c @@ -55,7 +55,7 @@ #include "../screen.h" -#include "../m_menu.h" +#include "../k_menu.h" // Wheel support for Win95/WinNT3.51 #include From 58d5d1759be7bd184e9ccd385405f52e85423a37 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 4 Dec 2021 17:34:34 -0500 Subject: [PATCH 049/379] Set menu delay between menus --- src/k_menu.h | 2 +- src/k_menufunc.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/k_menu.h b/src/k_menu.h index 73df79521..af0162610 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -335,7 +335,7 @@ extern INT16 skullAnimCounter; // skull animation counter extern INT32 menuKey; // keyboard key pressed for menu -#define MENUDELAYTIME 3 +#define MENUDELAYTIME 5 extern INT16 menuInputDelay; extern struct menutransition_s { diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 75e582348..1abc38270 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -892,6 +892,7 @@ void M_StartControlPanel(void) } menuactive = true; + menuInputDelay = MENUDELAYTIME; if (demo.playback) { @@ -1079,6 +1080,8 @@ void M_SetupNextMenu(menu_t *menudef, boolean notransition) { INT16 i; + menuInputDelay = MENUDELAYTIME; + if (!notransition) { if (currentMenu->transitionID == menudef->transitionID From d2e26bbc86536b8c4bc634775ebbcc5bce8d3fe5 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 4 Dec 2021 17:58:23 -0500 Subject: [PATCH 050/379] Finish menu input for char select TODO: - Menu delay shouldn't be a static value and work closer to how it did before (likely requires a more complicated system than just calling G_PlayerInputDown ... menu ticcmds? lol) - Create a sdl function to determine if any of the possible joysticks' buttons are being pressed & return the joystick number & set usejoystick to that number ... for the PRESS START prompt. Already tired of this code, I'm good --- src/k_menu.h | 2 +- src/k_menufunc.c | 144 ++++++++++++++++++++++++----------------------- 2 files changed, 75 insertions(+), 71 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index af0162610..e4b825e5b 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -335,7 +335,7 @@ extern INT16 skullAnimCounter; // skull animation counter extern INT32 menuKey; // keyboard key pressed for menu -#define MENUDELAYTIME 5 +#define MENUDELAYTIME 3 // TODO: recreate how old input holding worked extern INT16 menuInputDelay; extern struct menutransition_s { diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 1abc38270..cb7f2aaf3 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1875,7 +1875,7 @@ static void M_HandleCharacterGrid(setup_player_t *p, UINT8 num) if (p->gridy > 8) p->gridy = 0; S_StartSound(NULL, sfx_s3k5b); - menuInputDelay = MENUDELAYTIME; + p->delay = MENUDELAYTIME; } else if (G_PlayerInputDown(num, gc_up, true) == true) { @@ -1883,7 +1883,7 @@ static void M_HandleCharacterGrid(setup_player_t *p, UINT8 num) if (p->gridy < 0) p->gridy = 8; S_StartSound(NULL, sfx_s3k5b); - menuInputDelay = MENUDELAYTIME; + p->delay = MENUDELAYTIME; } else if (G_PlayerInputDown(num, gc_right, true) == true) { @@ -1891,7 +1891,7 @@ static void M_HandleCharacterGrid(setup_player_t *p, UINT8 num) if (p->gridx > 8) p->gridx = 0; S_StartSound(NULL, sfx_s3k5b); - menuInputDelay = MENUDELAYTIME; + p->delay = MENUDELAYTIME; } else if (G_PlayerInputDown(num, gc_left, true) == true) { @@ -1899,7 +1899,7 @@ static void M_HandleCharacterGrid(setup_player_t *p, UINT8 num) if (p->gridx < 0) p->gridx = 8; S_StartSound(NULL, sfx_s3k5b); - menuInputDelay = MENUDELAYTIME; + p->delay = MENUDELAYTIME; } else if (G_PlayerInputDown(num, gc_a, true) == true || G_PlayerInputDown(num, gc_start, true) == true) { @@ -1917,7 +1917,7 @@ static void M_HandleCharacterGrid(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_s3k63); } - menuInputDelay = MENUDELAYTIME; + p->delay = MENUDELAYTIME; } else if (G_PlayerInputDown(num, gc_b, true) == true) { @@ -1931,80 +1931,81 @@ static void M_HandleCharacterGrid(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_s3kb2); } - menuInputDelay = MENUDELAYTIME; + p->delay = MENUDELAYTIME; } } -static void M_HandleCharRotate(INT32 choice, setup_player_t *p) +static void M_HandleCharRotate(setup_player_t *p, UINT8 num) { UINT8 numclones = setup_chargrid[p->gridx][p->gridy].numskins; - switch (choice) + if (G_PlayerInputDown(num, gc_right, true) == true) { - case KEY_RIGHTARROW: - p->clonenum++; - if (p->clonenum >= numclones) - p->clonenum = 0; - p->rotate = CSROTATETICS; - p->delay = CSROTATETICS; - S_StartSound(NULL, sfx_s3kc3s); - break; - case KEY_LEFTARROW: - p->clonenum--; - if (p->clonenum < 0) - p->clonenum = numclones-1; - p->rotate = -CSROTATETICS; - p->delay = CSROTATETICS; - S_StartSound(NULL, sfx_s3kc3s); - break; - case KEY_ENTER: - p->mdepth = CSSTEP_COLORS; - S_StartSound(NULL, sfx_s3k63); - break; - case KEY_ESCAPE: - p->mdepth = CSSTEP_CHARS; - S_StartSound(NULL, sfx_s3k5b); - break; - default: - break; + p->clonenum++; + if (p->clonenum >= numclones) + p->clonenum = 0; + p->rotate = CSROTATETICS; + p->delay = CSROTATETICS; + S_StartSound(NULL, sfx_s3kc3s); + } + else if (G_PlayerInputDown(num, gc_left, true) == true) + { + p->clonenum--; + if (p->clonenum < 0) + p->clonenum = numclones-1; + p->rotate = -CSROTATETICS; + p->delay = CSROTATETICS; + S_StartSound(NULL, sfx_s3kc3s); + } + else if (G_PlayerInputDown(num, gc_a, true) == true || G_PlayerInputDown(num, gc_start, true) == true) + { + p->mdepth = CSSTEP_COLORS; + S_StartSound(NULL, sfx_s3k63); + p->delay = MENUDELAYTIME; + } + else if (G_PlayerInputDown(num, gc_b, true) == true) + { + p->mdepth = CSSTEP_CHARS; + S_StartSound(NULL, sfx_s3k5b); + p->delay = MENUDELAYTIME; } } -static void M_HandleColorRotate(INT32 choice, setup_player_t *p) +static void M_HandleColorRotate(setup_player_t *p, UINT8 num) { - switch (choice) + if (G_PlayerInputDown(num, gc_right, true) == true) { - case KEY_RIGHTARROW: - p->color++; - if (p->color >= numskincolors) - p->color = 1; - p->rotate = CSROTATETICS; - //p->delay = CSROTATETICS; - S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s - break; - case KEY_LEFTARROW: - p->color--; - if (p->color < 1) - p->color = numskincolors-1; - p->rotate = -CSROTATETICS; - //p->delay = CSROTATETICS; - S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s - break; - case KEY_ENTER: - p->mdepth = CSSTEP_READY; - p->delay = TICRATE; - M_SetupReadyExplosions(p); - S_StartSound(NULL, sfx_s3k4e); - break; - case KEY_ESCAPE: - if (setup_chargrid[p->gridx][p->gridy].numskins == 1) - p->mdepth = CSSTEP_CHARS; // Skip clones menu - else - p->mdepth = CSSTEP_ALTS; - S_StartSound(NULL, sfx_s3k5b); - break; - default: - break; + p->color++; + if (p->color >= numskincolors) + p->color = 1; + p->rotate = CSROTATETICS; + p->delay = MENUDELAYTIME; //CSROTATETICS + S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s + } + else if (G_PlayerInputDown(num, gc_left, true) == true) + { + p->color--; + if (p->color < 1) + p->color = numskincolors-1; + p->rotate = -CSROTATETICS; + p->delay = MENUDELAYTIME; //CSROTATETICS + S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s + } + else if (G_PlayerInputDown(num, gc_a, true) == true || G_PlayerInputDown(num, gc_start, true) == true) + { + p->mdepth = CSSTEP_READY; + p->delay = TICRATE; + M_SetupReadyExplosions(p); + S_StartSound(NULL, sfx_s3k4e); + } + else if (G_PlayerInputDown(num, gc_b, true) == true) + { + if (setup_chargrid[p->gridx][p->gridy].numskins == 1) + p->mdepth = CSSTEP_CHARS; // Skip clones menu + else + p->mdepth = CSSTEP_ALTS; + S_StartSound(NULL, sfx_s3k5b); + p->delay = MENUDELAYTIME; } } @@ -2012,6 +2013,8 @@ boolean M_CharacterSelectHandler(INT32 choice) { UINT8 i; + (void)choice; + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { setup_player_t *p = &setup_player[i]; @@ -2036,17 +2039,18 @@ boolean M_CharacterSelectHandler(INT32 choice) M_HandleCharacterGrid(p, i); break; case CSSTEP_ALTS: // Select clone - M_HandleCharRotate(choice, p); + M_HandleCharRotate(p, i); break; case CSSTEP_COLORS: // Select color - M_HandleColorRotate(choice, p); + M_HandleColorRotate(p, i); break; case CSSTEP_READY: default: // Unready - if (choice == KEY_ESCAPE) + if (G_PlayerInputDown(i, gc_b, true) == true) { p->mdepth = CSSTEP_COLORS; S_StartSound(NULL, sfx_s3k5b); + p->delay = MENUDELAYTIME; } break; } From ecffa909497b7d59dadeab9ffd7b50af77045622 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 6 Dec 2021 14:53:30 -0500 Subject: [PATCH 051/379] Add menucmd system Allows menu input delays to feel sorta similar to how they did before ... maybe could be adjusted further, since it feels just a tad bit touchy to me, but it's much better than before when it was a static value. --- src/k_menu.h | 47 +++++++--- src/k_menufunc.c | 223 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 192 insertions(+), 78 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index e4b825e5b..dfef52055 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -335,8 +335,32 @@ extern INT16 skullAnimCounter; // skull animation counter extern INT32 menuKey; // keyboard key pressed for menu -#define MENUDELAYTIME 3 // TODO: recreate how old input holding worked -extern INT16 menuInputDelay; +#define MENUDELAYTIME 7 + +typedef enum +{ + MBT_A = 1, + MBT_B = 1<<1, + MBT_C = 1<<2, + MBT_X = 1<<3, + MBT_Y = 1<<4, + MBT_Z = 1<<5, + MBT_L = 1<<6, + MBT_R = 1<<7, + MBT_START = 1<<8 +} menuButtonCode_t; + +typedef struct menucmd_s +{ + SINT8 dpad_ud; // up / down dpad + SINT8 dpad_lr; // left / right + UINT32 buttons; // buttons + UINT32 buttonsHeld; // prev frame's buttons + UINT16 delay; // menu wait + UINT32 delayCount; // num times ya did menu wait (to make the wait shorter each time) +} menucmd_t; + +extern menucmd_t menucmd[MAXSPLITSCREENPLAYERS]; extern struct menutransition_s { INT16 tics; @@ -358,6 +382,7 @@ void Addons_option_Onchange(void); void M_SortServerList(void); boolean M_Responder(event_t *ev); +boolean M_MenuButtonPressed(UINT8 pid, UINT32 bt); void M_StartControlPanel(void); void M_ClearMenus(boolean callexitmenufunc); void M_SelectableClearMenus(INT32 choice); @@ -382,20 +407,22 @@ void M_InitPlayerSetupColors(void); void M_FreePlayerSetupColors(void); // If you want to waste a bunch of memory for a limit no one will hit, feel free to boost this to MAXSKINS :P -// I figure this will be enough clone characters to fit onto the character select. -// (If someone runs into it after release I'll probably boost it, though.) -#define MAXCLONES MAXSKINS/16 +// I figure this will be enough clone characters to fit onto one grid space. +#define MAXCLONES MAXSKINS/8 extern struct setup_chargrid_s { SINT8 skinlist[MAXCLONES]; UINT8 numskins; } setup_chargrid[9][9]; -#define CSSTEP_NONE 0 -#define CSSTEP_CHARS 1 -#define CSSTEP_ALTS 2 -#define CSSTEP_COLORS 3 -#define CSSTEP_READY 4 +typedef enum +{ + CSSTEP_NONE = 0, + CSSTEP_CHARS, + CSSTEP_ALTS, + CSSTEP_COLORS, + CSSTEP_READY +} setup_mdepth_t; typedef struct setup_player_s { diff --git a/src/k_menufunc.c b/src/k_menufunc.c index cb7f2aaf3..360d2aadb 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -94,7 +94,7 @@ INT16 skullAnimCounter = 8; // skull animation counter struct menutransition_s menutransition; // Menu transition properties INT32 menuKey = -1; // keyboard key pressed for menu -INT16 menuInputDelay = 0; // Delay before registering multiple inputs. +menucmd_t menucmd[MAXSPLITSCREENPLAYERS]; // finish wipes between screens boolean menuwipe = false; @@ -749,11 +749,6 @@ static void M_PrevOpt(void) // // M_Responder // -static boolean M_BasicMenuInput(INT32 gc) -{ - return G_PlayerInputDown(0, gc, true); -} - boolean M_Responder(event_t *ev) { menuKey = -1; @@ -844,7 +839,7 @@ boolean M_Responder(event_t *ev) } #endif - if (M_BasicMenuInput(gc_start) == true) + if (G_PlayerInputDown(0, gc_start, true) == true) { if (chat_on) { @@ -873,6 +868,8 @@ boolean M_Responder(event_t *ev) // void M_StartControlPanel(void) { + INT32 i; + // intro might call this repeatedly if (menuactive) { @@ -892,7 +889,11 @@ void M_StartControlPanel(void) } menuactive = true; - menuInputDelay = MENUDELAYTIME; + + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + menucmd[i].delay = MENUDELAYTIME; + } if (demo.playback) { @@ -1080,7 +1081,10 @@ void M_SetupNextMenu(menu_t *menudef, boolean notransition) { INT16 i; - menuInputDelay = MENUDELAYTIME; + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + menucmd[i].delay = MENUDELAYTIME; + } if (!notransition) { @@ -1164,24 +1168,90 @@ void M_GoBack(INT32 choice) // // M_Ticker // -static boolean M_HandleMenuInput(void) +static void M_SetMenuDelay(UINT8 i) { - void (*routine)(INT32 choice); // for some casting problem + menucmd[i].delayCount++; + if (menucmd[i].delayCount < 1) + { + // Shouldn't happen, but for safety. + menucmd[i].delayCount = 1; + } - if (menuInputDelay > 0) + menucmd[i].delay = (MENUDELAYTIME / menucmd[i].delayCount); + if (menucmd[i].delay < 1) + { + menucmd[i].delay = 1; + } +} + +static void M_UpdateMenuCMD(UINT8 i) +{ + menucmd[i].dpad_ud = 0; + menucmd[i].dpad_lr = 0; + + menucmd[i].buttonsHeld = menucmd[i].buttons; + menucmd[i].buttons = 0; + + if (G_PlayerInputDown(i, gc_up, true)) { menucmd[i].dpad_ud--; } + if (G_PlayerInputDown(i, gc_down, true)) { menucmd[i].dpad_ud++; } + + if (G_PlayerInputDown(i, gc_left, true)) { menucmd[i].dpad_lr--; } + if (G_PlayerInputDown(i, gc_right, true)) { menucmd[i].dpad_lr++; } + + if (G_PlayerInputDown(i, gc_a, true)) { menucmd[i].buttons |= MBT_A; } + if (G_PlayerInputDown(i, gc_b, true)) { menucmd[i].buttons |= MBT_B; } + if (G_PlayerInputDown(i, gc_c, true)) { menucmd[i].buttons |= MBT_C; } + if (G_PlayerInputDown(i, gc_x, true)) { menucmd[i].buttons |= MBT_X; } + if (G_PlayerInputDown(i, gc_y, true)) { menucmd[i].buttons |= MBT_Y; } + if (G_PlayerInputDown(i, gc_z, true)) { menucmd[i].buttons |= MBT_Z; } + if (G_PlayerInputDown(i, gc_l, true)) { menucmd[i].buttons |= MBT_L; } + if (G_PlayerInputDown(i, gc_r, true)) { menucmd[i].buttons |= MBT_R; } + if (G_PlayerInputDown(i, gc_start, true)) { menucmd[i].buttons |= MBT_START; } + + if (menucmd[i].dpad_ud == 0 && menucmd[i].dpad_lr == 0 && menucmd[i].buttons == 0) + { + // Reset delay count with no buttons. + menucmd[i].delayCount = 0; + } +} + +boolean M_MenuButtonPressed(UINT8 pid, UINT32 bt) +{ + if (menucmd[pid].buttonsHeld & bt) { return false; } + return (menucmd[pid].buttons & bt); +} + +static void M_HandleMenuInput(void) +{ + void (*routine)(INT32 choice); // for some casting problem + INT32 i; + UINT8 pid = 0; // todo: Add ability for any splitscreen player to bring up the menu. + SINT8 lr = 0, ud = 0; + + // Update menu CMD + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + M_UpdateMenuCMD(i); + } + // Handle menu-specific input handling. If this returns true, we skip regular input handling. if (currentMenu->inputroutine) { if (currentMenu->inputroutine(menuKey)) { - return false; + return; } } + if (menucmd[pid].delay > 0) + { + return; + } + routine = currentMenu->menuitems[itemOn].itemaction; // Handle menuitems which need a specific key handling @@ -1193,7 +1263,7 @@ static boolean M_HandleMenuInput(void) if (routine && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_KEYHANDLER) { routine(-1); - return true; + return; } */ @@ -1202,7 +1272,7 @@ static boolean M_HandleMenuInput(void) { if (currentMenu->menuitems[itemOn].mvar1 != MM_EVENTHANDLER) { - if (M_BasicMenuInput(gc_a) || M_BasicMenuInput(gc_b) || M_BasicMenuInput(gc_start)) + if (menucmd[pid].buttons != 0 && menucmd[pid].buttonsHeld == 0) { if (routine) { @@ -1211,10 +1281,11 @@ static boolean M_HandleMenuInput(void) M_StopMessage(0); noFurtherInput = true; - return true; + M_SetMenuDelay(pid); + return; } - return false; + return; } else { @@ -1226,7 +1297,7 @@ static boolean M_HandleMenuInput(void) } #endif - return false; + return; } } @@ -1244,44 +1315,53 @@ static boolean M_HandleMenuInput(void) } } + lr = menucmd[pid].dpad_lr; + ud = menucmd[pid].dpad_ud; + + // LR does nothing in the default menu, just remap as dpad. + if (menucmd[pid].buttons & MBT_L) { lr--; } + if (menucmd[pid].buttons & MBT_R) { lr++; } + // Keys usable within menu - if (M_BasicMenuInput(gc_down) == true) + if (ud > 0) { M_NextOpt(); S_StartSound(NULL, sfx_s3k5b); - return true; + M_SetMenuDelay(pid); + return; } - else if (M_BasicMenuInput(gc_up) == true) + else if (ud < 0) { M_PrevOpt(); S_StartSound(NULL, sfx_s3k5b); - return true; + M_SetMenuDelay(pid); + return; } - else if (M_BasicMenuInput(gc_left) == true) + else if (lr < 0) { if (routine && ((currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_ARROWS || (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_CVAR)) { S_StartSound(NULL, sfx_s3k5b); routine(0); - return true; + M_SetMenuDelay(pid); } - return false; + return; } - else if (M_BasicMenuInput(gc_right) == true) + else if (lr > 0) { if (routine && ((currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_ARROWS || (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_CVAR)) { S_StartSound(NULL, sfx_s3k5b); routine(1); - return true; + M_SetMenuDelay(pid); } - return false; + return; } - else if (M_BasicMenuInput(gc_a) == true) + else if ((menucmd[pid].buttons & MBT_A) || (menucmd[pid].buttons & MBT_X) /*|| (menucmd[pid].buttons & MBT_START)*/) { noFurtherInput = true; currentMenu->lastOn = itemOn; @@ -1297,7 +1377,7 @@ static boolean M_HandleMenuInput(void) if (((currentMenu->menuitems[itemOn].status & IT_CALLTYPE) & IT_CALL_NOTMODIFIED) && majormods) { M_StartMessage(M_GetText("This cannot be done with complex addons\nor in a cheated game.\n\n(Press a key)\n"), NULL, MM_NOTHING); - return true; + return; } } @@ -1317,14 +1397,16 @@ static boolean M_HandleMenuInput(void) } } - return true; + M_SetMenuDelay(pid); + return; } - else if (M_BasicMenuInput(gc_b) == true) + else if ((menucmd[pid].buttons & MBT_B) || (menucmd[pid].buttons & MBT_Y)) { M_GoBack(0); - return true; + M_SetMenuDelay(pid); + return; } - else if (M_BasicMenuInput(gc_c) == true) + else if ((menucmd[pid].buttons & MBT_C) || (menucmd[pid].buttons & MBT_Z)) { if (routine && ((currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_ARROWS || (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_CVAR)) @@ -1340,23 +1422,26 @@ static boolean M_HandleMenuInput(void) */ ) { - return true; + return; } S_StartSound(NULL, sfx_s3k5b); routine(-1); - return true; + M_SetMenuDelay(pid); + return; } - return false; + return; } - return false; + return; } void M_Ticker(void) { + INT32 i; + if (menutransition.tics != 0 || menutransition.dest != 0) { noFurtherInput = true; @@ -1408,17 +1493,17 @@ void M_Ticker(void) } } - if (menuInputDelay > 0) + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { - menuInputDelay--; + if (menucmd[i].delay > 0) + { + menucmd[i].delay--; + } } if (noFurtherInput == false) { - if (M_HandleMenuInput() == true) - { - menuInputDelay = MENUDELAYTIME; - } + M_HandleMenuInput(); } if (currentMenu->tickroutine) @@ -1869,39 +1954,41 @@ static void M_SetupReadyExplosions(setup_player_t *p) static void M_HandleCharacterGrid(setup_player_t *p, UINT8 num) { - if (G_PlayerInputDown(num, gc_down, true) == true) + if (menucmd[num].dpad_ud > 0) { p->gridy++; if (p->gridy > 8) p->gridy = 0; S_StartSound(NULL, sfx_s3k5b); - p->delay = MENUDELAYTIME; + M_SetMenuDelay(num); } - else if (G_PlayerInputDown(num, gc_up, true) == true) + else if (menucmd[num].dpad_ud < 0) { p->gridy--; if (p->gridy < 0) p->gridy = 8; S_StartSound(NULL, sfx_s3k5b); - p->delay = MENUDELAYTIME; + M_SetMenuDelay(num); } - else if (G_PlayerInputDown(num, gc_right, true) == true) + + if (menucmd[num].dpad_lr > 0) { p->gridx++; if (p->gridx > 8) p->gridx = 0; S_StartSound(NULL, sfx_s3k5b); - p->delay = MENUDELAYTIME; + M_SetMenuDelay(num); } - else if (G_PlayerInputDown(num, gc_left, true) == true) + else if (menucmd[num].dpad_lr < 0) { p->gridx--; if (p->gridx < 0) p->gridx = 8; S_StartSound(NULL, sfx_s3k5b); - p->delay = MENUDELAYTIME; + M_SetMenuDelay(num); } - else if (G_PlayerInputDown(num, gc_a, true) == true || G_PlayerInputDown(num, gc_start, true) == true) + + if ((menucmd[num].buttons & MBT_A) || (menucmd[num].buttons & MBT_X) /*|| (menucmd[num].buttons & MBT_START)*/) { if (setup_chargrid[p->gridx][p->gridy].numskins == 0) { @@ -1917,9 +2004,9 @@ static void M_HandleCharacterGrid(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_s3k63); } - p->delay = MENUDELAYTIME; + M_SetMenuDelay(num); } - else if (G_PlayerInputDown(num, gc_b, true) == true) + else if ((menucmd[num].buttons & MBT_B) || (menucmd[num].buttons & MBT_Y)) { if (num == setup_numplayers-1) { @@ -1931,7 +2018,7 @@ static void M_HandleCharacterGrid(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_s3kb2); } - p->delay = MENUDELAYTIME; + M_SetMenuDelay(num); } } @@ -1957,17 +2044,18 @@ static void M_HandleCharRotate(setup_player_t *p, UINT8 num) p->delay = CSROTATETICS; S_StartSound(NULL, sfx_s3kc3s); } - else if (G_PlayerInputDown(num, gc_a, true) == true || G_PlayerInputDown(num, gc_start, true) == true) + + if (G_PlayerInputDown(num, gc_a, true) == true || G_PlayerInputDown(num, gc_start, true) == true) { p->mdepth = CSSTEP_COLORS; S_StartSound(NULL, sfx_s3k63); - p->delay = MENUDELAYTIME; + M_SetMenuDelay(num); } else if (G_PlayerInputDown(num, gc_b, true) == true) { p->mdepth = CSSTEP_CHARS; S_StartSound(NULL, sfx_s3k5b); - p->delay = MENUDELAYTIME; + M_SetMenuDelay(num); } } @@ -1979,7 +2067,7 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num) if (p->color >= numskincolors) p->color = 1; p->rotate = CSROTATETICS; - p->delay = MENUDELAYTIME; //CSROTATETICS + M_SetMenuDelay(num); //CSROTATETICS S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s } else if (G_PlayerInputDown(num, gc_left, true) == true) @@ -1988,15 +2076,17 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num) if (p->color < 1) p->color = numskincolors-1; p->rotate = -CSROTATETICS; - p->delay = MENUDELAYTIME; //CSROTATETICS + M_SetMenuDelay(num); //CSROTATETICS S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s } - else if (G_PlayerInputDown(num, gc_a, true) == true || G_PlayerInputDown(num, gc_start, true) == true) + + if (G_PlayerInputDown(num, gc_a, true) == true || G_PlayerInputDown(num, gc_start, true) == true) { p->mdepth = CSSTEP_READY; p->delay = TICRATE; M_SetupReadyExplosions(p); S_StartSound(NULL, sfx_s3k4e); + M_SetMenuDelay(num); } else if (G_PlayerInputDown(num, gc_b, true) == true) { @@ -2005,7 +2095,7 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num) else p->mdepth = CSSTEP_ALTS; S_StartSound(NULL, sfx_s3k5b); - p->delay = MENUDELAYTIME; + M_SetMenuDelay(num); } } @@ -2022,7 +2112,7 @@ boolean M_CharacterSelectHandler(INT32 choice) if (i > 0) break; // temp - if (p->delay == 0) + if (p->delay == 0 && menucmd[i].delay == 0) { switch (p->mdepth) { @@ -2050,15 +2140,12 @@ boolean M_CharacterSelectHandler(INT32 choice) { p->mdepth = CSSTEP_COLORS; S_StartSound(NULL, sfx_s3k5b); - p->delay = MENUDELAYTIME; + M_SetMenuDelay(i); } break; } } - if (p->mdepth < CSSTEP_ALTS) - p->clonenum = 0; - // Just makes it easier to access later p->skin = setup_chargrid[p->gridx][p->gridy].skinlist[p->clonenum]; From d067c1ddeffab9fdb0191b54fbcea2f21eebfc82 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 6 Dec 2021 17:43:52 -0500 Subject: [PATCH 052/379] First pass on character select device select Ultra mega hacked in, by saving all "discarded" joysticks to an array so they don't get totally closed & we can still poll them. Events now properly send the device number instead of the player number, which means we can store all controllers pressing buttons, and thus, can detect when ANY controller is pressing anything, and THUS we can make the character select work like we wanted to :V Did not bother fixing any of the bugs, however. First of all, the opening menus do not properly fallback to default controls. Yet again, we may need a more robust system -- storing all keys from gamekeydown separately? Additionally it seems like when I input gamepad it makes me use keyboard anyway, so I think something fishy is up. --- src/d_clisrv.c | 3 ++ src/d_main.c | 1 + src/d_netcmd.c | 10 ++--- src/doomdef.h | 1 + src/g_game.c | 1 + src/g_input.c | 68 ++++++++++++++++++++-------- src/g_input.h | 3 ++ src/k_menudraw.c | 7 ++- src/k_menufunc.c | 108 +++++++++++++++++++++++++++++++++++++-------- src/sdl/i_system.c | 49 ++++++++++++++++++-- src/sdl/i_video.c | 56 ++++------------------- src/sdl/sdlmain.h | 3 ++ 12 files changed, 216 insertions(+), 94 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 7d8cb9ddd..cd404a685 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1922,6 +1922,8 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic if (*oldtic != I_GetTime()) { I_OsPolling(); + + memset(deviceResponding, false, sizeof (deviceResponding)); for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1)) G_MapEventsToControls(&events[eventtail]); @@ -1937,6 +1939,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic CL_Reset(); D_StartTitle(); memset(gamekeydown, 0, sizeof (gamekeydown)); + memset(deviceResponding, false, sizeof (deviceResponding)); return false; } diff --git a/src/d_main.c b/src/d_main.c index 54d62fbbe..9bb6c678d 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -183,6 +183,7 @@ void D_ProcessEvents(void) boolean eaten; + memset(deviceResponding, false, sizeof (deviceResponding)); for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1)) { ev = &events[eventtail]; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 80493d7f3..0f91028ff 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -231,7 +231,7 @@ static CV_PossibleValue_t joyport_cons_t[] = {{1, "/dev/js0"}, {2, "/dev/js1"}, {4, "/dev/js3"}, {0, NULL}}; #else // accept whatever value - it is in fact the joystick device number -#define usejoystick_cons_t NULL +static CV_PossibleValue_t usejoystick_cons_t[] = {{-1, "MIN"}, {MAXGAMEPADS, "MAX"}, {0, NULL}}; #endif static CV_PossibleValue_t teamscramble_cons_t[] = {{0, "Off"}, {1, "Random"}, {2, "Points"}, {0, NULL}}; @@ -308,10 +308,10 @@ INT32 cv_debug; consvar_t cv_usemouse = CVAR_INIT ("use_mouse", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse); consvar_t cv_usejoystick[MAXSPLITSCREENPLAYERS] = { - CVAR_INIT ("use_gamepad", "1", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick1), - CVAR_INIT ("use_gamepad2", "2", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick2), - CVAR_INIT ("use_joystick3", "3", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick3), - CVAR_INIT ("use_joystick4", "4", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick4) + CVAR_INIT ("use_device", "1", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick1), + CVAR_INIT ("use_device2", "2", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick2), + CVAR_INIT ("use_device3", "3", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick3), + CVAR_INIT ("use_device4", "4", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick4) }; #if (defined (LJOYSTICK) || defined (HAVE_SDL)) diff --git a/src/doomdef.h b/src/doomdef.h index ff6d2c1e5..c5ef7839f 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -204,6 +204,7 @@ extern char logfilename[1024]; #define PLAYERSMASK (MAXPLAYERS-1) #define MAXPLAYERNAME 21 #define MAXSPLITSCREENPLAYERS 4 // Max number of players on a single computer +#define MAXGAMEPADS (MAXSPLITSCREENPLAYERS * 2) // Number of gamepads we'll be allowing #define MAXSKINS 128 diff --git a/src/g_game.c b/src/g_game.c index 13751b2d3..4e3030fb4 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1146,6 +1146,7 @@ void G_DoLoadLevel(boolean resetplayer) // clear cmd building stuff memset(gamekeydown, 0, sizeof (gamekeydown)); + memset(deviceResponding, false, sizeof (deviceResponding)); // clear hud messages remains (usually from game startup) CON_ClearHUD(); diff --git a/src/g_input.c b/src/g_input.c index 9ecb24e38..5b4fe8675 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -34,6 +34,7 @@ consvar_t cv_controlperkey = CVAR_INIT ("controlperkey", "One", CV_SAVE, onecont // current state of the keys // FRACUNIT for fully pressed, 0 for not pressed INT32 gamekeydown[MAXSPLITSCREENPLAYERS][NUMINPUTS]; +boolean deviceResponding[MAXDEVICES]; // two key codes (or virtual key) per game control INT32 gamecontrol[MAXSPLITSCREENPLAYERS][num_gamecontrols][MAXINPUTMAPPING]; @@ -76,13 +77,44 @@ const INT32 gcl_full[num_gcl_full] = { void G_MapEventsToControls(event_t *ev) { INT32 i; + INT32 devicePlayer = INT32_MAX; + + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + if (ev->device == cv_usejoystick[i].value) + { + devicePlayer = i; + break; + } + } + + if (ev->device >= 0 && ev->device <= MAXGAMEPADS) + { + switch (ev->type) + { + case ev_keydown: + //case ev_keyup: + //case ev_mouse: + //case ev_joystick: + deviceResponding[ev->device] = true; + break; + + default: + break; + } + } + + if (devicePlayer == INT32_MAX) + { + return; + } switch (ev->type) { case ev_keydown: if (ev->data1 < NUMINPUTS) { - gamekeydown[ev->device][ev->data1] = FRACUNIT; + gamekeydown[devicePlayer][ev->data1] = FRACUNIT; } #ifdef PARANOIA else @@ -95,7 +127,7 @@ void G_MapEventsToControls(event_t *ev) case ev_keyup: if (ev->data1 < NUMINPUTS) { - gamekeydown[ev->device][ev->data1] = 0; + gamekeydown[devicePlayer][ev->data1] = 0; } #ifdef PARANOIA else @@ -115,28 +147,28 @@ void G_MapEventsToControls(event_t *ev) if (ev->data2 < 0) { // Left - gamekeydown[ev->device][KEY_MOUSEMOVE + 2] = abs(ev->data2); - gamekeydown[ev->device][KEY_MOUSEMOVE + 3] = 0; + gamekeydown[devicePlayer][KEY_MOUSEMOVE + 2] = abs(ev->data2); + gamekeydown[devicePlayer][KEY_MOUSEMOVE + 3] = 0; } else { // Right - gamekeydown[ev->device][KEY_MOUSEMOVE + 2] = 0; - gamekeydown[ev->device][KEY_MOUSEMOVE + 3] = abs(ev->data2); + gamekeydown[devicePlayer][KEY_MOUSEMOVE + 2] = 0; + gamekeydown[devicePlayer][KEY_MOUSEMOVE + 3] = abs(ev->data2); } // Y axis if (ev->data3 < 0) { // Up - gamekeydown[ev->device][KEY_MOUSEMOVE] = abs(ev->data3); - gamekeydown[ev->device][KEY_MOUSEMOVE + 1] = 0; + gamekeydown[devicePlayer][KEY_MOUSEMOVE] = abs(ev->data3); + gamekeydown[devicePlayer][KEY_MOUSEMOVE + 1] = 0; } else { // Down - gamekeydown[ev->device][KEY_MOUSEMOVE] = 0; - gamekeydown[ev->device][KEY_MOUSEMOVE + 1] = abs(ev->data3); + gamekeydown[devicePlayer][KEY_MOUSEMOVE] = 0; + gamekeydown[devicePlayer][KEY_MOUSEMOVE + 1] = abs(ev->data3); } break; @@ -168,14 +200,14 @@ void G_MapEventsToControls(event_t *ev) if (ev->data2 < 0) { // Left - gamekeydown[ev->device][KEY_AXIS1 + i] = abs(ev->data2); - gamekeydown[ev->device][KEY_AXIS1 + i + 1] = 0; + gamekeydown[devicePlayer][KEY_AXIS1 + i] = abs(ev->data2); + gamekeydown[devicePlayer][KEY_AXIS1 + i + 1] = 0; } else { // Right - gamekeydown[ev->device][KEY_AXIS1 + i] = 0; - gamekeydown[ev->device][KEY_AXIS1 + i + 1] = abs(ev->data2); + gamekeydown[devicePlayer][KEY_AXIS1 + i] = 0; + gamekeydown[devicePlayer][KEY_AXIS1 + i + 1] = abs(ev->data2); } } @@ -185,14 +217,14 @@ void G_MapEventsToControls(event_t *ev) if (ev->data3 < 0) { // Up - gamekeydown[ev->device][KEY_AXIS1 + i + 2] = abs(ev->data3); - gamekeydown[ev->device][KEY_AXIS1 + i + 3] = 0; + gamekeydown[devicePlayer][KEY_AXIS1 + i + 2] = abs(ev->data3); + gamekeydown[devicePlayer][KEY_AXIS1 + i + 3] = 0; } else { // Down - gamekeydown[ev->device][KEY_AXIS1 + i + 2] = 0; - gamekeydown[ev->device][KEY_AXIS1 + i + 3] = abs(ev->data3); + gamekeydown[devicePlayer][KEY_AXIS1 + i + 2] = 0; + gamekeydown[devicePlayer][KEY_AXIS1 + i + 3] = abs(ev->data3); } } diff --git a/src/g_input.h b/src/g_input.h index 3542d9ede..8a41267c5 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -86,6 +86,9 @@ extern consvar_t cv_controlperkey; // Or anything inbetween for analog values extern INT32 gamekeydown[MAXSPLITSCREENPLAYERS][NUMINPUTS]; +#define MAXDEVICES (MAXGAMEPADS + 1) // Gamepads + keyboard & mouse +extern boolean deviceResponding[MAXDEVICES]; + // several key codes (or virtual key) per game control extern INT32 gamecontrol[MAXSPLITSCREENPLAYERS][num_gamecontrols][MAXINPUTMAPPING]; extern INT32 gamecontroldefault[num_gamecontrols][MAXINPUTMAPPING]; // default control storage diff --git a/src/k_menudraw.c b/src/k_menudraw.c index f529585d2..2f4a29d15 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -934,10 +934,15 @@ static void M_DrawCharSelectCursor(UINT8 num) void M_DrawCharacterSelect(void) { UINT8 i, j, k; - UINT8 priority = setup_animcounter % setup_numplayers; + UINT8 priority = 0; INT16 quadx, quady; SINT8 skin; + if (setup_numplayers > 0) + { + priority = setup_animcounter % setup_numplayers; + } + // We have to loop twice -- first time to draw the drop shadows, a second time to draw the icons. for (i = 0; i < 9; i++) { diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 360d2aadb..824c0050c 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1871,8 +1871,8 @@ void M_CharacterSelectInit(INT32 choice) } memset(setup_player, 0, sizeof(setup_player)); - setup_player[0].mdepth = CSSTEP_CHARS; - setup_numplayers = 1; + //setup_player[0].mdepth = CSSTEP_CHARS; + setup_numplayers = 0; memset(setup_explosions, 0, sizeof(setup_explosions)); setup_animcounter = 0; @@ -1952,6 +1952,91 @@ static void M_SetupReadyExplosions(setup_player_t *p) } } +static boolean M_DeviceAvailable(UINT8 deviceID, UINT8 numPlayers) +{ + INT32 i; + + if (numPlayers == 0) + { + // All of them are available! + return true; + } + + for (i = 0; i < numPlayers; i++) + { + if (cv_usejoystick[i].value == deviceID) + { + // This one's already being used. + return false; + } + } + + // This device is good to go. + return true; +} + +static void M_HandlePressStart(setup_player_t *p, UINT8 num) +{ + INT32 i, j; + + // Detect B press first ... this means P1 can actually exit out of the menu. + if (menucmd[num].buttons & MBT_B) + { + M_SetMenuDelay(num); + + if (num == 0) + { + // We're done here. + M_GoBack(0); + return; + } + + // Don't allow this press to ever count as "start". + return; + } + + if (num != setup_numplayers) + { + // Only detect devices for the last player... + // just too complicated otherwise. + return; + } + + // Now detect new devices trying to join. + for (i = 0; i < MAXDEVICES; i++) + { + if (deviceResponding[i] == false) + { + // No buttons are being pushed. + continue; + } + + if (M_DeviceAvailable(i, setup_numplayers) == true) + { + // Available!! Let's use this one!! + cv_usejoystick[setup_numplayers].value = i; + + for (j = setup_numplayers+1; j < MAXSPLITSCREENPLAYERS; j++) + { + if (cv_usejoystick[j].value == i) + { + // Un-set devices for other players. + cv_usejoystick[j].value = -1; + } + } + + setup_numplayers++; + p->mdepth = CSSTEP_CHARS; + S_StartSound(NULL, sfx_s3k65); + + // Prevent excess presses + memset(deviceResponding, false, sizeof (deviceResponding)); + + M_SetMenuDelay(num); + } + } +} + static void M_HandleCharacterGrid(setup_player_t *p, UINT8 num) { if (menucmd[num].dpad_ud > 0) @@ -2117,13 +2202,7 @@ boolean M_CharacterSelectHandler(INT32 choice) switch (p->mdepth) { case CSSTEP_NONE: // Enter Game - if (i == setup_numplayers) - { - //I_DetectNewControllers(); // Look through all joysticks to see if any have pressed start. - - p->mdepth = CSSTEP_CHARS; - S_StartSound(NULL, sfx_s3k65); - } + M_HandlePressStart(p, i); break; case CSSTEP_CHARS: // Character Select grid M_HandleCharacterGrid(p, i); @@ -2162,15 +2241,6 @@ boolean M_CharacterSelectHandler(INT32 choice) setup_numplayers = i+1; } - // If the first player unjoins, then we get outta here - if (setup_player[0].mdepth == CSSTEP_NONE) - { - if (currentMenu->prevMenu) - M_SetupNextMenu(currentMenu->prevMenu, false); - else - M_ClearMenus(true); - } - return true; } @@ -2249,7 +2319,7 @@ void M_CharacterSelectTick(void) setup_explosions[i].tics--; } - if (setupnext) + if (setupnext && setup_numplayers > 0) { // Selecting from the menu diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index a856c6f84..8c80b9d24 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -197,6 +197,48 @@ static char returnWadPath[256]; #include "../byteptr.h" #endif +void I_StoreExJoystick(SDL_Joystick *dev) +{ + // ExJoystick is a massive hack to avoid needing to completely + // rewrite pretty much all of the controller support from scratch... + + // Used in favor of most instances of SDL_JoystickClose. + // If a joystick would've been discarded, then save it in an array, + // because we want it have it for the joystick input screen. + + int index = 0; + + if (dev == NULL) + { + // No joystick? + return; + } + + index = SDL_JoystickInstanceID(dev); + + if (index >= MAXGAMEPADS || index < 0) + { + // Not enough space to save this joystick, completely discard. + SDL_JoystickClose(dev); + return; + } + + if (ExJoystick[index] == dev) + { + // No need to do anything else. + return; + } + + if (ExJoystick[index] != NULL) + { + // Discard joystick in the old slot. + SDL_JoystickClose(ExJoystick[index]); + } + + // Keep for safe-keeping. + ExJoystick[index] = dev; +} + /** \brief The JoyReset function \param JoySet Joystick info to reset @@ -207,7 +249,7 @@ static void JoyReset(SDLJoyInfo_t *JoySet) { if (JoySet->dev) { - SDL_JoystickClose(JoySet->dev); + I_StoreExJoystick(JoySet->dev); } JoySet->dev = NULL; JoySet->oldjoy = -1; @@ -222,6 +264,7 @@ static INT32 joystick_started[MAXSPLITSCREENPLAYERS] = {0,0,0,0}; /** \brief SDL info about joystick 1 */ SDLJoyInfo_t JoyInfo[MAXSPLITSCREENPLAYERS]; +SDL_Joystick *ExJoystick[MAXGAMEPADS]; SDL_bool consolevent = SDL_FALSE; SDL_bool framebuffer = SDL_FALSE; @@ -963,7 +1006,7 @@ INT32 I_GetJoystickDeviceIndex(SDL_Joystick *dev) } if (j == MAXSPLITSCREENPLAYERS) - SDL_JoystickClose(test); + I_StoreExJoystick(test); } } @@ -1373,7 +1416,7 @@ void I_InitJoystick(UINT8 index) if (i == MAXSPLITSCREENPLAYERS) { // Joystick didn't end up being used - SDL_JoystickClose(newjoy); + I_StoreExJoystick(newjoy); } } diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index ad5b3723d..fec2bc306 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -619,6 +619,7 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt) SDLforceUngrabMouse(); } memset(gamekeydown, 0, sizeof(gamekeydown)); // TODO this is a scary memset + memset(deviceResponding, false, sizeof (deviceResponding)); if (MOUSE_MENU) { @@ -632,7 +633,7 @@ static void Impl_HandleKeyboardEvent(SDL_KeyboardEvent evt, Uint32 type) { event_t event; - event.device = 0; // TODO: properly set a device + event.device = 0; if (type == SDL_KEYUP) { @@ -715,7 +716,7 @@ static void Impl_HandleMouseButtonEvent(SDL_MouseButtonEvent evt, Uint32 type) /// \todo inputEvent.button.which if (USE_MOUSEINPUT) { - event.device = 0; // TODO: properly set a device + event.device = 0; if (type == SDL_MOUSEBUTTONUP) { @@ -749,7 +750,7 @@ static void Impl_HandleMouseWheelEvent(SDL_MouseWheelEvent evt) SDL_memset(&event, 0, sizeof(event_t)); - event.device = 0; // TODO: properly set a device + event.device = 0; if (evt.y > 0) { @@ -775,23 +776,9 @@ static void Impl_HandleMouseWheelEvent(SDL_MouseWheelEvent evt) static void Impl_HandleJoystickAxisEvent(SDL_JoyAxisEvent evt) { event_t event; - SDL_JoystickID joyid[MAXSPLITSCREENPLAYERS]; - UINT8 i; - event.device = INT32_MAX; event.data1 = event.data2 = event.data3 = INT32_MAX; - - // Determine the Joystick IDs for each current open joystick - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) - { - joyid[i] = SDL_JoystickInstanceID(JoyInfo[i].dev); - - if (evt.which == joyid[i]) - { - event.device = i; - } - } - + event.device = 1 + evt.which; evt.axis++; if (event.device == INT32_MAX) @@ -826,20 +813,8 @@ static void Impl_HandleJoystickHatEvent(SDL_JoyHatEvent evt) { event_t event; SDL_JoystickID joyid[MAXSPLITSCREENPLAYERS]; - UINT8 i; - event.device = INT32_MAX; - - // Determine the Joystick IDs for each current open joystick - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) - { - joyid[i] = SDL_JoystickInstanceID(JoyInfo[i].dev); - - if (evt.which == joyid[i]) - { - event.device = i; - } - } + event.device = 1 + evt.which; if (event.device == INT32_MAX) { @@ -860,21 +835,8 @@ static void Impl_HandleJoystickHatEvent(SDL_JoyHatEvent evt) static void Impl_HandleJoystickButtonEvent(SDL_JoyButtonEvent evt, Uint32 type) { event_t event; - SDL_JoystickID joyid[MAXSPLITSCREENPLAYERS]; - UINT8 i; - event.device = INT32_MAX; - - // Determine the Joystick IDs for each current open joystick - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) - { - joyid[i] = SDL_JoystickInstanceID(JoyInfo[i].dev); - - if (evt.which == joyid[i]) - { - event.device = i; - } - } + event.device = 1 + evt.which; if (event.device == INT32_MAX) { @@ -913,8 +875,6 @@ static void Impl_HandleJoystickButtonEvent(SDL_JoyButtonEvent evt, Uint32 type) } } - - void I_GetEvent(void) { SDL_Event evt; @@ -1054,7 +1014,7 @@ void I_GetEvent(void) } if (i == MAXSPLITSCREENPLAYERS) - SDL_JoystickClose(newjoy); + I_StoreExJoystick(newjoy); } break; diff --git a/src/sdl/sdlmain.h b/src/sdl/sdlmain.h index 39c099a2b..391614bcd 100644 --- a/src/sdl/sdlmain.h +++ b/src/sdl/sdlmain.h @@ -61,6 +61,9 @@ typedef struct SDLJoyInfo_s /** \brief SDL info about joysticks */ extern SDLJoyInfo_t JoyInfo[MAXSPLITSCREENPLAYERS]; +extern SDL_Joystick *ExJoystick[MAXGAMEPADS]; + +void I_StoreExJoystick(SDL_Joystick *dev); /** \brief joystick axis deadzone */ From 2e79d84f4a8f02bfd4c8cd0491c41ca75a6aa427 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 10 Dec 2021 16:07:36 +0100 Subject: [PATCH 053/379] Extras menu barebones start, it does nothing --- src/k_menu.h | 29 +++++++++++++++ src/k_menudef.c | 38 ++++++++++++++++++- src/k_menudraw.c | 54 +++++++++++++++++++++++++++ src/k_menufunc.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 216 insertions(+), 2 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index f5f4f20e7..3d0cc3272 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -268,6 +268,10 @@ extern menu_t OPTIONS_DataDiscordDef; extern menuitem_t OPTIONS_DataErase[]; extern menu_t OPTIONS_DataEraseDef; +// EXTRAS +extern menuitem_t EXTRAS_Main[]; +extern menu_t EXTRAS_MainDef; + // PAUSE extern menuitem_t PAUSE_Main[]; extern menu_t PAUSE_MainDef; @@ -565,6 +569,27 @@ void M_EraseData(INT32 choice); // For data erasing void M_VideoModeMenu(INT32 choice); void M_HandleVideoModes(INT32 ch); + +// Extras menu: +extern struct extrasmenu_s { + + tic_t ticker; // How long the menu's been open for + INT16 offset; // To make the icons move smoothly when we transition! + + // For moving the button when we get into a submenu. it's smooth and cool! (normal x/y and target x/y.) + // this is only used during menu transitions. (and will probably remain unused until we get the statistics menu + INT16 extx; + INT16 exty; + INT16 textx; + INT16 texty; + +} extrasmenu; + +void M_InitExtras(INT32 choice); // init for the struct +void M_ExtrasTick(void); +boolean M_ExtrasInputs(INT32 ch); +boolean M_ExtrasQuit(void); // resets buttons when you quit + // Pause menu: // Keep track of some pause menu data for visual goodness. @@ -660,6 +685,10 @@ void M_DrawGenericOptions(void); void M_DrawVideoModes(void); void M_DrawItemToggles(void); +// Extras menu: +void M_DrawExtrasMovingButton(void); +void M_DrawExtras(void); + // Misc menus: #define LOCATIONSTRING1 "Visit \x83SRB2.ORG/MODS\x80 to get & make addons!" #define LOCATIONSTRING2 "Visit \x88SRB2.ORG/MODS\x80 to get & make addons!" diff --git a/src/k_menudef.c b/src/k_menudef.c index 9754ae366..6260e9ea5 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -31,9 +31,9 @@ menuitem_t MainMenu[] = "Cut to the chase and start the race!", NULL, M_CharacterSelectInit, 0, 0}, - {IT_STRING, "Extra", + {IT_STRING | IT_CALL, "Extras", "Check out some bonus features.", "MENUI001", - NULL, 0, 0}, + M_InitExtras, 0, 0}, {IT_STRING, "Options", "Configure your controls, settings, and preferences.", NULL, @@ -1103,6 +1103,40 @@ menu_t OPTIONS_DataEraseDef = { NULL, }; + + +// extras menu +menuitem_t EXTRAS_Main[] = +{ + + {IT_STRING | IT_CALL, "Addons", "Add files to customize your experience.", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CALL, "Replay Hut", "Play the replays you've saved throughout your many races & battles!", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CALL, "Statistics", "Look back on some of your greatest achievements such as your playtime and wins!", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_TRANSTEXT, "Extras Checklist", "View the requirement for some of the secret content you can unlock!", + NULL, NULL, 0, 0}, +}; + +// the extras menu essentially reuses the options menu stuff +menu_t EXTRAS_MainDef = { + sizeof (EXTRAS_Main) / sizeof (menuitem_t), + &MainDef, + 0, + EXTRAS_Main, + 0, 0, + 0, 0, + 2, 10, + M_DrawExtras, + M_ExtrasTick, + NULL, + M_ExtrasInputs +}; + // ------------------- // In-game/pause menus // ------------------- diff --git a/src/k_menudraw.c b/src/k_menudraw.c index f529585d2..1ddbd40d4 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2108,6 +2108,60 @@ void M_DrawItemToggles(void) } +// EXTRAS: +// Copypasted from options but separate either way in case we want it to look more unique later down the line. +void M_DrawExtrasMovingButton(void) +{ + patch_t *butt = W_CachePatchName("OPT_BUTT", PU_CACHE); + UINT8 *c = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE); + + V_DrawFixedPatch((extrasmenu.extx)*FRACUNIT, (extrasmenu.exty)*FRACUNIT, FRACUNIT, 0, butt, c); + V_DrawCenteredGamemodeString((extrasmenu.extx)-3, (extrasmenu.exty) - 16, V_ALLOWLOWERCASE, c, EXTRAS_MainDef.menuitems[EXTRAS_MainDef.lastOn].text); +} + +void M_DrawExtras(void) +{ + UINT8 i; + INT32 x = 140 - (48*itemOn) + extrasmenu.offset; + INT32 y = 70 + extrasmenu.offset; + patch_t *buttback = W_CachePatchName("OPT_BUTT", PU_CACHE); + patch_t *bg = W_CachePatchName("M_XTRABG", PU_CACHE); + + UINT8 *c = NULL; + + V_DrawFixedPatch(0, 0, FRACUNIT, 0, bg, NULL); + + for (i=0; i < currentMenu->numitems; i++) + { + INT32 py = y - (itemOn*48); + INT32 px = x - menutransition.tics*64; + INT32 tflag = 0; + + if (i == itemOn) + c = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE); + else + c = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_BLACK, GTC_CACHE); + + if (currentMenu->menuitems[i].status & IT_TRANSTEXT) + tflag = V_TRANSLUCENT; + + if (!(menutransition.tics && i == itemOn)) + { + V_DrawFixedPatch(px*FRACUNIT, py*FRACUNIT, FRACUNIT, 0, buttback, c); + V_DrawCenteredGamemodeString(px-3, py - 16, V_ALLOWLOWERCASE|tflag, (i == itemOn ? c : NULL), currentMenu->menuitems[i].text); + } + + y += 48; + x += 48; + } + + M_DrawMenuTooltips(); + + if (menutransition.tics) + M_DrawExtrasMovingButton(); + +} + // // INGAME / PAUSE MENUS // diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 902a38d33..cd5b09a94 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3378,6 +3378,103 @@ void M_HandleItemToggles(INT32 choice) } +// Extras menu; +// this is copypasted from the options menu but all of these are different functions in case we ever want it to look more unique + +struct extrasmenu_s extrasmenu; + +void M_InitExtras(INT32 choice) +{ + (void)choice; + + extrasmenu.ticker = 0; + extrasmenu.offset = 0; + + extrasmenu.extx = 0; + extrasmenu.exty = 0; + extrasmenu.textx = 0; + extrasmenu.texty = 0; + + M_SetupNextMenu(&EXTRAS_MainDef, false); +} + +// For statistics, will maybe remain unused for a while +boolean M_ExtrasQuit(void) +{ + extrasmenu.textx = 140-1; + extrasmenu.texty = 70+1; + + return true; // Always allow quitting, duh. +} + +void M_ExtrasTick(void) +{ + extrasmenu.offset /= 2; + extrasmenu.ticker++; + + extrasmenu.extx += (extrasmenu.textx - extrasmenu.extx)/2; + extrasmenu.exty += (extrasmenu.texty - extrasmenu.exty)/2; + + if (abs(extrasmenu.extx - extrasmenu.exty) < 2) + { + extrasmenu.extx = extrasmenu.textx; + extrasmenu.exty = extrasmenu.texty; // Avoid awkward 1 px errors. + } + + // Move the button for cool animations + if (currentMenu == &EXTRAS_MainDef) + { + M_ExtrasQuit(); // reset the options button. + } + else + { + extrasmenu.textx = 160; + extrasmenu.texty = 50; + } +} + +boolean M_ExtrasInputs(INT32 ch) +{ + + switch (ch) + { + case KEY_DOWNARROW: + { + extrasmenu.offset += 48; + M_NextOpt(); + S_StartSound(NULL, sfx_menu1); + + if (itemOn == 0) + extrasmenu.offset -= currentMenu->numitems*48; + + return true; + } + case KEY_UPARROW: + { + extrasmenu.offset -= 48; + M_PrevOpt(); + S_StartSound(NULL, sfx_menu1); + + if (itemOn == currentMenu->numitems-1) + extrasmenu.offset += currentMenu->numitems*48; + + return true; + } + case KEY_ENTER: + { + + if (currentMenu->menuitems[itemOn].status & IT_TRANSTEXT) + return true; // No. + + extrasmenu.extx = 140; + extrasmenu.exty = 70; // Default position for the currently selected option. + + return false; // Don't eat. + } + } + return false; +} + // ===================== // PAUSE / IN-GAME MENUS // ===================== From 75df86da2f83037bf23581b15b584f0beebd2c3e Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 10 Dec 2021 16:15:54 +0100 Subject: [PATCH 054/379] extras menu: addons --- src/k_menudef.c | 2 +- src/k_menudraw.c | 8 ++++++++ src/k_menufunc.c | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/k_menudef.c b/src/k_menudef.c index 6260e9ea5..594d4475d 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -1110,7 +1110,7 @@ menuitem_t EXTRAS_Main[] = { {IT_STRING | IT_CALL, "Addons", "Add files to customize your experience.", - NULL, NULL, 0, 0}, + NULL, M_Addons, 0, 0}, {IT_STRING | IT_CALL, "Replay Hut", "Play the replays you've saved throughout your many races & battles!", NULL, NULL, 0, 0}, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 1ddbd40d4..a865fcb97 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2543,6 +2543,14 @@ void M_DrawAddons(void) M_CacheAddonPatches(); + // hack: If we're calling this from GS_MENU, that means we're in the extras menu! + // so draw the apropriate background + if (gamestate == GS_MENU) + { + patch_t *bg = W_CachePatchName("M_XTRABG", PU_CACHE); + V_DrawFixedPatch(0, 0, FRACUNIT, 0, bg, NULL); + } + // hack - need to refresh at end of frame to handle addfile... if (refreshdirmenu & M_AddonsRefresh()) { diff --git a/src/k_menufunc.c b/src/k_menufunc.c index cd5b09a94..15cbf7316 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3839,7 +3839,7 @@ void M_Addons(INT32 choice) dir_on[menudepthleft] = 0; MISC_AddonsDef.prevMenu = currentMenu; - M_SetupNextMenu(&MISC_AddonsDef, false); + M_SetupNextMenu(&MISC_AddonsDef, true); // No transitions. } From 9de737682dca3b7f29a5f53e03a72ba7e03a97d9 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 10 Dec 2021 20:30:09 +0100 Subject: [PATCH 055/379] extras: replay hut --- src/k_menu.h | 29 ++++ src/k_menudef.c | 58 +++++++- src/k_menudraw.c | 381 ++++++++++++++++++++++++++++++++++++++++++++++- src/k_menufunc.c | 211 ++++++++++++++++++++++++++ 4 files changed, 677 insertions(+), 2 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 3d0cc3272..7eca2ca0d 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -18,6 +18,7 @@ #include "d_event.h" #include "command.h" #include "doomstat.h" // MAXSPLITSCREENPLAYERS +#include "g_demo.h" //menudemo_t // flags for items in the menu // menu handle (what we do when key is pressed @@ -272,6 +273,12 @@ extern menu_t OPTIONS_DataEraseDef; extern menuitem_t EXTRAS_Main[]; extern menu_t EXTRAS_MainDef; +extern menuitem_t EXTRAS_ReplayHut[]; +extern menu_t EXTRAS_ReplayHutDef; + +extern menuitem_t EXTRAS_ReplayStart[]; +extern menu_t EXTRAS_ReplayStartDef; + // PAUSE extern menuitem_t PAUSE_Main[]; extern menu_t PAUSE_MainDef; @@ -571,6 +578,8 @@ void M_HandleVideoModes(INT32 ch); // Extras menu: +#define DF_ENCORE 0x40 + extern struct extrasmenu_s { tic_t ticker; // How long the menu's been open for @@ -583,6 +592,16 @@ extern struct extrasmenu_s { INT16 textx; INT16 texty; + + // The replay vars...... oh no...... + menudemo_t *demolist; + + INT16 replayScrollTitle; + SINT8 replayScrollDelay; + SINT8 replayScrollDir; + + + } extrasmenu; void M_InitExtras(INT32 choice); // init for the struct @@ -590,6 +609,13 @@ void M_ExtrasTick(void); boolean M_ExtrasInputs(INT32 ch); boolean M_ExtrasQuit(void); // resets buttons when you quit +// Extras: Replay Hut +void M_HandleReplayHutList(INT32 choice); +boolean M_QuitReplayHut(void); +void M_HutStartReplay(INT32 choice); +void M_PrepReplayList(void); + + // Pause menu: // Keep track of some pause menu data for visual goodness. @@ -688,6 +714,9 @@ void M_DrawItemToggles(void); // Extras menu: void M_DrawExtrasMovingButton(void); void M_DrawExtras(void); +void M_DrawReplayHut(void); +void M_DrawReplayStartMenu(void); +void M_DrawReplayHutReplayInfo(void); // Misc menus: #define LOCATIONSTRING1 "Visit \x83SRB2.ORG/MODS\x80 to get & make addons!" diff --git a/src/k_menudef.c b/src/k_menudef.c index 594d4475d..6a9487828 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -1113,7 +1113,7 @@ menuitem_t EXTRAS_Main[] = NULL, M_Addons, 0, 0}, {IT_STRING | IT_CALL, "Replay Hut", "Play the replays you've saved throughout your many races & battles!", - NULL, NULL, 0, 0}, + NULL, M_ReplayHut, 0, 0}, {IT_STRING | IT_CALL, "Statistics", "Look back on some of your greatest achievements such as your playtime and wins!", NULL, NULL, 0, 0}, @@ -1137,6 +1137,62 @@ menu_t EXTRAS_MainDef = { M_ExtrasInputs }; +// extras menu: replay hut +menuitem_t EXTRAS_ReplayHut[] = +{ + {IT_KEYHANDLER|IT_NOTHING, "", "", // Dummy menuitem for the replay list + NULL, M_HandleReplayHutList, 0, 0}, + + {IT_NOTHING, "", "", // Dummy for handling wrapping to the top of the menu.. + NULL, NULL, 0, 0}, +}; + +menu_t EXTRAS_ReplayHutDef = +{ + sizeof (EXTRAS_ReplayHut)/sizeof (menuitem_t), + &EXTRAS_MainDef, + 0, + EXTRAS_ReplayHut, + 30, 80, + 0, 0, + 0, 0, + M_DrawReplayHut, + NULL, + M_QuitReplayHut, + NULL +}; + +menuitem_t EXTRAS_ReplayStart[] = +{ + {IT_CALL |IT_STRING, "Load Addons and Watch", NULL, + NULL, M_HutStartReplay, 0, 0}, + + {IT_CALL |IT_STRING, "Load Without Addons", NULL, + NULL, M_HutStartReplay, 10, 0}, + + {IT_CALL |IT_STRING, "Watch Replay", NULL, + NULL, M_HutStartReplay, 10, 0}, + + {IT_SUBMENU |IT_STRING, "Go Back", NULL, + NULL, &EXTRAS_ReplayHutDef, 30, 0}, +}; + + +menu_t EXTRAS_ReplayStartDef = +{ + sizeof (EXTRAS_ReplayStart)/sizeof (menuitem_t), + &EXTRAS_ReplayHutDef, + 0, + EXTRAS_ReplayStart, + 27, 80, + 0, 0, + 0, 0, + M_DrawReplayStartMenu, + NULL, + NULL, + NULL +}; + // ------------------- // In-game/pause menus // ------------------- diff --git a/src/k_menudraw.c b/src/k_menudraw.c index a865fcb97..c9fc729c3 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -342,7 +342,7 @@ static const char *M_CreateSecretMenuOption(const char *str) // void M_DrawGenericMenu(void) { - INT32 x = 0, y = 0, w, i, cursory = 0; + INT32 x = currentMenu->x, y = currentMenu->y, w, i, cursory = 0; M_DrawMenuTooltips(); @@ -2456,6 +2456,385 @@ void M_DrawPlaybackMenu(void) } +// replay hut... +// ...dear lord this is messy, but Ima be real I ain't fixing this. + +#define SCALEDVIEWWIDTH (vid.width/vid.dupx) +#define SCALEDVIEWHEIGHT (vid.height/vid.dupy) +void M_DrawReplayHutReplayInfo(void) +{ + lumpnum_t lumpnum; + patch_t *patch; + UINT8 *colormap; + INT32 x, y, w, h; + + switch (extrasmenu.demolist[dir_on[menudepthleft]].type) + { + case MD_NOTLOADED: + V_DrawCenteredString(160, 40, V_SNAPTOTOP, "Loading replay information..."); + break; + + case MD_INVALID: + V_DrawCenteredString(160, 40, V_SNAPTOTOP|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, 64, V_SNAPTOTOP|V_ALLOWLOWERCASE|V_TRANSLUCENT|highlightflags, "Recorded on an outdated version."); + /* FALLTHRU */ + default: + // Draw level stuff + x = 15; y = 15; + + // A 160x100 image of the level as entry MAPxxP + //CONS_Printf("%d %s\n", extrasmenu.demolist[dir_on[menudepthleft]].map, G_BuildMapName(extrasmenu.demolist[dir_on[menudepthleft]].map)); + lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(extrasmenu.demolist[dir_on[menudepthleft]].map))); + if (lumpnum != LUMPERROR) + patch = W_CachePatchNum(lumpnum, PU_CACHE); + else + patch = W_CachePatchName("M_NOLVL", PU_CACHE); + + if (!(extrasmenu.demolist[dir_on[menudepthleft]].kartspeed & DF_ENCORE)) + V_DrawSmallScaledPatch(x, y, V_SNAPTOTOP, patch); + else + { + w = SHORT(patch->width); + h = SHORT(patch->height); + V_DrawSmallScaledPatch(x+(w>>1), y, V_SNAPTOTOP|V_FLIP, patch); + + { + static angle_t rubyfloattime = 0; + const fixed_t rubyheight = FINESINE(rubyfloattime>>ANGLETOFINESHIFT); + V_DrawFixedPatch((x+(w>>2))<>2))<width), y+20, V_SNAPTOTOP, patch, colormap); + + break; + } +} + +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; + + M_DrawEggaChannel(); + + // Draw menu choices + x = currentMenu->x; + y = currentMenu->y; + + if (itemOn > replaylistitem) + { + itemOn = replaylistitem; + dir_on[menudepthleft] = sizedirmenu-1; + extrasmenu.replayScrollTitle = 0; extrasmenu.replayScrollDelay = TICRATE; extrasmenu.replayScrollDir = 1; + } + else if (itemOn < replaylistitem) + { + dir_on[menudepthleft] = 0; + extrasmenu.replayScrollTitle = 0; extrasmenu.replayScrollDelay = TICRATE; extrasmenu.replayScrollDir = 1; + } + + if (itemOn == replaylistitem) + { + INT32 maxy; + // Scroll menu items if needed + cursory = y + currentMenu->menuitems[replaylistitem].mvar1 + dir_on[menudepthleft]*10; + maxy = y + currentMenu->menuitems[replaylistitem].mvar1 + sizedirmenu*10; + + if (cursory > maxy - 20) + cursory = maxy - 20; + + if (cursory - replayhutmenuy > SCALEDVIEWHEIGHT-50) + replayhutmenuy += (cursory-SCALEDVIEWHEIGHT-replayhutmenuy + 51)/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++) + { + INT32 localy = y + currentMenu->menuitems[i].mvar1; + + if (localy < 65) + continue; + + if (i == itemOn) + cursory = localy; + + if ((currentMenu->menuitems[i].status & IT_DISPLAY)==IT_STRING) + V_DrawString(x, localy, V_SNAPTOTOP|V_SNAPTOLEFT, currentMenu->menuitems[i].text); + else + V_DrawString(x, localy, V_SNAPTOTOP|V_SNAPTOLEFT|highlightflags, currentMenu->menuitems[i].text); + } + + y += currentMenu->menuitems[replaylistitem].mvar1; + + for (i = 0; i < (INT16)sizedirmenu; i++) + { + INT32 localy = y+i*10; + INT32 localx = x; + + if (localy < 65) + continue; + if (localy >= SCALEDVIEWHEIGHT) + break; + + if (extrasmenu.demolist[i].type == MD_NOTLOADED && !processed_one_this_frame) + { + processed_one_this_frame = true; + G_LoadDemoInfo(&extrasmenu.demolist[i]); + } + + if (extrasmenu.demolist[i].type == MD_SUBDIR) + { + localx += 8; + V_DrawScaledPatch(x - 4, localy, V_SNAPTOTOP|V_SNAPTOLEFT, W_CachePatchName(dirmenu[i][DIR_TYPE] == EXT_UP ? "M_RBACK" : "M_RFLDR", PU_CACHE)); + } + + if (itemOn == replaylistitem && i == (INT16)dir_on[menudepthleft]) + { + cursory = localy; + + if (extrasmenu.replayScrollDelay) + extrasmenu.replayScrollDelay--; + else if (extrasmenu.replayScrollDir > 0) + { + if (extrasmenu.replayScrollTitle < (V_StringWidth(extrasmenu.demolist[i].title, 0) - (SCALEDVIEWWIDTH - (x<<1)))<<1) + extrasmenu.replayScrollTitle++; + else + { + extrasmenu.replayScrollDelay = TICRATE; + extrasmenu.replayScrollDir = -1; + } + } + else + { + if (extrasmenu.replayScrollTitle > 0) + extrasmenu.replayScrollTitle--; + else + { + extrasmenu.replayScrollDelay = TICRATE; + extrasmenu.replayScrollDir = 1; + } + } + + V_DrawString(localx - (extrasmenu.replayScrollTitle>>1), localy, V_SNAPTOTOP|V_SNAPTOLEFT|highlightflags|V_ALLOWLOWERCASE, extrasmenu.demolist[i].title); + } + else + V_DrawString(localx, localy, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, extrasmenu.demolist[i].title); + } + + // Draw scrollbar + y = sizedirmenu*10 + currentMenu->menuitems[replaylistitem].mvar1 + 30; + if (y > SCALEDVIEWHEIGHT-80) + { + V_DrawFill(BASEVIDWIDTH-4, 75, 4, SCALEDVIEWHEIGHT-80, V_SNAPTOTOP|V_SNAPTORIGHT|159); + V_DrawFill(BASEVIDWIDTH-3, 76 + (SCALEDVIEWHEIGHT-80) * replayhutmenuy / y, 2, (((SCALEDVIEWHEIGHT-80) * (SCALEDVIEWHEIGHT-80))-1) / y - 1, V_SNAPTOTOP|V_SNAPTORIGHT|149); + } + + // Draw the cursor + V_DrawScaledPatch(currentMenu->x - 24, cursory, V_SNAPTOTOP|V_SNAPTOLEFT, + W_CachePatchName("M_CURSOR", PU_CACHE)); + V_DrawString(currentMenu->x, cursory, V_SNAPTOTOP|V_SNAPTOLEFT|highlightflags, currentMenu->menuitems[itemOn].text); + + // Now draw some replay info! + V_DrawFill(10, 10, 300, 60, V_SNAPTOTOP|159); + + if (itemOn == replaylistitem) + { + M_DrawReplayHutReplayInfo(); + } +} + +void M_DrawReplayStartMenu(void) +{ + const char *warning; + UINT8 i; + + M_DrawEggaChannel(); + M_DrawGenericMenu(); + +#define STARTY 62-(extrasmenu.replayScrollTitle>>1) + // Draw rankings beyond first + for (i = 1; i < MAXPLAYERS && extrasmenu.demolist[dir_on[menudepthleft]].standings[i].ranking; i++) + { + patch_t *patch; + UINT8 *colormap; + + V_DrawRightAlignedString(BASEVIDWIDTH-100, STARTY + i*20, V_SNAPTOTOP|highlightflags, va("%2d", extrasmenu.demolist[dir_on[menudepthleft]].standings[i].ranking)); + V_DrawThinString(BASEVIDWIDTH-96, STARTY + i*20, V_SNAPTOTOP|V_ALLOWLOWERCASE, extrasmenu.demolist[dir_on[menudepthleft]].standings[i].name); + + if (extrasmenu.demolist[dir_on[menudepthleft]].standings[i].timeorscore == UINT32_MAX-1) + V_DrawThinString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, "NO CONTEST"); + else if (extrasmenu.demolist[dir_on[menudepthleft]].gametype == GT_RACE) + V_DrawRightAlignedString(BASEVIDWIDTH-40, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d'%02d\"%02d", + G_TicsToMinutes(extrasmenu.demolist[dir_on[menudepthleft]].standings[i].timeorscore, true), + G_TicsToSeconds(extrasmenu.demolist[dir_on[menudepthleft]].standings[i].timeorscore), + G_TicsToCentiseconds(extrasmenu.demolist[dir_on[menudepthleft]].standings[i].timeorscore) + )); + else + V_DrawString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d", extrasmenu.demolist[dir_on[menudepthleft]].standings[i].timeorscore)); + + // Character face! + + // Lat: 08/06/2020: For some reason missing skins have their value set to 255 (don't even ask me why I didn't write this) + // and for an even STRANGER reason this passes the first check below, so we're going to make sure that the skin here ISN'T 255 before we do anything stupid. + + if (extrasmenu.demolist[dir_on[menudepthleft]].standings[i].skin != 0xFF) + { + patch = faceprefix[extrasmenu.demolist[dir_on[menudepthleft]].standings[i].skin][FACE_RANK]; + colormap = R_GetTranslationColormap( + extrasmenu.demolist[dir_on[menudepthleft]].standings[i].skin, + extrasmenu.demolist[dir_on[menudepthleft]].standings[i].color, + GTC_MENUCACHE); + } + else + { + patch = W_CachePatchName("M_NORANK", PU_CACHE); + colormap = R_GetTranslationColormap( + TC_RAINBOW, + extrasmenu.demolist[dir_on[menudepthleft]].standings[i].color, + GTC_MENUCACHE); + } + + V_DrawMappedPatch(BASEVIDWIDTH-5 - SHORT(patch->width), STARTY + i*20, V_SNAPTOTOP, patch, colormap); + } +#undef STARTY + + // Handle scrolling rankings + if (extrasmenu.replayScrollDelay) + extrasmenu.replayScrollDelay--; + else if (extrasmenu.replayScrollDir > 0) + { + if (extrasmenu.replayScrollTitle < (i*20 - SCALEDVIEWHEIGHT + 100)<<1) + extrasmenu.replayScrollTitle++; + else + { + extrasmenu.replayScrollDelay = TICRATE; + extrasmenu.replayScrollDir = -1; + } + } + else + { + if (extrasmenu.replayScrollTitle > 0) + extrasmenu.replayScrollTitle--; + else + { + extrasmenu.replayScrollDelay = TICRATE; + extrasmenu.replayScrollDir = 1; + } + } + + V_DrawFill(10, 10, 300, 60, V_SNAPTOTOP|159); + M_DrawReplayHutReplayInfo(); + + V_DrawString(10, 72, V_SNAPTOTOP|highlightflags|V_ALLOWLOWERCASE, extrasmenu.demolist[dir_on[menudepthleft]].title); + + // Draw a warning prompt if needed + switch (extrasmenu.demolist[dir_on[menudepthleft]].addonstatus) + { + case DFILE_ERROR_CANNOTLOAD: + warning = "Some addons in this replay cannot be loaded.\nYou can watch anyway, but desyncs may occur."; + break; + + case DFILE_ERROR_NOTLOADED: + case DFILE_ERROR_INCOMPLETEOUTOFORDER: + warning = "Loading addons will mark your game as modified, and Record Attack may be unavailable.\nYou can watch without loading addons, but desyncs may occur."; + break; + + case DFILE_ERROR_EXTRAFILES: + warning = "You have addons loaded that were not present in this replay.\nYou can watch anyway, but desyncs may occur."; + break; + + case DFILE_ERROR_OUTOFORDER: + warning = "You have this replay's addons loaded, but they are out of order.\nYou can watch anyway, but desyncs may occur."; + break; + + default: + return; + } + + V_DrawSmallString(4, BASEVIDHEIGHT-14, V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, warning); +} + // Draw misc menus: // Addons (this is merely copypasted, original code by toaster) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 15cbf7316..5ced69765 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3783,12 +3783,223 @@ void M_PlaybackQuit(INT32 choice) D_StartTitle(); } +void M_PrepReplayList(void) +{ + size_t i; + + if (extrasmenu.demolist) + Z_Free(extrasmenu.demolist); + + extrasmenu.demolist = Z_Calloc(sizeof(menudemo_t) * sizedirmenu, PU_STATIC, NULL); + + for (i = 0; i < sizedirmenu; i++) + { + if (dirmenu[i][DIR_TYPE] == EXT_UP) + { + extrasmenu.demolist[i].type = MD_SUBDIR; + sprintf(extrasmenu.demolist[i].title, "UP"); + } + else if (dirmenu[i][DIR_TYPE] == EXT_FOLDER) + { + extrasmenu.demolist[i].type = MD_SUBDIR; + strncpy(extrasmenu.demolist[i].title, dirmenu[i] + DIR_STRING, 64); + } + else + { + extrasmenu.demolist[i].type = MD_NOTLOADED; + snprintf(extrasmenu.demolist[i].filepath, 255, "%s%s", menupath, dirmenu[i] + DIR_STRING); + sprintf(extrasmenu.demolist[i].title, "....."); + } + } +} + void M_ReplayHut(INT32 choice) { (void)choice; + + extrasmenu.replayScrollTitle = 0; + extrasmenu.replayScrollDelay = TICRATE; + extrasmenu.replayScrollDir = 1; + + if (!demo.inreplayhut) + { + snprintf(menupath, 1024, "%s"PATHSEP"media"PATHSEP"replay"PATHSEP"online"PATHSEP, srb2home); + menupathindex[(menudepthleft = menudepth-1)] = strlen(menupath); + } + if (!preparefilemenu(false, true)) + { + M_StartMessage("No replays found.\n\n(Press a key)\n", NULL, MM_NOTHING); + return; + } + else if (!demo.inreplayhut) + dir_on[menudepthleft] = 0; + demo.inreplayhut = true; + + extrasmenu.replayScrollTitle = 0; extrasmenu.replayScrollDelay = TICRATE; extrasmenu.replayScrollDir = 1; + + M_PrepReplayList(); + + menuactive = true; + M_SetupNextMenu(&EXTRAS_ReplayHutDef, false); + //G_SetGamestate(GS_TIMEATTACK); + //titlemapinaction = TITLEMAP_OFF; // Nope don't give us HOMs please + + demo.rewinding = false; + CL_ClearRewinds(); + + //S_ChangeMusicInternal("replst", true); } +// key handler +void M_HandleReplayHutList(INT32 choice) +{ + switch (choice) + { + case KEY_UPARROW: + if (dir_on[menudepthleft]) + dir_on[menudepthleft]--; + else + return; + //M_PrevOpt(); + + S_StartSound(NULL, sfx_menu1); + extrasmenu.replayScrollTitle = 0; extrasmenu.replayScrollDelay = TICRATE; extrasmenu.replayScrollDir = 1; + break; + + case KEY_DOWNARROW: + if (dir_on[menudepthleft] < sizedirmenu-1) + dir_on[menudepthleft]++; + else + return; + //itemOn = 0; // Not M_NextOpt because that would take us to the extra dummy item + + S_StartSound(NULL, sfx_menu1); + extrasmenu.replayScrollTitle = 0; extrasmenu.replayScrollDelay = TICRATE; extrasmenu.replayScrollDir = 1; + break; + + case KEY_ESCAPE: + M_QuitReplayHut(); + break; + + case KEY_ENTER: + 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, true)) + { + 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, true)) + { + M_QuitReplayHut(); + return; + } + } + else + { + S_StartSound(NULL, sfx_menu1); + dir_on[menudepthleft] = 1; + M_PrepReplayList(); + } + } + 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, true)) + { + M_QuitReplayHut(); + return; + } + M_PrepReplayList(); + break; + default: + // We can't just use M_SetupNextMenu because that'll run ReplayDef's quitroutine and boot us back to the title screen! + currentMenu->lastOn = itemOn; + currentMenu = &EXTRAS_ReplayStartDef; + + extrasmenu.replayScrollTitle = 0; extrasmenu.replayScrollDelay = TICRATE; extrasmenu.replayScrollDir = 1; + + switch (extrasmenu.demolist[dir_on[menudepthleft]].addonstatus) + { + case DFILE_ERROR_CANNOTLOAD: + // Only show "Watch Replay Without Addons" + EXTRAS_ReplayStart[0].status = IT_DISABLED; + EXTRAS_ReplayStart[1].status = IT_CALL|IT_STRING; + //EXTRAS_ReplayStart[1].alphaKey = 0; + EXTRAS_ReplayStart[2].status = IT_DISABLED; + itemOn = 1; + break; + + case DFILE_ERROR_NOTLOADED: + case DFILE_ERROR_INCOMPLETEOUTOFORDER: + // Show "Load Addons and Watch Replay" and "Watch Replay Without Addons" + EXTRAS_ReplayStart[0].status = IT_CALL|IT_STRING; + EXTRAS_ReplayStart[1].status = IT_CALL|IT_STRING; + //EXTRAS_ReplayStart[1].alphaKey = 10; + EXTRAS_ReplayStart[2].status = IT_DISABLED; + itemOn = 0; + break; + + case DFILE_ERROR_EXTRAFILES: + case DFILE_ERROR_OUTOFORDER: + default: + // Show "Watch Replay" + EXTRAS_ReplayStart[0].status = IT_DISABLED; + EXTRAS_ReplayStart[1].status = IT_DISABLED; + EXTRAS_ReplayStart[2].status = IT_CALL|IT_STRING; + //EXTRAS_ReplayStart[2].alphaKey = 0; + itemOn = 2; + break; + } + } + + break; + } +} + +boolean M_QuitReplayHut(void) +{ + // D_StartTitle does its own wipe, since GS_TIMEATTACK is now a complete gamestate. + menuactive = false; + D_StartTitle(); + + if (extrasmenu.demolist) + Z_Free(extrasmenu.demolist); + extrasmenu.demolist = NULL; + + demo.inreplayhut = false; + + return true; +} + +void M_HutStartReplay(INT32 choice) +{ + (void)choice; + + M_ClearMenus(false); + demo.loadfiles = (itemOn == 0); + demo.ignorefiles = (itemOn != 0); + + G_DoPlayDemo(extrasmenu.demolist[dir_on[menudepthleft]].filepath); +} + + static void Splitplayers_OnChange(void) { #if 0 From f093d5783f2954dec8a9f9961bff227b2faf52fb Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 10 Dec 2021 20:31:07 +0100 Subject: [PATCH 056/379] Put back the transition for addons screen after all... --- src/k_menufunc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 5ced69765..349f9e560 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -4050,7 +4050,7 @@ void M_Addons(INT32 choice) dir_on[menudepthleft] = 0; MISC_AddonsDef.prevMenu = currentMenu; - M_SetupNextMenu(&MISC_AddonsDef, true); // No transitions. + M_SetupNextMenu(&MISC_AddonsDef, false); } From 841649c7ead29ad63cad01c382711279a581c9b9 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 11 Dec 2021 11:23:48 +0100 Subject: [PATCH 057/379] opengl: fake the pause fade background --- src/k_menudraw.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index c9fc729c3..de9a354d0 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -245,7 +245,10 @@ void M_Drawer(void) } else if (!WipeInAction && currentMenu != &PAUSE_PlaybackMenuDef) { - V_DrawCustomFadeScreen("FADEMAP0", 4); // now that's more readable with a faded background (yeah like Quake...) + if (rendermode == render_opengl) // OGL can't handle what SW is doing so let's fake it; + V_DrawFadeScreen(122, 3); // palette index aproximation... + else // Software can keep its unique fade + V_DrawCustomFadeScreen("FADEMAP0", 4); // now that's more readable with a faded background (yeah like Quake...) } if (currentMenu->drawroutine) From cdb3b1ca982a41f10db10b100742ace229307a17 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 11 Dec 2021 18:22:56 +0100 Subject: [PATCH 058/379] Difficulty select before GP, match race & netgames --- src/k_menu.h | 15 ++++++ src/k_menudef.c | 52 +++++++++++++++++-- src/k_menudraw.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++ src/k_menufunc.c | 104 +++++++++++++++++++++++++++++++++++--- 4 files changed, 291 insertions(+), 9 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 7eca2ca0d..1f70256d0 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -174,6 +174,9 @@ extern menu_t PLAY_GamemodesDef; extern menuitem_t PLAY_RaceGamemodesMenu[]; extern menu_t PLAY_RaceGamemodesDef; +extern menuitem_t PLAY_RaceDifficulty[]; +extern menu_t PLAY_RaceDifficultyDef; + extern menuitem_t PLAY_CupSelect[]; extern menu_t PLAY_CupSelectDef; @@ -478,6 +481,16 @@ void M_CupSelectTick(void); void M_LevelSelectHandler(INT32 choice); void M_LevelSelectTick(void); +// dummy consvars for GP & match race setup +extern consvar_t cv_dummygpdifficulty; +extern consvar_t cv_dummykartspeed; +extern consvar_t cv_dummygpencore; +extern consvar_t cv_dummymatchbots; + +void M_SetupDifficultySelect(INT32 choice); +void M_SetupDifficultySelectMP(INT32 choice); +void M_DifficultySelectInputs(INT32 choice); + // Multiplayer menu stuff // Keep track of multiplayer menu related data @@ -692,6 +705,8 @@ void M_DrawCupSelect(void); void M_DrawLevelSelect(void); void M_DrawTimeAttack(void); +void M_DrawRaceDifficulty(void); + // Multiplayer menu stuff void M_DrawMPOptSelect(void); void M_DrawMPHost(void); diff --git a/src/k_menudef.c b/src/k_menudef.c index 6a9487828..38ddcbe29 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -100,10 +100,10 @@ menu_t PLAY_GamemodesDef = KARTGAMEMODEMENU(PLAY_GamemodesMenu, &PLAY_MainDef); menuitem_t PLAY_RaceGamemodesMenu[] = { {IT_STRING | IT_CALL, "Grand Prix", "Compete for the best rank over five races!", - NULL, M_LevelSelectInit, 2, GT_RACE}, + NULL, M_SetupDifficultySelect, 0, 0}, {IT_STRING | IT_CALL, "Match Race", "Play by your own rules in a specialized, single race!", - "MENIMG01", M_LevelSelectInit, 0, GT_RACE}, + "MENIMG01", M_SetupDifficultySelect, 1, 0}, {IT_STRING | IT_CALL, "Time Attack", "Record your best time on any track!", NULL, M_LevelSelectInit, 1, GT_RACE}, @@ -113,6 +113,52 @@ menuitem_t PLAY_RaceGamemodesMenu[] = menu_t PLAY_RaceGamemodesDef = KARTGAMEMODEMENU(PLAY_RaceGamemodesMenu, &PLAY_GamemodesDef); + +// difficulty selection: +menuitem_t PLAY_RaceDifficulty[] = +{ + // local play + {IT_STRING | IT_CVAR, "Difficulty", "Select the game difficulty", + NULL, &cv_dummygpdifficulty, 0, 0}, + + // netgames + {IT_STRING | IT_CVAR, "Difficulty", "Select the game speed", + NULL, &cv_dummykartspeed, 0, 0}, + + // DISABLE THAT OPTION OUTSIDE OF MATCH RACE + {IT_STRING2 | IT_CVAR, "CPU Players", "Enable or disable CPU players.", // 2 whitestring is used by the drawer to know to draw shitstring + NULL, &cv_dummymatchbots, 0, 0}, + + {IT_STRING2 | IT_CVAR, "Encore", "Enable or disable Encore mode", // 3 + NULL, &cv_dummygpencore, 0, 0}, + + // For GP: + {IT_STRING | IT_CALL, "Cup Select", "Go on and select a cup!", NULL, M_LevelSelectInit, 2, GT_RACE}, // 4 + + // For Match Race: + {IT_STRING | IT_CALL, "Map Select", "Go on and select a race track!", NULL, M_LevelSelectInit, 0, GT_RACE}, // 5 + + // For Match Race in NETGAMES: + {IT_STRING | IT_CALL, "Map Select", "Go on and select a race track!", NULL, M_MPSetupNetgameMapSelect, 0, GT_RACE}, // 6 + + {IT_STRING | IT_CALL, "Back", NULL, NULL, M_GoBack, 0, 0}, +}; + +menu_t PLAY_RaceDifficultyDef = { + sizeof(PLAY_RaceDifficulty) / sizeof(menuitem_t), + &PLAY_RaceGamemodesDef, + 0, + PLAY_RaceDifficulty, + 0, 0, + 0, 0, + 1, 10, + M_DrawRaceDifficulty, + NULL, + NULL, + NULL +}; + + menuitem_t PLAY_CupSelect[] = { {IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, M_CupSelectHandler, 0, 0}, @@ -234,7 +280,7 @@ menuitem_t PLAY_MP_Host[] = NULL, &cv_dummygametype, 0, 0}, {IT_STRING | IT_CALL, "GO", "Select a map with the currently selected gamemode", - NULL, M_MPSetupNetgameMapSelect, 0, 0}, + NULL, M_SetupDifficultySelectMP, 0, 0}, }; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index de9a354d0..abc29e912 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1020,6 +1020,135 @@ void M_DrawCharacterSelect(void) M_DrawCharSelectCursor(priority); } +// DIFFICULTY SELECT +// This is a mix of K_DrawKartGamemodeMenu and the generic menu drawer depending on what we need. +// This is only ever used here (I hope because this is starting to pile up on hacks to look like the old m_menu.c lol...) + +void M_DrawRaceDifficulty(void) +{ + UINT8 n = currentMenu->numitems-4; + patch_t *box = W_CachePatchName("M_DBOX", PU_CACHE); + + INT32 i; + INT32 x = 120; + INT32 y = 48; + + M_DrawMenuTooltips(); + + // Draw the box for difficulty... + V_DrawFixedPatch((111 + 24*menutransition.tics)*FRACUNIT, 33*FRACUNIT, FRACUNIT, 0, box, NULL); + + if (menutransition.tics) + { + x += 24 * menutransition.tics; + } + + for (i = 0; i < currentMenu->numitems; i++) + { + if (i >= n) + { + + x = GM_STARTX + (GM_XOFFSET * 5 / 2); + y = GM_STARTY + (GM_YOFFSET * 5 / 2); + + if (i < currentMenu->numitems-1) + { + x -= GM_XOFFSET; + y -= GM_YOFFSET; + } + + + if (menutransition.tics) + { + x += 24 * menutransition.tics; + } + } + + switch (currentMenu->menuitems[i].status & IT_DISPLAY) + { + // This is HACKY...... + + case IT_STRING2: + { + + INT32 f = (i == itemOn) ? highlightflags : 0; + + V_DrawString(140 + 24*menutransition.tics, y, f, currentMenu->menuitems[i].text); + + if (currentMenu->menuitems[i].status & IT_CVAR) + { + // implicitely we'll only take care of normal cvars + INT32 cx = 260 + 24*menutransition.tics; + consvar_t *cv = (consvar_t *)currentMenu->menuitems[i].itemaction; + + V_DrawCenteredString(cx, y, f, cv->string); + + if (i == itemOn) + { + + INT32 w = V_StringWidth(cv->string, 0)/2; + + V_DrawCharacter(cx - 10 - w - (skullAnimCounter/5), y, '\x1C' | highlightflags, false); // left arrow + V_DrawCharacter(cx + w + 2 + (skullAnimCounter/5), y, '\x1D' | highlightflags, false); // right arrow + } + } + + y += 12; + + break; + } + + case IT_STRING: + { + + UINT8 *colormap = NULL; + + if (i == itemOn) + { + colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE); + } + else + { + colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_MOSS, GTC_CACHE); + } + + + if (currentMenu->menuitems[i].status & IT_CVAR) + { + + INT32 fx = (x - 24*menutransition.tics); + INT32 centx = fx + (320-fx)/2 + (menutransition.tics*24); // undo the menutransition movement to redo it here otherwise the text won't move at the same speed lole. + + // implicitely we'll only take care of normal consvars + consvar_t *cv = (consvar_t *)currentMenu->menuitems[i].itemaction; + + V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, W_CachePatchName("MENUSHRT", PU_CACHE), colormap); + V_DrawCenteredGamemodeString(centx, y - 3, V_ALLOWLOWERCASE, colormap, cv->string); + + if (i == itemOn) + { + patch_t *arr_r = W_CachePatchName("GM_ARRL", PU_CACHE); + patch_t *arr_l = W_CachePatchName("GM_ARRR", PU_CACHE); + + V_DrawFixedPatch((centx-54 - arr_r->width - (skullAnimCounter/5))*FRACUNIT, (y-3)*FRACUNIT, FRACUNIT, 0, arr_r, colormap); + V_DrawFixedPatch((centx+54 + (skullAnimCounter/5))*FRACUNIT, (y-3)*FRACUNIT, FRACUNIT, 0, arr_l, colormap); + } + + } + else // not a cvar + { + V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, W_CachePatchName("MENUPLTR", PU_CACHE), colormap); + V_DrawGamemodeString(x + 16, y - 3, V_ALLOWLOWERCASE, colormap, currentMenu->menuitems[i].text); + } + x += GM_XOFFSET; + y += GM_YOFFSET; + + break; + } + } + } +} + // LEVEL SELECT static void M_DrawCupPreview(INT16 y, cupheader_t *cup) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 349f9e560..a507f1632 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -166,6 +166,9 @@ static CV_PossibleValue_t dummyscramble_cons_t[] = {{0, "Random"}, {1, "Points"} static CV_PossibleValue_t dummystaff_cons_t[] = {{0, "MIN"}, {100, "MAX"}, {0, NULL}}; static CV_PossibleValue_t dummygametype_cons_t[] = {{0, "Race"}, {1, "Battle"}, {0, NULL}}; +static CV_PossibleValue_t dummygpdifficulty_cons_t[] = {{0, "Easy"}, {1, "Normal"}, {2, "Hard"}, {3, "Master"}, {0, NULL}}; +static CV_PossibleValue_t dummykartspeed_cons_t[] = {{-1, "Auto"}, {0, "Easy"}, {1, "Normal"}, {2, "Hard"}, {0, NULL}}; + //static consvar_t cv_dummymenuplayer = CVAR_INIT ("dummymenuplayer", "P1", CV_HIDDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange); static consvar_t cv_dummyteam = CVAR_INIT ("dummyteam", "Spectator", CV_HIDDEN, dummyteam_cons_t, NULL); //static cv_dummyspectate = CVAR_INITconsvar_t ("dummyspectate", "Spectator", CV_HIDDEN, dummyspectate_cons_t, NULL); @@ -176,6 +179,26 @@ consvar_t cv_dummyip = CVAR_INIT ("dummyip", "", CV_HIDDEN, NULL, NULL); consvar_t cv_dummymenuplayer = CVAR_INIT ("dummymenuplayer", "P1", CV_HIDDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange); consvar_t cv_dummyspectate = CVAR_INIT ("dummyspectate", "Spectator", CV_HIDDEN, dummyspectate_cons_t, NULL); +consvar_t cv_dummygpdifficulty = CVAR_INIT ("dummygpdifficulty", "Normal", CV_HIDDEN, dummygpdifficulty_cons_t, NULL); +consvar_t cv_dummykartspeed = CVAR_INIT ("dummykartspeed", "Auto", CV_HIDDEN, dummykartspeed_cons_t, NULL); +consvar_t cv_dummygpencore = CVAR_INIT ("dummygpdifficulty", "No", CV_HIDDEN, CV_YesNo, NULL); + +static CV_PossibleValue_t dummymatchbots_cons_t[] = { + {0, "Off"}, + {1, "Lv.1"}, + {2, "Lv.2"}, + {3, "Lv.3"}, + {4, "Lv.4"}, + {5, "Lv.5"}, + {6, "Lv.6"}, + {7, "Lv.7"}, + {8, "Lv.8"}, + {9, "Lv.9"}, + {0, NULL} +}; + +consvar_t cv_dummymatchbots = CVAR_INIT ("dummymatchbots", "Off", CV_HIDDEN|CV_SAVE, dummymatchbots_cons_t, NULL); // Save this one if you wanna test your stuff without bots for instance + // ========================================================================== // CVAR ONCHANGE EVENTS GO HERE // ========================================================================== @@ -1629,6 +1652,11 @@ void M_Init(void) CV_RegisterVar(&cv_dummygametype); CV_RegisterVar(&cv_dummyip); + CV_RegisterVar(&cv_dummygpdifficulty); + CV_RegisterVar(&cv_dummykartspeed); + CV_RegisterVar(&cv_dummygpencore); + CV_RegisterVar(&cv_dummymatchbots); + M_UpdateMenuBGImage(true); #if 0 @@ -2347,6 +2375,60 @@ boolean M_CharacterSelectQuit(void) return true; } +// DIFFICULTY SELECT + +void M_SetupDifficultySelect(INT32 choice) +{ + // check what we picked. + choice = currentMenu->menuitems[itemOn].mvar1; + + // setup the difficulty menu and then remove choices depending on choice + PLAY_RaceDifficultyDef.prevMenu = currentMenu; + M_SetupNextMenu(&PLAY_RaceDifficultyDef, false); + + PLAY_RaceDifficulty[0].status = IT_STRING|IT_CVAR; + PLAY_RaceDifficulty[1].status = IT_DISABLED; + PLAY_RaceDifficulty[2].status = IT_DISABLED; + PLAY_RaceDifficulty[3].status = IT_DISABLED; + PLAY_RaceDifficulty[4].status = IT_DISABLED; + PLAY_RaceDifficulty[5].status = IT_DISABLED; + PLAY_RaceDifficulty[6].status = IT_DISABLED; + + if (choice) // Match Race + { + PLAY_RaceDifficulty[2].status = IT_STRING2|IT_CVAR; // CPUs on/off use string2 to signify not to use the normal gm font drawer + PLAY_RaceDifficulty[3].status = IT_STRING2|IT_CVAR; // Encore on/off use string2 to signify not to use the normal gm font drawer + PLAY_RaceDifficulty[5].status = IT_STRING|IT_CALL; // Level Select (Match Race) + itemOn = 5; // Select cup select by default. + + } + else // GP + { + PLAY_RaceDifficulty[3].status = IT_STRING2|IT_CVAR; // Encore on/off use string2 to signify not to use the normal gm font drawer + PLAY_RaceDifficulty[4].status = IT_STRING|IT_CALL; // Level Select (GP) + itemOn = 4; // Select cup select by default. + } +} + +// calls the above but changes the cvar we set +void M_SetupDifficultySelectMP(INT32 choice) +{ + (void) choice; + + PLAY_RaceDifficultyDef.prevMenu = currentMenu; + M_SetupNextMenu(&PLAY_RaceDifficultyDef, false); + + PLAY_RaceDifficulty[0].status = IT_DISABLED; + PLAY_RaceDifficulty[1].status = IT_STRING|IT_CVAR; + PLAY_RaceDifficulty[2].status = IT_STRING2|IT_CVAR; // CPUs on/off use string2 to signify not to use the normal gm font drawer + PLAY_RaceDifficulty[3].status = IT_STRING2|IT_CVAR; // Encore on/off use string2 to signify not to use the normal gm font drawer + PLAY_RaceDifficulty[4].status = IT_DISABLED; + PLAY_RaceDifficulty[5].status = IT_DISABLED; + PLAY_RaceDifficulty[6].status = IT_STRING|IT_CALL; + + itemOn = 6; // Select cup select by default. +} + // LEVEL SELECT // @@ -2585,10 +2667,11 @@ void M_CupSelectHandler(INT32 choice) memset(&grandprixinfo, 0, sizeof(struct grandprixinfo)); - // TODO: game settings screen - grandprixinfo.gamespeed = KARTSPEED_NORMAL; - grandprixinfo.masterbots = false; - grandprixinfo.encore = false; + // read our dummy cvars + + grandprixinfo.gamespeed = min(KARTSPEED_HARD, cv_dummygpdifficulty.value); + grandprixinfo.masterbots = (cv_dummygpdifficulty.value == 3); + grandprixinfo.encore = (boolean)cv_dummygpencore.value; grandprixinfo.cup = newcup; @@ -2744,9 +2827,18 @@ void M_LevelSelectHandler(INT32 choice) SV_StartSinglePlayerServer(); multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... netgame = levellist.netgame; // ^ ditto. - } - D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false); + // this is considered to be CV_CHEAT however... + CV_StealthSet(&cv_kartbot, cv_dummymatchbots.string); // Match the kartbot value to the dummy match bots value. + + if (netgame) // check for the dummy kartspeed value + CV_StealthSet(&cv_kartspeed, cv_dummykartspeed.string); + + + D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_dummygpencore.value == 1), 1, 1, false, false); + } + else // directly do the map change + D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false); M_ClearMenus(true); } From 813b2da06576b436d365f1def8b9a36deb804529 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 11 Dec 2021 18:32:50 +0100 Subject: [PATCH 059/379] Fix the host menu re-folding on itself if you backed out of difficulty selection --- src/k_menufunc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index a507f1632..11774e85c 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2427,6 +2427,9 @@ void M_SetupDifficultySelectMP(INT32 choice) PLAY_RaceDifficulty[6].status = IT_STRING|IT_CALL; itemOn = 6; // Select cup select by default. + + // okay this is REALLY stupid but this fixes the host menu re-folding on itself when we go back. + mpmenu.modewinextend[0][0] = 1; } // LEVEL SELECT From e53ad4edf496f82949a2b1ea1e8c9c42dd73d0eb Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 25 Dec 2021 07:02:07 -0500 Subject: [PATCH 060/379] Char select fixes - Allow input for multiplayer now - Unset all devices in this menu --- src/k_menufunc.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index d46064bb9..885f4fed2 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1899,7 +1899,6 @@ void M_CharacterSelectInit(INT32 choice) } memset(setup_player, 0, sizeof(setup_player)); - //setup_player[0].mdepth = CSSTEP_CHARS; setup_numplayers = 0; memset(setup_explosions, 0, sizeof(setup_explosions)); @@ -1929,6 +1928,12 @@ void M_CharacterSelectInit(INT32 choice) } } + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + // Un-set all devices upon entering this menu. + cv_usejoystick[i].value = -1; + } + PLAY_CharSelectDef.prevMenu = currentMenu; M_SetupNextMenu(&PLAY_CharSelectDef, false); } @@ -2222,9 +2227,6 @@ boolean M_CharacterSelectHandler(INT32 choice) { setup_player_t *p = &setup_player[i]; - if (i > 0) - break; // temp - if (p->delay == 0 && menucmd[i].delay == 0) { switch (p->mdepth) From d6d561f0e82a0067b2c0b28566449489b1d0a5e6 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 25 Dec 2021 07:10:56 -0500 Subject: [PATCH 061/379] Unset delay when not pressing anything Allows for mashing a button to be just as responsive as before --- src/k_menufunc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 885f4fed2..f408fea3f 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1234,7 +1234,7 @@ static void M_UpdateMenuCMD(UINT8 i) if (menucmd[i].dpad_ud == 0 && menucmd[i].dpad_lr == 0 && menucmd[i].buttons == 0) { // Reset delay count with no buttons. - menucmd[i].delayCount = 0; + menucmd[i].delay = menucmd[i].delayCount = 0; } } From 7684f5980adafe400cbd975fa9d2ba4ebaa9502b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 25 Dec 2021 08:58:21 -0500 Subject: [PATCH 062/379] Almost multiplayer char select For some reason gamepads have not been registering buttons for a while, which makes this pretty hard to continue. Not sure if it's to do with how the menu cmd is generated, or something deeper in the SDL code. --- src/g_game.c | 47 +++++++++++++++++++-------------- src/g_input.c | 68 +++++++++++++++++++++++++----------------------- src/g_input.h | 5 ++-- src/k_menufunc.c | 25 ++++++++++-------- 4 files changed, 81 insertions(+), 64 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index affffcd88..481822d07 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -652,21 +652,38 @@ INT16 G_SoftwareClipAimingPitch(INT32 *aiming) return (INT16)((*aiming)>>16); } -static INT32 KeyValue(UINT8 p, INT32 key) +static INT32 KeyValue(UINT8 p, INT32 key, boolean menu) { + INT32 deviceID; + if (key <= 0 || key >= NUMINPUTS) { return 0; } - return gamekeydown[p][key]; + if (menu == false) + { + deviceID = cv_usejoystick[p].value; + if (deviceID < 0 || deviceID >= MAXDEVICES) + { + return 0; + } + + return gamekeydown[deviceID][key]; + } + else + { + // Use keyboard as alternative for P1 menu. + return gamekeydown[0][key]; + } + + return 0; } INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean menu) { INT32 i; INT32 deadzone = 0; - boolean bound = false; if (p >= MAXSPLITSCREENPLAYERS) { @@ -676,6 +693,11 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean menu) return 0; } + if (p > splitscreen) + { + return 0; + } + deadzone = (JOYAXISRANGE * cv_deadzone[p].value) / FRACUNIT; for (i = 0; i < MAXINPUTMAPPING; i++) @@ -688,14 +710,7 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean menu) continue; } - value = KeyValue(p, key); - bound = true; - - if (gc == gc_a || gc == gc_b || gc == gc_c) - { - // Handle spindash key - value = max(value, KeyValue(p, gamecontrol[p][gc_abc][i])); - } + value = KeyValue(p, key, false); if (value >= deadzone) { @@ -703,7 +718,7 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean menu) } } - if (menu == true && bound == false) + if (p == 0 && menu == true) { // We don't want menus to become unnavigable if people unbind // all of their controls, so use the default control scheme in @@ -719,13 +734,7 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean menu) continue; } - value = KeyValue(p, key); - - if (gc == gc_a || gc == gc_b || gc == gc_c) - { - // Handle spindash key - value = max(value, KeyValue(p, gamecontroldefault[gc_abc][i])); - } + value = KeyValue(p, key, true); if (value >= deadzone) { diff --git a/src/g_input.c b/src/g_input.c index 5b4fe8675..00f6ac5a0 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -33,7 +33,7 @@ consvar_t cv_controlperkey = CVAR_INIT ("controlperkey", "One", CV_SAVE, onecont // current state of the keys // FRACUNIT for fully pressed, 0 for not pressed -INT32 gamekeydown[MAXSPLITSCREENPLAYERS][NUMINPUTS]; +INT32 gamekeydown[MAXDEVICES][NUMINPUTS]; boolean deviceResponding[MAXDEVICES]; // two key codes (or virtual key) per game control @@ -67,6 +67,21 @@ const INT32 gcl_full[num_gcl_full] = { }; */ +INT32 G_GetDevicePlayer(INT32 deviceID) +{ + INT32 i; + + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + if (deviceID == cv_usejoystick[i].value) + { + return i; + } + } + + return -1; +} + // // Remaps the inputs to game controls. // @@ -77,18 +92,8 @@ const INT32 gcl_full[num_gcl_full] = { void G_MapEventsToControls(event_t *ev) { INT32 i; - INT32 devicePlayer = INT32_MAX; - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) - { - if (ev->device == cv_usejoystick[i].value) - { - devicePlayer = i; - break; - } - } - - if (ev->device >= 0 && ev->device <= MAXGAMEPADS) + if (ev->device >= 0 && ev->device < MAXDEVICES) { switch (ev->type) { @@ -103,8 +108,7 @@ void G_MapEventsToControls(event_t *ev) break; } } - - if (devicePlayer == INT32_MAX) + else { return; } @@ -114,7 +118,7 @@ void G_MapEventsToControls(event_t *ev) case ev_keydown: if (ev->data1 < NUMINPUTS) { - gamekeydown[devicePlayer][ev->data1] = FRACUNIT; + gamekeydown[ev->device][ev->data1] = FRACUNIT; } #ifdef PARANOIA else @@ -127,7 +131,7 @@ void G_MapEventsToControls(event_t *ev) case ev_keyup: if (ev->data1 < NUMINPUTS) { - gamekeydown[devicePlayer][ev->data1] = 0; + gamekeydown[ev->device][ev->data1] = 0; } #ifdef PARANOIA else @@ -147,28 +151,28 @@ void G_MapEventsToControls(event_t *ev) if (ev->data2 < 0) { // Left - gamekeydown[devicePlayer][KEY_MOUSEMOVE + 2] = abs(ev->data2); - gamekeydown[devicePlayer][KEY_MOUSEMOVE + 3] = 0; + gamekeydown[ev->device][KEY_MOUSEMOVE + 2] = abs(ev->data2); + gamekeydown[ev->device][KEY_MOUSEMOVE + 3] = 0; } else { // Right - gamekeydown[devicePlayer][KEY_MOUSEMOVE + 2] = 0; - gamekeydown[devicePlayer][KEY_MOUSEMOVE + 3] = abs(ev->data2); + gamekeydown[ev->device][KEY_MOUSEMOVE + 2] = 0; + gamekeydown[ev->device][KEY_MOUSEMOVE + 3] = abs(ev->data2); } // Y axis if (ev->data3 < 0) { // Up - gamekeydown[devicePlayer][KEY_MOUSEMOVE] = abs(ev->data3); - gamekeydown[devicePlayer][KEY_MOUSEMOVE + 1] = 0; + gamekeydown[ev->device][KEY_MOUSEMOVE] = abs(ev->data3); + gamekeydown[ev->device][KEY_MOUSEMOVE + 1] = 0; } else { // Down - gamekeydown[devicePlayer][KEY_MOUSEMOVE] = 0; - gamekeydown[devicePlayer][KEY_MOUSEMOVE + 1] = abs(ev->data3); + gamekeydown[ev->device][KEY_MOUSEMOVE] = 0; + gamekeydown[ev->device][KEY_MOUSEMOVE + 1] = abs(ev->data3); } break; @@ -200,14 +204,14 @@ void G_MapEventsToControls(event_t *ev) if (ev->data2 < 0) { // Left - gamekeydown[devicePlayer][KEY_AXIS1 + i] = abs(ev->data2); - gamekeydown[devicePlayer][KEY_AXIS1 + i + 1] = 0; + gamekeydown[ev->device][KEY_AXIS1 + i] = abs(ev->data2); + gamekeydown[ev->device][KEY_AXIS1 + i + 1] = 0; } else { // Right - gamekeydown[devicePlayer][KEY_AXIS1 + i] = 0; - gamekeydown[devicePlayer][KEY_AXIS1 + i + 1] = abs(ev->data2); + gamekeydown[ev->device][KEY_AXIS1 + i] = 0; + gamekeydown[ev->device][KEY_AXIS1 + i + 1] = abs(ev->data2); } } @@ -217,14 +221,14 @@ void G_MapEventsToControls(event_t *ev) if (ev->data3 < 0) { // Up - gamekeydown[devicePlayer][KEY_AXIS1 + i + 2] = abs(ev->data3); - gamekeydown[devicePlayer][KEY_AXIS1 + i + 3] = 0; + gamekeydown[ev->device][KEY_AXIS1 + i + 2] = abs(ev->data3); + gamekeydown[ev->device][KEY_AXIS1 + i + 3] = 0; } else { // Down - gamekeydown[devicePlayer][KEY_AXIS1 + i + 2] = 0; - gamekeydown[devicePlayer][KEY_AXIS1 + i + 3] = abs(ev->data3); + gamekeydown[ev->device][KEY_AXIS1 + i + 2] = 0; + gamekeydown[ev->device][KEY_AXIS1 + i + 3] = abs(ev->data3); } } diff --git a/src/g_input.h b/src/g_input.h index 8a41267c5..5739e097e 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -84,9 +84,8 @@ extern consvar_t cv_controlperkey; // current state of the keys: JOYAXISRANGE or 0 when boolean. // Or anything inbetween for analog values -extern INT32 gamekeydown[MAXSPLITSCREENPLAYERS][NUMINPUTS]; - #define MAXDEVICES (MAXGAMEPADS + 1) // Gamepads + keyboard & mouse +extern INT32 gamekeydown[MAXDEVICES][NUMINPUTS]; extern boolean deviceResponding[MAXDEVICES]; // several key codes (or virtual key) per game control @@ -114,6 +113,8 @@ extern const INT32 gcl_full[num_gcl_full]; // peace to my little coder fingers! // check a gamecontrol being active or not +INT32 G_GetDevicePlayer(INT32 deviceID); + // remaps the input event to a game control. void G_MapEventsToControls(event_t *ev); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index f408fea3f..472e3a09e 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1931,7 +1931,8 @@ void M_CharacterSelectInit(INT32 choice) for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { // Un-set all devices upon entering this menu. - cv_usejoystick[i].value = -1; + CV_SetValue(&cv_usejoystick[i], -1); + CONS_Printf("Device for %d set to %d\n", i, -1); } PLAY_CharSelectDef.prevMenu = currentMenu; @@ -2047,14 +2048,16 @@ static void M_HandlePressStart(setup_player_t *p, UINT8 num) if (M_DeviceAvailable(i, setup_numplayers) == true) { // Available!! Let's use this one!! - cv_usejoystick[setup_numplayers].value = i; + CV_SetValue(&cv_usejoystick[setup_numplayers], i); + CONS_Printf("Device for %d set to %d\n", setup_numplayers, i); for (j = setup_numplayers+1; j < MAXSPLITSCREENPLAYERS; j++) { if (cv_usejoystick[j].value == i) { // Un-set devices for other players. - cv_usejoystick[j].value = -1; + CV_SetValue(&cv_usejoystick[j], -1); + CONS_Printf("Device for %d set to %d\n", j, -1); } } @@ -2144,7 +2147,7 @@ static void M_HandleCharRotate(setup_player_t *p, UINT8 num) { UINT8 numclones = setup_chargrid[p->gridx][p->gridy].numskins; - if (G_PlayerInputDown(num, gc_right, true) == true) + if (menucmd[num].dpad_lr > 0) { p->clonenum++; if (p->clonenum >= numclones) @@ -2153,7 +2156,7 @@ static void M_HandleCharRotate(setup_player_t *p, UINT8 num) p->delay = CSROTATETICS; S_StartSound(NULL, sfx_s3kc3s); } - else if (G_PlayerInputDown(num, gc_left, true) == true) + else if (menucmd[num].dpad_lr < 0) { p->clonenum--; if (p->clonenum < 0) @@ -2163,13 +2166,13 @@ static void M_HandleCharRotate(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_s3kc3s); } - if (G_PlayerInputDown(num, gc_a, true) == true || G_PlayerInputDown(num, gc_start, true) == true) + if ((menucmd[num].buttons & MBT_A) || (menucmd[num].buttons & MBT_X) /*|| (menucmd[num].buttons & MBT_START)*/) { p->mdepth = CSSTEP_COLORS; S_StartSound(NULL, sfx_s3k63); M_SetMenuDelay(num); } - else if (G_PlayerInputDown(num, gc_b, true) == true) + else if ((menucmd[num].buttons & MBT_B) || (menucmd[num].buttons & MBT_Y)) { p->mdepth = CSSTEP_CHARS; S_StartSound(NULL, sfx_s3k5b); @@ -2179,7 +2182,7 @@ static void M_HandleCharRotate(setup_player_t *p, UINT8 num) static void M_HandleColorRotate(setup_player_t *p, UINT8 num) { - if (G_PlayerInputDown(num, gc_right, true) == true) + if (menucmd[num].dpad_lr > 0) { p->color++; if (p->color >= numskincolors) @@ -2188,7 +2191,7 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num) M_SetMenuDelay(num); //CSROTATETICS S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s } - else if (G_PlayerInputDown(num, gc_left, true) == true) + else if (menucmd[num].dpad_lr < 0) { p->color--; if (p->color < 1) @@ -2198,7 +2201,7 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s } - if (G_PlayerInputDown(num, gc_a, true) == true || G_PlayerInputDown(num, gc_start, true) == true) + if ((menucmd[num].buttons & MBT_A) || (menucmd[num].buttons & MBT_X) /*|| (menucmd[num].buttons & MBT_START)*/) { p->mdepth = CSSTEP_READY; p->delay = TICRATE; @@ -2206,7 +2209,7 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_s3k4e); M_SetMenuDelay(num); } - else if (G_PlayerInputDown(num, gc_b, true) == true) + else if ((menucmd[num].buttons & MBT_B) || (menucmd[num].buttons & MBT_Y)) { if (setup_chargrid[p->gridx][p->gridy].numskins == 1) p->mdepth = CSSTEP_CHARS; // Skip clones menu From 479292543afbeaad48f3dd69a6e490a09e277742 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 28 Dec 2021 05:09:57 -0500 Subject: [PATCH 063/379] Let me get in game in single player For testing... please... --- src/d_netcmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 775cf8598..e27f9e1a4 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2559,7 +2559,7 @@ static void Command_Map_f(void) mustmodifygame = !(netgame || multiplayer) && !majormods; - if (mustmodifygame) + if (mustmodifygame && !option_force) { /* May want to be more descriptive? */ CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n")); From c49a5dd0cff2f5ce5bd18bd6f00ba6c7978c1c67 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 28 Dec 2021 05:19:04 -0500 Subject: [PATCH 064/379] Properly only accept menu controls when its active --- src/g_input.h | 2 +- src/k_menufunc.c | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/g_input.h b/src/g_input.h index 5739e097e..97290eb89 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -38,7 +38,7 @@ typedef enum KEY_HAT1 = KEY_JOY1 + JOYBUTTONS, KEY_AXIS1 = KEY_HAT1 + JOYHATS*4, - KEY_MOUSE1 = KEY_AXIS1 + JOYAXISSET*4, + KEY_MOUSE1 = KEY_AXIS1 + JOYAXISSET*2*2, // 4 sets of 2 axes, each with positive & negative KEY_MOUSEMOVE = KEY_MOUSE1 + MOUSEBUTTONS, KEY_MOUSEWHEELUP = KEY_MOUSEMOVE + 4, KEY_MOUSEWHEELDOWN = KEY_MOUSEWHEELUP + 1, diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 472e3a09e..009448558 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -862,7 +862,7 @@ boolean M_Responder(event_t *ev) } #endif - if (G_PlayerInputDown(0, gc_start, true) == true) + if (CON_Ready() == false && G_PlayerInputDown(0, gc_start, true) == true) { if (chat_on) { @@ -1516,17 +1516,20 @@ void M_Ticker(void) } } - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + if (menuactive == true) { - if (menucmd[i].delay > 0) + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { - menucmd[i].delay--; + if (menucmd[i].delay > 0) + { + menucmd[i].delay--; + } } - } - if (noFurtherInput == false) - { - M_HandleMenuInput(); + if (noFurtherInput == false) + { + M_HandleMenuInput(); + } } if (currentMenu->tickroutine) From 746f112bf76456896755346459c4ffe33d5e1bcf Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 28 Dec 2021 05:31:14 -0500 Subject: [PATCH 065/379] Fix gamepad joystick handling --- src/g_input.c | 42 ++++++++---------------------------------- src/sdl/i_video.c | 14 ++------------ 2 files changed, 10 insertions(+), 46 deletions(-) diff --git a/src/g_input.c b/src/g_input.c index 00f6ac5a0..de35690b3 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -92,6 +92,7 @@ INT32 G_GetDevicePlayer(INT32 deviceID) void G_MapEventsToControls(event_t *ev) { INT32 i; + boolean alternate = false; if (ev->device >= 0 && ev->device < MAXDEVICES) { @@ -142,11 +143,6 @@ void G_MapEventsToControls(event_t *ev) break; case ev_mouse: // buttons are virtual keys - if (menuactive) - { - break; - } - // X axis if (ev->data2 < 0) { @@ -177,11 +173,6 @@ void G_MapEventsToControls(event_t *ev) break; case ev_joystick: // buttons are virtual keys - if (menuactive) - { - break; - } - if (ev->data1 >= JOYAXISSET) { #ifdef PARANOIA @@ -190,18 +181,13 @@ void G_MapEventsToControls(event_t *ev) break; } - i = ev->data1 * 4; - - if (ev->device == 0) - { - if (CON_Ready() || chat_on) - break; - } + alternate = ev->data1 % 2; + i = (ev->data1 / 2) * 2; + CONS_Printf("AXIS ID IS %d\n", i); if (ev->data2 != INT32_MAX) { - // X axis - if (ev->data2 < 0) + if (alternate == true) { // Left gamekeydown[ev->device][KEY_AXIS1 + i] = abs(ev->data2); @@ -214,22 +200,10 @@ void G_MapEventsToControls(event_t *ev) gamekeydown[ev->device][KEY_AXIS1 + i + 1] = abs(ev->data2); } } - - if (ev->data3 != INT32_MAX) + else { - // Y axis - if (ev->data3 < 0) - { - // Up - gamekeydown[ev->device][KEY_AXIS1 + i + 2] = abs(ev->data3); - gamekeydown[ev->device][KEY_AXIS1 + i + 3] = 0; - } - else - { - // Down - gamekeydown[ev->device][KEY_AXIS1 + i + 2] = 0; - gamekeydown[ev->device][KEY_AXIS1 + i + 3] = abs(ev->data3); - } + gamekeydown[ev->device][KEY_AXIS1 + i] = 0; + gamekeydown[ev->device][KEY_AXIS1 + i + 1] = 0; } break; diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index c8277500f..a5f22a87b 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -792,18 +792,8 @@ static void Impl_HandleJoystickAxisEvent(SDL_JoyAxisEvent evt) return; } - //vaule[sic] - if (evt.axis%2) - { - event.data1 = evt.axis / 2; - event.data2 = SDLJoyAxis(evt.value, event.type, event.device); - } - else - { - evt.axis--; - event.data1 = evt.axis / 2; - event.data3 = SDLJoyAxis(evt.value, event.type, event.device); - } + event.data1 = evt.axis; + event.data2 = SDLJoyAxis(evt.value, event.type, event.device); D_PostEvent(&event); } From 38122e4fa5ecf1d6ada977e43e951d9c2135b3de Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 28 Dec 2021 05:39:50 -0500 Subject: [PATCH 066/379] Fix cursor appearing before P1 has pressed start --- src/k_menudraw.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 8bab9160c..cf49e2669 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1021,8 +1021,11 @@ void M_DrawCharacterSelect(void) M_DrawCharSelectCursor(i); } - // Draw the priority player over the other ones - M_DrawCharSelectCursor(priority); + if (setup_numplayers > 0) + { + // Draw the priority player over the other ones + M_DrawCharSelectCursor(priority); + } } // DIFFICULTY SELECT From 25342af17f30b92089374206d3574ef3de105a59 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 28 Dec 2021 05:48:17 -0500 Subject: [PATCH 067/379] Slight adjustments to cause less misclicks --- src/k_menu.h | 1 + src/k_menufunc.c | 14 ++++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 3280eba8a..f59e74099 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -350,6 +350,7 @@ extern INT16 skullAnimCounter; // skull animation counter extern INT32 menuKey; // keyboard key pressed for menu #define MENUDELAYTIME 7 +#define MENUMINDELAY 2 typedef enum { diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 009448558..422c85b1f 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1234,7 +1234,8 @@ static void M_UpdateMenuCMD(UINT8 i) if (menucmd[i].dpad_ud == 0 && menucmd[i].dpad_lr == 0 && menucmd[i].buttons == 0) { // Reset delay count with no buttons. - menucmd[i].delay = menucmd[i].delayCount = 0; + menucmd[i].delay = min(menucmd[i].delay, MENUMINDELAY); + menucmd[i].delayCount = 0; } } @@ -2034,8 +2035,7 @@ static void M_HandlePressStart(setup_player_t *p, UINT8 num) if (num != setup_numplayers) { - // Only detect devices for the last player... - // just too complicated otherwise. + // Only detect devices for the last player. return; } @@ -2062,15 +2062,17 @@ static void M_HandlePressStart(setup_player_t *p, UINT8 num) CV_SetValue(&cv_usejoystick[j], -1); CONS_Printf("Device for %d set to %d\n", j, -1); } + + // Prevent excess presses for new players. + setup_player[j].delay = TICRATE; } setup_numplayers++; p->mdepth = CSSTEP_CHARS; S_StartSound(NULL, sfx_s3k65); - // Prevent excess presses - memset(deviceResponding, false, sizeof (deviceResponding)); - + // Prevent quick presses + p->delay = MENUDELAYTIME; M_SetMenuDelay(num); } } From f0a3dc74bb06687a01cbfdc6af1f2cbffbe64bab Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 28 Dec 2021 06:48:53 -0500 Subject: [PATCH 068/379] Fix control saving --- src/g_input.c | 250 +++++++++++++---------------------------------- src/g_input.h | 2 +- src/k_menufunc.c | 11 ++- 3 files changed, 74 insertions(+), 189 deletions(-) diff --git a/src/g_input.c b/src/g_input.c index de35690b3..ec3ccf846 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -398,6 +398,10 @@ static const char *gamecontrolname[num_gamecontrols] = "start", "abc", "console", + "talk", + "teamtalk", + "screenshot", + "recordgif", }; #define NUMKEYNAMES (sizeof (keynames)/sizeof (keyname_t)) @@ -538,8 +542,7 @@ void G_SaveKeySetting(FILE *f, INT32 (*fromcontrolsa)[MAXINPUTMAPPING], INT32 (* // TODO: would be nice to get rid of this code duplication for (i = 1; i < num_gamecontrols; i++) { - fprintf(f, "setcontrol \"%s\" \"%s\"", gamecontrolname[i], - G_KeynumToString(fromcontrolsa[i][0])); + fprintf(f, "setcontrol \"%s\" \"%s\"", gamecontrolname[i], G_KeynumToString(fromcontrolsa[i][0])); for (j = 1; j < MAXINPUTMAPPING+1; j++) { @@ -613,21 +616,23 @@ void G_SaveKeySetting(FILE *f, INT32 (*fromcontrolsa)[MAXINPUTMAPPING], INT32 (* } } -INT32 G_CheckDoubleUsage(INT32 keynum, boolean modify) +INT32 G_CheckDoubleUsage(INT32 keynum, INT32 playernum, boolean modify) { INT32 result = gc_null; + if (cv_controlperkey.value == 1) { - INT32 i, j, k; + INT32 i, j; for (i = 0; i < num_gamecontrols; i++) { - for (j = 0; j < 2; j++) + for (j = 0; j < MAXINPUTMAPPING; j++) { - for (k = 0; k < MAXSPLITSCREENPLAYERS; k++) + if (gamecontrol[playernum][i][j] == keynum) { - if (gamecontrol[k][i][j] == keynum) { - result = i; - if (modify) gamecontrol[k][i][j] = KEY_NULL; + result = i; + if (modify) + { + gamecontrol[playernum][i][j] = KEY_NULL; } } @@ -636,191 +641,68 @@ INT32 G_CheckDoubleUsage(INT32 keynum, boolean modify) } } } + return result; } -static INT32 G_FilterKeyByVersion(INT32 numctrl, INT32 keyidx, INT32 player, INT32 *keynum1, INT32 *keynum2, boolean *nestedoverride) -{ - // Special case: ignore KEY_PAUSE because it's hardcoded - if (keyidx == 0 && *keynum1 == KEY_PAUSE) - { - if (*keynum2 != KEY_PAUSE) - { - *keynum1 = *keynum2; // shift down keynum2 and continue - *keynum2 = 0; - } - else - return -1; // skip setting control - } - else if (keyidx == 1 && *keynum2 == KEY_PAUSE) - return -1; // skip setting control - -#if 1 - // We don't have changed control defaults yet - (void)numctrl; - (void)player; - (void)nestedoverride; -#else - if (GETMAJOREXECVERSION(cv_execversion.value) < 27 && ( // v2.1.22 - numctrl == gc_weaponnext || numctrl == gc_weaponprev || numctrl == gc_tossflag || - numctrl == gc_spin || numctrl == gc_camreset || numctrl == gc_jump || - numctrl == gc_pause || numctrl == gc_systemmenu || numctrl == gc_camtoggle || - numctrl == gc_screenshot || numctrl == gc_talkkey || numctrl == gc_scores || - numctrl == gc_centerview - )) - { - INT32 keynum = 0, existingctrl = 0; - INT32 defaultkey; - boolean defaultoverride = false; - - // get the default gamecontrol - defaultkey = gamecontrol[player][numctrl][0]; - - // Assign joypad button defaults if there is an open slot. - // At this point, gamecontrol should have the default controls - // (unless LOADCONFIG is being run) - // - // If the player runs SETCONTROL in-game, this block should not be reached - // because EXECVERSION is locked onto the latest version. - if (keyidx == 0 && !*keynum1) - { - if (*keynum2) // push keynum2 down; this is an edge case - { - *keynum1 = *keynum2; - *keynum2 = 0; - keynum = *keynum1; - } - else - { - keynum = defaultkey; - defaultoverride = true; - } - } - else if (keyidx == 1 && (!*keynum2 || (!*keynum1 && *keynum2))) // last one is the same edge case as above - { - keynum = defaultkey; - defaultoverride = true; - } - else // default to the specified keynum - keynum = (keyidx == 1 ? *keynum2 : *keynum1); - - // Did our last call override keynum2? - if (*nestedoverride) - { - defaultoverride = true; - *nestedoverride = false; - } - - // Fill keynum2 with the default control - if (keyidx == 0 && !*keynum2) - { - *keynum2 = defaultkey; - // Tell the next call that this is an override - *nestedoverride = true; - - // if keynum2 already matches keynum1, we probably recursed - // so unset it - if (*keynum1 == *keynum2) - { - *keynum2 = 0; - *nestedoverride = false; - } - } - - // check if the key is being used somewhere else before passing it - // pass it through if it's the same numctrl. This is an edge case -- when using - // LOADCONFIG, gamecontrol is not reset with default. - // - // Also, only check if we're actually overriding, to preserve behavior where - // config'd keys overwrite default keys. - if (defaultoverride) - existingctrl = G_CheckDoubleUsage(keynum, false); - - if (keynum && (!existingctrl || existingctrl == numctrl)) - return keynum; - else if (keyidx == 0 && *keynum2) - { - // try it again and push down keynum2 - *keynum1 = *keynum2; - *keynum2 = 0; - return G_FilterKeyByVersion(numctrl, keyidx, player, keynum1, keynum2, nestedoverride); - // recursion *should* be safe because we only assign keynum2 to a joy default - // and then clear it if we find that keynum1 already has the joy default. - } - else - return 0; - } -#endif - - // All's good, so pass the keynum as-is - if (keyidx == 1) - return *keynum2; - else //if (keyidx == 0) - return *keynum1; -} - -static void setcontrol(INT32 (*gc)[MAXINPUTMAPPING]) +static void setcontrol(UINT8 player) { INT32 numctrl; const char *namectrl; - INT32 keynum, keynum1, keynum2; - INT32 player; - boolean nestedoverride = false; - - if ((void*)gc == (void*)&gamecontrol[3]) - player = 3; - else if ((void*)gc == (void*)&gamecontrol[2]) - player = 2; - else if ((void*)gc == (void*)&gamecontrol[1]) - player = 1; - else - player = 0; + INT32 keynum; + INT32 inputMap = 0; + INT32 i; namectrl = COM_Argv(1); - for (numctrl = 0; numctrl < num_gamecontrols && stricmp(namectrl, gamecontrolname[numctrl]); + for (numctrl = 0; + numctrl < num_gamecontrols && stricmp(namectrl, gamecontrolname[numctrl]); numctrl++) - ; + { ; } + if (numctrl == num_gamecontrols) { CONS_Printf(M_GetText("Control '%s' unknown\n"), namectrl); return; } - keynum1 = G_KeyStringtoNum(COM_Argv(2)); - keynum2 = G_KeyStringtoNum(COM_Argv(3)); - keynum = G_FilterKeyByVersion(numctrl, 0, player, &keynum1, &keynum2, &nestedoverride); - if (keynum >= 0) + for (i = 0; i < MAXINPUTMAPPING; i++) { - (void)G_CheckDoubleUsage(keynum, true); + keynum = G_KeyStringtoNum(COM_Argv(inputMap + 2)); - // if keynum was rejected, try it again with keynum2 - if (!keynum && keynum2) - { - keynum1 = keynum2; // push down keynum2 - keynum2 = 0; - keynum = G_FilterKeyByVersion(numctrl, 0, player, &keynum1, &keynum2, &nestedoverride); - if (keynum >= 0) - (void)G_CheckDoubleUsage(keynum, true); - } - } - - if (keynum >= 0) - gc[numctrl][0] = keynum; - - if (keynum2) - { - keynum = G_FilterKeyByVersion(numctrl, 1, player, &keynum1, &keynum2, &nestedoverride); if (keynum >= 0) { - if (keynum != gc[numctrl][0]) - gc[numctrl][1] = keynum; - else - gc[numctrl][1] = 0; + (void)G_CheckDoubleUsage(keynum, player, true); + + // if keynum was rejected, try it again with the next key. + while (keynum == 0) + { + inputMap++; + if (inputMap >= MAXINPUTMAPPING) + { + break; + } + + keynum = G_KeyStringtoNum(COM_Argv(inputMap + 2)); + + if (keynum >= 0) + { + (void)G_CheckDoubleUsage(keynum, player, true); + } + } + } + + if (keynum >= 0) + { + gamecontrol[player][numctrl][i] = keynum; + } + + inputMap++; + if (inputMap >= MAXINPUTMAPPING) + { + break; } } - else - gc[numctrl][1] = 0; } void Command_Setcontrol_f(void) @@ -829,13 +711,13 @@ void Command_Setcontrol_f(void) na = (INT32)COM_Argc(); - if (na != 3 && na != 4) + if (na < 3 || na > MAXINPUTMAPPING+2) { - CONS_Printf(M_GetText("setcontrol [<2nd keyname>]: set controls for player 1\n")); + CONS_Printf(M_GetText("setcontrol [] [] []: set controls for player 1\n")); return; } - setcontrol(gamecontrol[0]); + setcontrol(0); } void Command_Setcontrol2_f(void) @@ -844,13 +726,13 @@ void Command_Setcontrol2_f(void) na = (INT32)COM_Argc(); - if (na != 3 && na != 4) + if (na < 3 || na > MAXINPUTMAPPING+2) { - CONS_Printf(M_GetText("setcontrol2 [<2nd keyname>]: set controls for player 2\n")); + CONS_Printf(M_GetText("setcontrol2 [] [] []: set controls for player 2\n")); return; } - setcontrol(gamecontrol[1]); + setcontrol(1); } void Command_Setcontrol3_f(void) @@ -859,13 +741,13 @@ void Command_Setcontrol3_f(void) na = (INT32)COM_Argc(); - if (na != 3 && na != 4) + if (na < 3 || na > MAXINPUTMAPPING+2) { - CONS_Printf(M_GetText("setcontrol3 [<2nd keyname>]: set controls for player 3\n")); + CONS_Printf(M_GetText("setcontrol3 [] [] []: set controls for player 3\n")); return; } - setcontrol(gamecontrol[2]); + setcontrol(2); } void Command_Setcontrol4_f(void) @@ -874,11 +756,11 @@ void Command_Setcontrol4_f(void) na = (INT32)COM_Argc(); - if (na != 3 && na != 4) + if (na < 3 || na > MAXINPUTMAPPING+2) { - CONS_Printf(M_GetText("setcontrol4 [<2nd keyname>]: set controls for player 4\n")); + CONS_Printf(M_GetText("setcontrol4 [] [] []: set controls for player 4\n")); return; } - setcontrol(gamecontrol[3]); + setcontrol(3); } diff --git a/src/g_input.h b/src/g_input.h index 97290eb89..38f0f4af3 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -133,6 +133,6 @@ void G_DefineDefaultControls(void); INT32 G_GetControlScheme(INT32 (*fromcontrols)[MAXINPUTMAPPING], const INT32 *gclist, INT32 gclen); void G_CopyControls(INT32 (*setupcontrols)[MAXINPUTMAPPING], INT32 (*fromcontrols)[MAXINPUTMAPPING], const INT32 *gclist, INT32 gclen); void G_SaveKeySetting(FILE *f, INT32 (*fromcontrolsa)[MAXINPUTMAPPING], INT32 (*fromcontrolsb)[MAXINPUTMAPPING], INT32 (*fromcontrolsc)[MAXINPUTMAPPING], INT32 (*fromcontrolsd)[MAXINPUTMAPPING]); -INT32 G_CheckDoubleUsage(INT32 keynum, boolean modify); +INT32 G_CheckDoubleUsage(INT32 keynum, INT32 playernum, boolean modify); #endif diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 422c85b1f..a1b4fe391 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2033,6 +2033,9 @@ static void M_HandlePressStart(setup_player_t *p, UINT8 num) return; } + // Ensure their device is unset + CV_SetValue(&cv_usejoystick[num], -1); + if (num != setup_numplayers) { // Only detect devices for the last player. @@ -2048,13 +2051,13 @@ static void M_HandlePressStart(setup_player_t *p, UINT8 num) continue; } - if (M_DeviceAvailable(i, setup_numplayers) == true) + if (M_DeviceAvailable(i, num) == true) { // Available!! Let's use this one!! - CV_SetValue(&cv_usejoystick[setup_numplayers], i); - CONS_Printf("Device for %d set to %d\n", setup_numplayers, i); + CV_SetValue(&cv_usejoystick[num], i); + CONS_Printf("Device for %d set to %d\n", num, i); - for (j = setup_numplayers+1; j < MAXSPLITSCREENPLAYERS; j++) + for (j = num+1; j < MAXSPLITSCREENPLAYERS; j++) { if (cv_usejoystick[j].value == i) { From 92aff0b57cac96834c113c9972cf6f28d2fb2931 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 28 Dec 2021 08:02:32 -0500 Subject: [PATCH 069/379] Finish some unused SDL hat input support to use events rather than polling all of the time --- src/console.c | 2 +- src/d_netcmd.c | 2 +- src/g_demo.c | 2 +- src/g_game.c | 4 ++ src/hu_stuff.c | 6 +- src/k_menufunc.c | 2 +- src/m_misc.c | 2 +- src/sdl/i_system.c | 144 +-------------------------------------------- src/sdl/i_video.c | 50 ++++++++++------ 9 files changed, 48 insertions(+), 166 deletions(-) diff --git a/src/console.c b/src/console.c index 1403855f9..06f2a6faf 100644 --- a/src/console.c +++ b/src/console.c @@ -915,7 +915,7 @@ boolean CON_Responder(event_t *ev) if (modeattacking || metalrecording || marathonmode) return false; - if (ev->data1 >= KEY_JOY1) // See also: HUD_Responder + if (ev->data1 >= NUMKEYS) // See also: HUD_Responder { INT32 i; for (i = 0; i < num_gamecontrols; i++) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index e27f9e1a4..775cf8598 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2559,7 +2559,7 @@ static void Command_Map_f(void) mustmodifygame = !(netgame || multiplayer) && !majormods; - if (mustmodifygame && !option_force) + if (mustmodifygame) { /* May want to be more descriptive? */ CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n")); diff --git a/src/g_demo.c b/src/g_demo.c index 35da8c034..ce0b0d9fa 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -3839,7 +3839,7 @@ boolean G_DemoTitleResponder(event_t *ev) return true; } - if (ch == KEY_ENTER || ch >= KEY_JOY1) + if (ch == KEY_ENTER || ch >= NUMKEYS) { demo.savemode = DSM_WILLSAVE; return true; diff --git a/src/g_game.c b/src/g_game.c index 481822d07..59a1a92bd 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -718,6 +718,9 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean menu) } } +#if 1 + (void)menu; +#else if (p == 0 && menu == true) { // We don't want menus to become unnavigable if people unbind @@ -742,6 +745,7 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean menu) } } } +#endif return 0; } diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 92ce9fa97..e5b148ccb 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -1097,7 +1097,7 @@ boolean HU_Responder(event_t *ev) // (Unless if you're sharing a keyboard, since you probably establish when you start chatting that you have dibs on it...) // (Ahhh, the good ol days when I was a kid who couldn't afford an extra USB controller...) - if (ev->data1 >= KEY_JOY1) + if (ev->data1 >= NUMKEYS) { INT32 i, j; for (i = 0; i < num_gamecontrols; i++) @@ -1158,7 +1158,7 @@ boolean HU_Responder(event_t *ev) return true; // Ignore non-keyboard keys, except when the talk key is bound - if (ev->data1 >= KEY_JOY1 + if (ev->data1 >= NUMKEYS /*&& (ev->data1 != gamecontrol[0][gc_talkkey][0] && ev->data1 != gamecontrol[0][gc_talkkey][1])*/) return false; @@ -1224,7 +1224,7 @@ boolean HU_Responder(event_t *ev) else if (c == KEY_ESCAPE /*|| ((c == gamecontrol[0][gc_talkkey][0] || c == gamecontrol[0][gc_talkkey][1] || c == gamecontrol[0][gc_teamkey][0] || c == gamecontrol[0][gc_teamkey][1]) - && c >= KEY_JOY1)*/) // If it's not a keyboard key, then the chat button is used as a toggle. + && c >= NUMKEYS)*/) // If it's not a keyboard key, then the chat button is used as a toggle. { chat_on = false; c_input = 0; // reset input cursor diff --git a/src/k_menufunc.c b/src/k_menufunc.c index a1b4fe391..a458945ce 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -790,7 +790,7 @@ boolean M_Responder(event_t *ev) return false; } - if (ev->type == ev_keydown && ev->data1 < KEY_JOY1) + if (ev->type == ev_keydown && ev->data1 < NUMKEYS) { // Record keyboard presses menuKey = ev->data1; diff --git a/src/m_misc.c b/src/m_misc.c index 969c63227..ef9a7c837 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -1760,7 +1760,7 @@ boolean M_ScreenshotResponder(event_t *ev) ch = ev->data1; - if (ch >= KEY_JOY1 && menuactive) // If it's not a keyboard key, then don't allow it in the menus! + if (ch >= NUMKEYS && menuactive) // If it's not a keyboard key, then don't allow it in the menus! return false; if (ch == KEY_F8 /*|| ch == gamecontrol[0][gc_screenshot][0] || ch == gamecontrol[0][gc_screenshot][1]*/) // remappable F8 diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 8c80b9d24..c716c1dc8 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1073,14 +1073,6 @@ void I_UpdateJoystickDeviceIndices(UINT8 excludePlayer) } } -/** \brief Joystick buttons states -*/ -static UINT64 lastjoybuttons[MAXSPLITSCREENPLAYERS] = {0,0,0,0}; - -/** \brief Joystick hats state -*/ -static UINT64 lastjoyhats[MAXSPLITSCREENPLAYERS] = {0,0,0,0}; - /** \brief Shuts down joystick \return void */ @@ -1088,12 +1080,12 @@ void I_ShutdownJoystick(UINT8 index) { INT32 i; event_t event; - event.type=ev_keyup; + + event.device = I_GetJoystickDeviceIndex(JoyInfo[index].dev); + event.type = ev_keyup; event.data2 = 0; event.data3 = 0; - lastjoybuttons[index] = lastjoyhats[index] = 0; - // emulate the up of all joystick buttons for (i=0;i= 0; i--) - { - joybuttons <<= 1; - if (SDL_JoystickGetButton(JoyInfo[index].dev,i)) - joybuttons |= 1; - } - - if (joybuttons != lastjoybuttons[index]) - { - INT64 j = 1; // keep only bits that changed since last time - INT64 newbuttons = joybuttons ^ lastjoybuttons[index]; - lastjoybuttons[index] = joybuttons; - - for (i = 0; i < JOYBUTTONS; i++, j <<= 1) - { - if (newbuttons & j) // button changed state? - { - if (joybuttons & j) - event.type = ev_keydown; - else - event.type = ev_keyup; - event.data1 = KEY_JOY1 + i; - D_PostEvent(&event); - } - } - } -#endif - - for (i = JoyInfo[index].hats - 1; i >= 0; i--) - { - Uint8 hat = SDL_JoystickGetHat(JoyInfo[index].dev, i); - - if (hat & SDL_HAT_UP ) joyhats|=(UINT64)0x1<<(0 + 4*i); - if (hat & SDL_HAT_DOWN ) joyhats|=(UINT64)0x1<<(1 + 4*i); - if (hat & SDL_HAT_LEFT ) joyhats|=(UINT64)0x1<<(2 + 4*i); - if (hat & SDL_HAT_RIGHT) joyhats|=(UINT64)0x1<<(3 + 4*i); - } - - if (joyhats != lastjoyhats[index]) - { - INT64 j = 1; // keep only bits that changed since last time - INT64 newhats = joyhats ^ lastjoyhats[index]; - lastjoyhats[index] = joyhats; - - for (i = 0; i < JOYHATS*4; i++, j <<= 1) - { - if (newhats & j) // hat changed state? - { - if (joyhats & j) - event.type = ev_keydown; - else - event.type = ev_keyup; - event.data1 = KEY_HAT1 + i; - D_PostEvent(&event); - } - } - } - -#if 0 - // send joystick axis positions - event.type = ev_joystick; - - for (i = JOYAXISSET - 1; i >= 0; i--) - { - event.data1 = i; - if (i*2 + 1 <= JoyInfo[index].axises) - axisx = SDL_JoystickGetAxis(JoyInfo[index].dev, i*2 + 0); - else axisx = 0; - if (i*2 + 2 <= JoyInfo[index].axises) - axisy = SDL_JoystickGetAxis(JoyInfo[index].dev, i*2 + 1); - else axisy = 0; - - - // -32768 to 32767 - axisx = axisx/32; - axisy = axisy/32; - - - if (Joystick[index].bGamepadStyle) - { - // gamepad control type, on or off, live or die - if (axisx < -(JOYAXISRANGE/2)) - event.data2 = -1; - else if (axisx > (JOYAXISRANGE/2)) - event.data2 = 1; - else event.data2 = 0; - if (axisy < -(JOYAXISRANGE/2)) - event.data3 = -1; - else if (axisy > (JOYAXISRANGE/2)) - event.data3 = 1; - else event.data3 = 0; - } - else - { - - axisx = JoyInfo[index].scale?((axisx/JoyInfo[index].scale)*JoyInfo[index].scale):axisx; - axisy = JoyInfo[index].scale?((axisy/JoyInfo[index].scale)*JoyInfo[index].scale):axisy; - -#ifdef SDL_JDEADZONE - if (-SDL_JDEADZONE <= axisx && axisx <= SDL_JDEADZONE) axisx = 0; - if (-SDL_JDEADZONE <= axisy && axisy <= SDL_JDEADZONE) axisy = 0; -#endif - - // analog control style , just send the raw data - event.data2 = axisx; // x axis - event.data3 = axisy; // y axis - } - D_PostEvent(&event); - } -#endif -} - /** \brief Open joystick handle \param fname name of joystick diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index a5f22a87b..ae70747b4 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -798,29 +798,51 @@ static void Impl_HandleJoystickAxisEvent(SDL_JoyAxisEvent evt) D_PostEvent(&event); } -#if 0 -static void Impl_HandleJoystickHatEvent(SDL_JoyHatEvent evt) +static void Impl_SendHatEvent(SDL_JoyHatEvent evt, UINT64 hatFlag, UINT8 keyOffset) { event_t event; - SDL_JoystickID joyid[MAXSPLITSCREENPLAYERS]; event.device = 1 + evt.which; - if (event.device == INT32_MAX) { return; } - if (evt.hat >= JOYHATS) + event.data1 = KEY_HAT1 + keyOffset; + + if (evt.hat < JOYHATS) { - return; // ignore hats with too high an index + event.data1 += (evt.hat * 4); + } + else + { + return; } - event.data1 = KEY_HAT1 + (evt.hat*4); + if (evt.value & hatFlag) + { + event.type = ev_keydown; + } + else + { + event.type = ev_keyup; + } - // NOTE: UNFINISHED + SDLJoyRemap(&event); + + if (event.type != ev_console) + { + D_PostEvent(&event); + } +} + +static void Impl_HandleJoystickHatEvent(SDL_JoyHatEvent evt) +{ + Impl_SendHatEvent(evt, SDL_HAT_UP, 0); + Impl_SendHatEvent(evt, SDL_HAT_DOWN, 1); + Impl_SendHatEvent(evt, SDL_HAT_LEFT, 2); + Impl_SendHatEvent(evt, SDL_HAT_RIGHT, 3); } -#endif static void Impl_HandleJoystickButtonEvent(SDL_JoyButtonEvent evt, Uint32 type) { @@ -908,11 +930,9 @@ void I_GetEvent(void) case SDL_JOYAXISMOTION: Impl_HandleJoystickAxisEvent(evt.jaxis); break; -#if 0 case SDL_JOYHATMOTION: - Impl_HandleJoystickHatEvent(evt.jhat) + Impl_HandleJoystickHatEvent(evt.jhat); break; -#endif case SDL_JOYBUTTONUP: case SDL_JOYBUTTONDOWN: Impl_HandleJoystickButtonEvent(evt.jbutton, evt.type); @@ -1113,16 +1133,12 @@ void I_StartupMouse(void) void I_OsPolling(void) { SDL_Keymod mod; - UINT8 i; if (consolevent) I_GetConsoleEvents(); + if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) - { SDL_JoystickUpdate(); - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) - I_GetJoystickEvents(i); - } I_GetEvent(); From b1d36496b22976c9484674cb64675f7179e9c88b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 28 Dec 2021 08:44:20 -0500 Subject: [PATCH 070/379] Properly implement joystick axes NOW it's fully navigable with controller --- src/g_game.c | 8 +++---- src/g_input.c | 28 ++++++++++++++--------- src/k_menufunc.c | 12 ++++------ src/sdl/i_system.c | 15 ++++++++----- src/sdl/i_video.c | 55 +++++++++++++++++++++++++++------------------- 5 files changed, 67 insertions(+), 51 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 59a1a92bd..78dc2f885 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -397,10 +397,10 @@ consvar_t cv_kickstartaccel[MAXSPLITSCREENPLAYERS] = { static CV_PossibleValue_t zerotoone_cons_t[] = {{0, "MIN"}, {FRACUNIT, "MAX"}, {0, NULL}}; consvar_t cv_deadzone[MAXSPLITSCREENPLAYERS] = { - CVAR_INIT ("joy_deadzone", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), - CVAR_INIT ("joy2_deadzone", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), - CVAR_INIT ("joy3_deadzone", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), - CVAR_INIT ("joy4_deadzone", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL) + CVAR_INIT ("deadzone", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), + CVAR_INIT ("deadzone2", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), + CVAR_INIT ("deadzone3", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), + CVAR_INIT ("deadzone4", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL) }; // now automatically allocated in D_RegisterClientCommands diff --git a/src/g_input.c b/src/g_input.c index ec3ccf846..04f808dc7 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -92,7 +92,6 @@ INT32 G_GetDevicePlayer(INT32 deviceID) void G_MapEventsToControls(event_t *ev) { INT32 i; - boolean alternate = false; if (ev->device >= 0 && ev->device < MAXDEVICES) { @@ -181,13 +180,12 @@ void G_MapEventsToControls(event_t *ev) break; } - alternate = ev->data1 % 2; - i = (ev->data1 / 2) * 2; - CONS_Printf("AXIS ID IS %d\n", i); + CONS_Printf("AXIS DATA (%d, %d, %d)\n", ev->data1, ev->data2, ev->data3); + i = ev->data1 * 4; if (ev->data2 != INT32_MAX) { - if (alternate == true) + if (ev->data2 < 0) { // Left gamekeydown[ev->device][KEY_AXIS1 + i] = abs(ev->data2); @@ -200,12 +198,22 @@ void G_MapEventsToControls(event_t *ev) gamekeydown[ev->device][KEY_AXIS1 + i + 1] = abs(ev->data2); } } - else - { - gamekeydown[ev->device][KEY_AXIS1 + i] = 0; - gamekeydown[ev->device][KEY_AXIS1 + i + 1] = 0; - } + if (ev->data3 != INT32_MAX) + { + if (ev->data3 < 0) + { + // Up + gamekeydown[ev->device][KEY_AXIS1 + i + 2] = abs(ev->data3); + gamekeydown[ev->device][KEY_AXIS1 + i + 3] = 0; + } + else + { + // Down + gamekeydown[ev->device][KEY_AXIS1 + i + 2] = 0; + gamekeydown[ev->device][KEY_AXIS1 + i + 3] = abs(ev->data3); + } + } break; default: diff --git a/src/k_menufunc.c b/src/k_menufunc.c index a458945ce..5a2ac0eb6 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1932,13 +1932,6 @@ void M_CharacterSelectInit(INT32 choice) } } - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) - { - // Un-set all devices upon entering this menu. - CV_SetValue(&cv_usejoystick[i], -1); - CONS_Printf("Device for %d set to %d\n", i, -1); - } - PLAY_CharSelectDef.prevMenu = currentMenu; M_SetupNextMenu(&PLAY_CharSelectDef, false); } @@ -2034,7 +2027,10 @@ static void M_HandlePressStart(setup_player_t *p, UINT8 num) } // Ensure their device is unset - CV_SetValue(&cv_usejoystick[num], -1); + if (cv_usejoystick[num].value != -1) + { + CV_SetValue(&cv_usejoystick[num], -1); + } if (num != setup_numplayers) { diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index c716c1dc8..bb2e4ea21 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1078,7 +1078,7 @@ void I_UpdateJoystickDeviceIndices(UINT8 excludePlayer) */ void I_ShutdownJoystick(UINT8 index) { - INT32 i; + INT32 i, j; event_t event; event.device = I_GetJoystickDeviceIndex(JoyInfo[index].dev); @@ -1087,22 +1087,25 @@ void I_ShutdownJoystick(UINT8 index) event.data3 = 0; // emulate the up of all joystick buttons - for (i=0;i (JOYAXISRANGE/2)) - raxis = 1; - else - raxis = 0; - } + // gamepad control type, on or off, live or die + if (raxis < -(JOYAXISRANGE/2)) + raxis = -1; + else if (raxis > (JOYAXISRANGE/2)) + raxis = 1; else - { - raxis = JoyInfo[pid].scale!=1?((raxis/JoyInfo[pid].scale)*JoyInfo[pid].scale):raxis; + raxis = 0; + } + else + { + raxis = (JoyInfo[pid].scale != 1) ? ((raxis / JoyInfo[pid].scale) * JoyInfo[pid].scale) : raxis; #ifdef SDL_JDEADZONE - if (-SDL_JDEADZONE <= raxis && raxis <= SDL_JDEADZONE) - raxis = 0; + if (-SDL_JDEADZONE <= raxis && raxis <= SDL_JDEADZONE) + raxis = 0; #endif - } } return raxis; @@ -777,23 +774,35 @@ static void Impl_HandleJoystickAxisEvent(SDL_JoyAxisEvent evt) { event_t event; - event.data1 = event.data2 = event.data3 = INT32_MAX; - event.device = 1 + evt.which; - evt.axis++; + event.type = ev_joystick; + event.device = 1 + evt.which; if (event.device == INT32_MAX) { return; } + evt.axis++; + event.data1 = event.data2 = event.data3 = INT32_MAX; + //axis if (evt.axis > JOYAXISSET*2) { return; } - event.data1 = evt.axis; - event.data2 = SDLJoyAxis(evt.value, event.type, event.device); + //vaule[sic] + if (evt.axis % 2) + { + event.data1 = evt.axis / 2; + event.data2 = SDLJoyAxis(evt.value, 0); // TODO: replace 0 with pid + } + else + { + evt.axis--; + event.data1 = evt.axis / 2; + event.data3 = SDLJoyAxis(evt.value, 0); // TODO: replace 0 with pid + } D_PostEvent(&event); } From 3890c06a4d09e21274f8a4e69bf2cbc5bb59ada7 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 28 Dec 2021 08:54:23 -0500 Subject: [PATCH 071/379] Fix number of players counter breaking when entering as player 1 & then exiting --- src/k_menufunc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 5a2ac0eb6..d101535e6 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2270,12 +2270,13 @@ boolean M_CharacterSelectHandler(INT32 choice) } // Setup new numplayers + setup_numplayers = 0; for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { if (setup_player[i].mdepth == CSSTEP_NONE) break; - else - setup_numplayers = i+1; + + setup_numplayers = i+1; } return true; From 0f218b285ec461b9d7fe7b39b6215904a92b9f83 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 28 Dec 2021 09:02:46 -0500 Subject: [PATCH 072/379] Fix menu multiplayer breaking again --- src/g_game.c | 5 ----- src/g_input.c | 1 - 2 files changed, 6 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 78dc2f885..d25828eb4 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -693,11 +693,6 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean menu) return 0; } - if (p > splitscreen) - { - return 0; - } - deadzone = (JOYAXISRANGE * cv_deadzone[p].value) / FRACUNIT; for (i = 0; i < MAXINPUTMAPPING; i++) diff --git a/src/g_input.c b/src/g_input.c index 04f808dc7..3ecc35724 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -180,7 +180,6 @@ void G_MapEventsToControls(event_t *ev) break; } - CONS_Printf("AXIS DATA (%d, %d, %d)\n", ev->data1, ev->data2, ev->data3); i = ev->data1 * 4; if (ev->data2 != INT32_MAX) From 01794661075ff99148706cc85eb1dac83b2c61c9 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 28 Dec 2021 12:02:31 -0500 Subject: [PATCH 073/379] Finalize char select multiplayer - Add menu control fallbacks. - If it could not find a bind using your existing keys, then it looks at default controls. - If it could not find it then, and you're P1, then it looks through gamepads, and then lastly settles for keyboard. - Changed around the order of operations on the character select menu, to accommodate for this change. - Added initroutine to menu_t, which is called every time without question when going to a new menu. This solves many, many minor bugs you could experience in the character select menu when changing between menus, due to things only being properly reset when selecting the character select menu option. --- src/d_clisrv.c | 6 +- src/f_finale.c | 2 + src/g_game.c | 97 +++++++++---- src/g_game.h | 4 +- src/k_menu.h | 7 +- src/k_menudef.c | 36 ++++- src/k_menufunc.c | 356 ++++++++++++++++++----------------------------- src/p_tick.c | 2 +- src/y_inter.c | 8 +- 9 files changed, 253 insertions(+), 265 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index b4055042e..6bf1983e3 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1503,7 +1503,7 @@ static void M_ConfirmConnect(event_t *ev) #ifndef NONET if (ev->type == ev_keydown) { - if (G_PlayerInputDown(0, gc_a, true)) + if (G_PlayerInputDown(0, gc_a, 1)) { if (totalfilesrequestednum > 0) { @@ -1526,7 +1526,7 @@ static void M_ConfirmConnect(event_t *ev) M_ClearMenus(true); } - else if (G_PlayerInputDown(0, gc_b, true)) + else if (G_PlayerInputDown(0, gc_b, 1)) { cl_mode = CL_ABORTED; M_ClearMenus(true); @@ -1934,7 +1934,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic if (cl_mode == CL_CONFIRMCONNECT) D_ProcessEvents(); //needed for menu system to receive inputs - if (G_PlayerInputDown(0, gc_b, true) || cl_mode == CL_ABORTED) + if (G_PlayerInputDown(0, gc_b, 1) || cl_mode == CL_ABORTED) { CONS_Printf(M_GetText("Network game synchronization aborted.\n")); // M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING); diff --git a/src/f_finale.c b/src/f_finale.c index 6749d259b..8b62206c0 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1788,6 +1788,8 @@ static void F_CacheTitleScreen(void) void F_StartTitleScreen(void) { + setup_numplayers = 0; + if (gamestate != GS_TITLESCREEN && gamestate != GS_WAITINGPLAYERS) { ttuser_count = 0; diff --git a/src/g_game.c b/src/g_game.c index d25828eb4..ab2307b9f 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -652,35 +652,67 @@ INT16 G_SoftwareClipAimingPitch(INT32 *aiming) return (INT16)((*aiming)>>16); } -static INT32 KeyValue(UINT8 p, INT32 key, boolean menu) +static INT32 KeyValue(UINT8 p, INT32 key, UINT8 menuPlayers) { INT32 deviceID; + INT32 i, j; if (key <= 0 || key >= NUMINPUTS) { return 0; } - if (menu == false) + deviceID = cv_usejoystick[p].value; + + if (menuPlayers > 0) + { + // Try every device that does NOT belong to another player. + for (i = MAXDEVICES-1; i >= 0; i--) + { + if (i == deviceID) + { + // We've tried this one multiple times :V + continue; + } + + if (menuPlayers > 1) + { + for (j = 1; j < menuPlayers; j++) + { + if (i == cv_usejoystick[j].value) + { + break; + } + } + + if (j < menuPlayers) + { + // This one's taken. + continue; + } + } + + if (gamekeydown[i][key] != 0) + { + return gamekeydown[i][key]; + } + } + } + else { - deviceID = cv_usejoystick[p].value; if (deviceID < 0 || deviceID >= MAXDEVICES) { + // Device is unset return 0; } return gamekeydown[deviceID][key]; } - else - { - // Use keyboard as alternative for P1 menu. - return gamekeydown[0][key]; - } return 0; } -INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean menu) +INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers) { INT32 i; INT32 deadzone = 0; @@ -713,15 +745,12 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean menu) } } -#if 1 - (void)menu; -#else - if (p == 0 && menu == true) + if (menuPlayers != 0) { // We don't want menus to become unnavigable if people unbind - // all of their controls, so use the default control scheme in - // this scenario. + // all of their controls, so we do several things in this scenario. + // First: check the same device, but with default binds. for (i = 0; i < MAXINPUTMAPPING; i++) { INT32 key = gamecontroldefault[gc][i]; @@ -732,22 +761,34 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean menu) continue; } - value = KeyValue(p, key, true); + value = KeyValue(p, key, false); if (value >= deadzone) { return value; } + + if (p == 0 && menuPlayers == 1) + { + // Second: if we're Player 1 and there are no other players, + // then we can use keyboard defaults as a final resort. + + value = KeyValue(p, key, menuPlayers); + + if (value >= deadzone) + { + return value; + } + } } } -#endif return 0; } -boolean G_PlayerInputDown(UINT8 p, INT32 gc, boolean menu) +boolean G_PlayerInputDown(UINT8 p, INT32 gc, UINT8 menuPlayers) { - return (G_PlayerInputAnalog(p, gc, menu) != 0); + return (G_PlayerInputAnalog(p, gc, menuPlayers) != 0); } // Take a magnitude of two axes, and adjust it to take out the deadzone @@ -867,7 +908,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) return; } - joystickvector.xaxis = G_PlayerInputAnalog(forplayer, gc_right, false) - G_PlayerInputAnalog(forplayer, gc_left, false); + joystickvector.xaxis = G_PlayerInputAnalog(forplayer, gc_right, 0) - G_PlayerInputAnalog(forplayer, gc_left, 0); joystickvector.yaxis = 0; G_HandleAxisDeadZone(forplayer, &joystickvector); @@ -875,7 +916,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // use it for aiming to throw items forward/backward and the vote screen // This mean that the turn axis will still be gradient but up/down will be 0 // until the stick is pushed far enough - joystickvector.yaxis = G_PlayerInputAnalog(forplayer, gc_down, false) - G_PlayerInputAnalog(forplayer, gc_up, false); + joystickvector.yaxis = G_PlayerInputAnalog(forplayer, gc_down, 0) - G_PlayerInputAnalog(forplayer, gc_up, 0); if (encoremode) { @@ -892,12 +933,12 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) if (player->spectator || objectplacing) // SRB2Kart: spectators need special controls { - if (G_PlayerInputDown(forplayer, gc_a, false)) + if (G_PlayerInputDown(forplayer, gc_a, 0)) { cmd->buttons |= BT_ACCELERATE; } - if (G_PlayerInputDown(forplayer, gc_b, false)) + if (G_PlayerInputDown(forplayer, gc_b, 0)) { cmd->buttons |= BT_BRAKE; } @@ -915,14 +956,14 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) else { // forward with key or button // SRB2kart - we use an accel/brake instead of forward/backward. - fixed_t value = G_PlayerInputAnalog(forplayer, gc_a, false); + fixed_t value = G_PlayerInputAnalog(forplayer, gc_a, 0); if (value != 0) { cmd->buttons |= BT_ACCELERATE; forward += ((value * MAXPLMOVE) >> 10); } - value = G_PlayerInputAnalog(forplayer, gc_b, false); + value = G_PlayerInputAnalog(forplayer, gc_b, 0); if (value != 0) { cmd->buttons |= BT_BRAKE; @@ -942,19 +983,19 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) } // fire with any button/key - if (G_PlayerInputDown(forplayer, gc_c, false)) + if (G_PlayerInputDown(forplayer, gc_c, 0)) { cmd->buttons |= BT_ATTACK; } // drift with any button/key - if (G_PlayerInputDown(forplayer, gc_x, false)) + if (G_PlayerInputDown(forplayer, gc_x, 0)) { cmd->buttons |= BT_DRIFT; } // rear view with any button/key - if (G_PlayerInputDown(forplayer, gc_y, false)) + if (G_PlayerInputDown(forplayer, gc_y, 0)) { cmd->buttons |= BT_LOOKBACK; } diff --git a/src/g_game.h b/src/g_game.h index 5f66b6ff6..0e249cf63 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -88,8 +88,8 @@ INT16 G_SoftwareClipAimingPitch(INT32 *aiming); extern angle_t localangle[MAXSPLITSCREENPLAYERS]; extern INT32 localaiming[MAXSPLITSCREENPLAYERS]; // should be an angle_t but signed -INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean menu); -boolean G_PlayerInputDown(UINT8 p, INT32 gc, boolean menu); +INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers); +boolean G_PlayerInputDown(UINT8 p, INT32 gc, UINT8 menuPlayers); // // GAME diff --git a/src/k_menu.h b/src/k_menu.h index f59e74099..9fb09a450 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -134,6 +134,7 @@ typedef struct menu_s void (*drawroutine)(void); // draw routine void (*tickroutine)(void); // ticker routine + void (*initroutine)(void); // called when starting a new menu boolean (*quitroutine)(void); // called before quit a menu return true if we can boolean (*inputroutine)(INT32); // if set, called every frame in the input handler. Returning true overwrites normal input handling. } menu_t; @@ -475,7 +476,8 @@ typedef enum } splitscreencvars_t; extern consvar_t *setup_playercvars[MAXSPLITSCREENPLAYERS][SPLITCV_MAX]; -void M_CharacterSelectInit(INT32 choice); +void M_CharacterSelectInit(void); +void M_CharacterSelect(INT32 choice); boolean M_CharacterSelectHandler(INT32 choice); void M_CharacterSelectTick(void); boolean M_CharacterSelectQuit(void); @@ -783,6 +785,7 @@ void M_DrawAddons(void); M_DrawGenericMenu,\ NULL,\ NULL,\ + NULL,\ NULL\ } @@ -799,6 +802,7 @@ void M_DrawAddons(void); M_DrawKartGamemodeMenu,\ NULL,\ NULL,\ + NULL,\ NULL\ } @@ -814,6 +818,7 @@ void M_DrawAddons(void); M_DrawImageDef,\ NULL,\ NULL,\ + NULL,\ NULL\ } diff --git a/src/k_menudef.c b/src/k_menudef.c index b2ef70cff..aeee90865 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -29,7 +29,7 @@ menuitem_t MainMenu[] = { {IT_STRING | IT_CALL, "Play", "Cut to the chase and start the race!", NULL, - M_CharacterSelectInit, 0, 0}, + M_CharacterSelect, 0, 0}, {IT_STRING | IT_CALL, "Extras", "Check out some bonus features.", "MENUI001", @@ -65,6 +65,7 @@ menu_t PLAY_CharSelectDef = { 0, 0, M_DrawCharacterSelect, M_CharacterSelectTick, + M_CharacterSelectInit, M_CharacterSelectQuit, M_CharacterSelectHandler }; @@ -155,6 +156,7 @@ menu_t PLAY_RaceDifficultyDef = { M_DrawRaceDifficulty, NULL, NULL, + NULL, NULL }; @@ -175,6 +177,7 @@ menu_t PLAY_CupSelectDef = { M_DrawCupSelect, M_CupSelectTick, NULL, + NULL, NULL }; @@ -194,6 +197,7 @@ menu_t PLAY_LevelSelectDef = { M_DrawLevelSelect, M_LevelSelectTick, NULL, + NULL, NULL }; @@ -216,6 +220,7 @@ menu_t PLAY_TimeAttackDef = { M_DrawTimeAttack, NULL, NULL, + NULL, NULL }; @@ -259,6 +264,7 @@ menu_t PLAY_MP_OptSelectDef = { M_DrawMPOptSelect, M_MPOptSelectTick, NULL, + NULL, NULL }; @@ -294,6 +300,7 @@ menu_t PLAY_MP_HostDef = { -1, 1, // 1 frame transition.... This is really just because I don't want the black fade when we press esc, hehe M_DrawMPHost, M_MPOptSelectTick, // This handles the unfolding options + NULL, M_MPResetOpts, NULL }; @@ -330,6 +337,7 @@ menu_t PLAY_MP_JoinIPDef = { -1, 1, // 1 frame transition.... This is really just because I don't want the black fade when we press esc, hehe M_DrawMPJoinIP, M_MPOptSelectTick, // This handles the unfolding options + NULL, M_MPResetOpts, M_JoinIPInputs }; @@ -351,6 +359,7 @@ menu_t PLAY_MP_RoomSelectDef = { M_DrawMPRoomSelect, M_MPRoomSelectTick, NULL, + NULL, NULL }; @@ -395,6 +404,7 @@ menu_t OPTIONS_MainDef = { M_DrawOptions, M_OptionsTick, NULL, + NULL, M_OptionsInputs }; @@ -457,6 +467,7 @@ menu_t OPTIONS_VideoDef = { M_OptionsTick, NULL, NULL, + NULL, }; menuitem_t OPTIONS_VideoModes[] = { @@ -478,6 +489,7 @@ menu_t OPTIONS_VideoModesDef = { M_OptionsTick, NULL, NULL, + NULL, }; #ifdef HWRENDER @@ -536,6 +548,7 @@ menu_t OPTIONS_VideoOGLDef = { M_OptionsTick, NULL, NULL, + NULL, }; #endif @@ -599,6 +612,7 @@ menu_t OPTIONS_SoundDef = { M_OptionsTick, NULL, NULL, + NULL, }; menuitem_t OPTIONS_HUD[] = @@ -654,6 +668,7 @@ menu_t OPTIONS_HUDDef = { M_OptionsTick, NULL, NULL, + NULL, }; menuitem_t OPTIONS_HUDOnline[] = @@ -703,6 +718,7 @@ menu_t OPTIONS_HUDOnlineDef = { M_OptionsTick, NULL, NULL, + NULL, }; @@ -756,6 +772,7 @@ menu_t OPTIONS_GameplayDef = { M_OptionsTick, NULL, NULL, + NULL, }; menuitem_t OPTIONS_GameplayItems[] = @@ -801,6 +818,7 @@ menu_t OPTIONS_GameplayItemsDef = { M_OptionsTick, NULL, NULL, + NULL, }; menuitem_t OPTIONS_Server[] = @@ -865,6 +883,7 @@ menu_t OPTIONS_ServerDef = { M_OptionsTick, NULL, NULL, + NULL, }; #ifndef NONET @@ -923,6 +942,7 @@ menu_t OPTIONS_ServerAdvancedDef = { M_OptionsTick, NULL, NULL, + NULL, }; #endif @@ -964,6 +984,7 @@ menu_t OPTIONS_DataDef = { M_OptionsTick, NULL, NULL, + NULL, }; menuitem_t OPTIONS_DataAddon[] = @@ -1010,6 +1031,7 @@ menu_t OPTIONS_DataAddonDef = { M_OptionsTick, NULL, NULL, + NULL, }; menuitem_t OPTIONS_DataScreenshot[] = @@ -1050,6 +1072,7 @@ menu_t OPTIONS_DataScreenshotDef = { M_OptionsTick, NULL, NULL, + NULL, }; menuitem_t OPTIONS_DataReplay[] = @@ -1076,6 +1099,7 @@ menu_t OPTIONS_DataReplayDef = { M_OptionsTick, NULL, NULL, + NULL, }; #ifdef HAVE_DISCORDRPC @@ -1114,6 +1138,7 @@ menu_t OPTIONS_DataDiscordDef = { M_OptionsTick, NULL, NULL, + NULL, }; #endif @@ -1147,6 +1172,7 @@ menu_t OPTIONS_DataEraseDef = { M_OptionsTick, NULL, NULL, + NULL, }; @@ -1180,6 +1206,7 @@ menu_t EXTRAS_MainDef = { M_DrawExtras, M_ExtrasTick, NULL, + NULL, M_ExtrasInputs }; @@ -1204,6 +1231,7 @@ menu_t EXTRAS_ReplayHutDef = 0, 0, M_DrawReplayHut, NULL, + NULL, M_QuitReplayHut, NULL }; @@ -1236,6 +1264,7 @@ menu_t EXTRAS_ReplayStartDef = M_DrawReplayStartMenu, NULL, NULL, + NULL, NULL }; @@ -1276,7 +1305,7 @@ menuitem_t PAUSE_Main[] = NULL, NULL, 0, 0}, {IT_STRING | IT_CALL, "PLAYER SETUP", "M_ICOCHR", - NULL, M_CharacterSelectInit, 0, 0}, + NULL, M_CharacterSelect, 0, 0}, {IT_STRING | IT_CALL, "OPTIONS", "M_ICOOPT", NULL, M_InitOptions, 0, 0}, @@ -1296,6 +1325,7 @@ menu_t PAUSE_MainDef = { M_DrawPause, M_PauseTick, NULL, + NULL, M_PauseInputs }; @@ -1346,6 +1376,7 @@ menu_t PAUSE_PlaybackMenuDef = { M_DrawPlaybackMenu, NULL, NULL, + NULL, NULL }; @@ -1390,5 +1421,6 @@ menu_t MISC_AddonsDef = { M_DrawAddons, NULL, NULL, + NULL, NULL }; diff --git a/src/k_menufunc.c b/src/k_menufunc.c index d101535e6..7331ac1f2 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -862,7 +862,7 @@ boolean M_Responder(event_t *ev) } #endif - if (CON_Ready() == false && G_PlayerInputDown(0, gc_start, true) == true) + if (CON_Ready() == false && G_PlayerInputDown(0, gc_start, splitscreen + 1) == true) { if (chat_on) { @@ -893,6 +893,13 @@ void M_StartControlPanel(void) { INT32 i; + memset(gamekeydown, 0, sizeof (gamekeydown)); + memset(menucmd, 0, sizeof (menucmd)); + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + menucmd[i].delay = MENUDELAYTIME; + } + // intro might call this repeatedly if (menuactive) { @@ -913,11 +920,6 @@ void M_StartControlPanel(void) menuactive = true; - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) - { - menucmd[i].delay = MENUDELAYTIME; - } - if (demo.playback) { currentMenu = &PAUSE_PlaybackMenuDef; @@ -933,137 +935,6 @@ void M_StartControlPanel(void) M_OpenPauseMenu(); } -#if 0 - else if (modeattacking) - { - currentMenu = &MAPauseDef; - itemOn = mapause_continue; - } - else if (!(netgame || multiplayer)) // Single Player - { - if (gamestate != GS_LEVEL) // intermission, so gray out stuff. - SPauseMenu[spause_retry].status = IT_GRAYEDOUT; - else - { - //INT32 numlives = 2; - - /*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); - } - - 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].mvar1 = 48; - MPauseMenu[mpause_switchspectate].mvar1 = 48; - MPauseMenu[mpause_options].mvar1 = 64; - MPauseMenu[mpause_title].mvar1 = 80; - MPauseMenu[mpause_quit].mvar1 = 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].mvar1 += ((splitscreen+1) * 8); - MPauseMenu[mpause_options].mvar1 += 8; - MPauseMenu[mpause_title].mvar1 += 8; - MPauseMenu[mpause_quit].mvar1 += 8; - } - else if (G_GametypeHasSpectators()) - { - MPauseMenu[mpause_switchspectate].status = IT_STRING | IT_SUBMENU; - MPauseMenu[mpause_switchspectate].mvar1 += ((splitscreen+1) * 8); - MPauseMenu[mpause_options].mvar1 += 8; - MPauseMenu[mpause_title].mvar1 += 8; - MPauseMenu[mpause_quit].mvar1 += 8; - } - } - - if (splitscreen > 1) - { - MPauseMenu[mpause_psetupsplit3].status = IT_STRING | IT_CALL; - - MPauseMenu[mpause_options].mvar1 += 8; - MPauseMenu[mpause_title].mvar1 += 8; - MPauseMenu[mpause_quit].mvar1 += 8; - - if (splitscreen > 2) - { - MPauseMenu[mpause_psetupsplit4].status = IT_STRING | IT_CALL; - MPauseMenu[mpause_options].mvar1 += 8; - MPauseMenu[mpause_title].mvar1 += 8; - MPauseMenu[mpause_quit].mvar1 += 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; - } -#endif - CON_ToggleOff(); // move away console } @@ -1104,11 +975,6 @@ void M_SetupNextMenu(menu_t *menudef, boolean notransition) { INT16 i; - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) - { - menucmd[i].delay = MENUDELAYTIME; - } - if (!notransition) { if (currentMenu->transitionID == menudef->transitionID @@ -1139,6 +1005,16 @@ void M_SetupNextMenu(menu_t *menudef, boolean notransition) return; // we can't quit this menu (also used to set parameter from the menu) } + if (menudef->initroutine != NULL +#if 0 + && currentMenu != menudef // Unsure if we need this... +#endif + ) + { + // Moving to a new menu, reinitialize. + menudef->initroutine(); + } + currentMenu = menudef; itemOn = currentMenu->lastOn; @@ -1209,27 +1085,29 @@ static void M_SetMenuDelay(UINT8 i) static void M_UpdateMenuCMD(UINT8 i) { + UINT8 mp = max(1, setup_numplayers); + menucmd[i].dpad_ud = 0; menucmd[i].dpad_lr = 0; menucmd[i].buttonsHeld = menucmd[i].buttons; menucmd[i].buttons = 0; - if (G_PlayerInputDown(i, gc_up, true)) { menucmd[i].dpad_ud--; } - if (G_PlayerInputDown(i, gc_down, true)) { menucmd[i].dpad_ud++; } + if (G_PlayerInputDown(i, gc_up, mp)) { menucmd[i].dpad_ud--; } + if (G_PlayerInputDown(i, gc_down, mp)) { menucmd[i].dpad_ud++; } - if (G_PlayerInputDown(i, gc_left, true)) { menucmd[i].dpad_lr--; } - if (G_PlayerInputDown(i, gc_right, true)) { menucmd[i].dpad_lr++; } + if (G_PlayerInputDown(i, gc_left, mp)) { menucmd[i].dpad_lr--; } + if (G_PlayerInputDown(i, gc_right, mp)) { menucmd[i].dpad_lr++; } - if (G_PlayerInputDown(i, gc_a, true)) { menucmd[i].buttons |= MBT_A; } - if (G_PlayerInputDown(i, gc_b, true)) { menucmd[i].buttons |= MBT_B; } - if (G_PlayerInputDown(i, gc_c, true)) { menucmd[i].buttons |= MBT_C; } - if (G_PlayerInputDown(i, gc_x, true)) { menucmd[i].buttons |= MBT_X; } - if (G_PlayerInputDown(i, gc_y, true)) { menucmd[i].buttons |= MBT_Y; } - if (G_PlayerInputDown(i, gc_z, true)) { menucmd[i].buttons |= MBT_Z; } - if (G_PlayerInputDown(i, gc_l, true)) { menucmd[i].buttons |= MBT_L; } - if (G_PlayerInputDown(i, gc_r, true)) { menucmd[i].buttons |= MBT_R; } - if (G_PlayerInputDown(i, gc_start, true)) { menucmd[i].buttons |= MBT_START; } + if (G_PlayerInputDown(i, gc_a, mp)) { menucmd[i].buttons |= MBT_A; } + if (G_PlayerInputDown(i, gc_b, mp)) { menucmd[i].buttons |= MBT_B; } + if (G_PlayerInputDown(i, gc_c, mp)) { menucmd[i].buttons |= MBT_C; } + if (G_PlayerInputDown(i, gc_x, mp)) { menucmd[i].buttons |= MBT_X; } + if (G_PlayerInputDown(i, gc_y, mp)) { menucmd[i].buttons |= MBT_Y; } + if (G_PlayerInputDown(i, gc_z, mp)) { menucmd[i].buttons |= MBT_Z; } + if (G_PlayerInputDown(i, gc_l, mp)) { menucmd[i].buttons |= MBT_L; } + if (G_PlayerInputDown(i, gc_r, mp)) { menucmd[i].buttons |= MBT_R; } + if (G_PlayerInputDown(i, gc_start, mp)) { menucmd[i].buttons |= MBT_START; } if (menucmd[i].dpad_ud == 0 && menucmd[i].dpad_lr == 0 && menucmd[i].buttons == 0) { @@ -1262,6 +1140,12 @@ static void M_HandleMenuInput(void) M_UpdateMenuCMD(i); } + if (menuactive == false) + { + // We're not in the menu. + return; + } + // Handle menu-specific input handling. If this returns true, we skip regular input handling. if (currentMenu->inputroutine) { @@ -1385,7 +1269,7 @@ static void M_HandleMenuInput(void) return; } - else if ((menucmd[pid].buttons & MBT_A) || (menucmd[pid].buttons & MBT_X) /*|| (menucmd[pid].buttons & MBT_START)*/) + else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X) /*|| M_MenuButtonPressed(pid, MBT_START)*/) { noFurtherInput = true; currentMenu->lastOn = itemOn; @@ -1424,13 +1308,13 @@ static void M_HandleMenuInput(void) M_SetMenuDelay(pid); return; } - else if ((menucmd[pid].buttons & MBT_B) || (menucmd[pid].buttons & MBT_Y)) + else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) { M_GoBack(0); M_SetMenuDelay(pid); return; } - else if ((menucmd[pid].buttons & MBT_C) || (menucmd[pid].buttons & MBT_Z)) + else if (M_MenuButtonPressed(pid, MBT_C) || M_MenuButtonPressed(pid, MBT_Z)) { if (routine && ((currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_ARROWS || (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_CVAR)) @@ -1517,20 +1401,17 @@ void M_Ticker(void) } } - if (menuactive == true) + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + if (menucmd[i].delay > 0) { - if (menucmd[i].delay > 0) - { - menucmd[i].delay--; - } + menucmd[i].delay--; } + } - if (noFurtherInput == false) - { - M_HandleMenuInput(); - } + if (noFurtherInput == false) + { + M_HandleMenuInput(); } if (currentMenu->tickroutine) @@ -1624,16 +1505,17 @@ static menuitem_t MessageMenu[] = menu_t MessageDef = { - 1, // # of menu items - NULL, // previous menu (TO HACK) - 0, // lastOn, flags (TO HACK) - MessageMenu, // menuitem_t -> - 0, 0, // x, y (TO HACK) + 1, // # of menu items + NULL, // previous menu (TO HACK) + 0, // lastOn, flags (TO HACK) + MessageMenu, // menuitem_t -> + 0, 0, // x, y (TO HACK) 0, 0, // extra1, extra2 - 0, 0, // transition tics - M_DrawMessageMenu, // drawing routine -> - NULL, // ticker routine - NULL, // quit routine + 0, 0, // transition tics + M_DrawMessageMenu, // drawing routine -> + NULL, // ticker routine + NULL, // init routine + NULL, // quit routine NULL // input routine }; @@ -1886,14 +1768,20 @@ struct setup_chargrid_s setup_chargrid[9][9]; setup_player_t setup_player[MAXSPLITSCREENPLAYERS]; struct setup_explosions_s setup_explosions[48]; -UINT8 setup_numplayers = 0; +UINT8 setup_numplayers = 0; // This variable is very important, it was extended to determine how many players exist in ALL menus. tic_t setup_animcounter = 0; -void M_CharacterSelectInit(INT32 choice) +void M_CharacterSelectInit(void) { UINT8 i, j; - (void)choice; + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + // Un-set devices for other players. + CV_SetValue(&cv_usejoystick[i], -1); + CONS_Printf("Device for %d set to %d\n", i, -1); + } + CONS_Printf("========\n"); memset(setup_chargrid, -1, sizeof(setup_chargrid)); for (i = 0; i < 9; i++) @@ -1931,7 +1819,11 @@ void M_CharacterSelectInit(INT32 choice) } } } +} +void M_CharacterSelect(INT32 choice) +{ + (void)choice; PLAY_CharSelectDef.prevMenu = currentMenu; M_SetupNextMenu(&PLAY_CharSelectDef, false); } @@ -1983,7 +1875,7 @@ static void M_SetupReadyExplosions(setup_player_t *p) } } -static boolean M_DeviceAvailable(UINT8 deviceID, UINT8 numPlayers) +static boolean M_DeviceAvailable(INT32 deviceID, UINT8 numPlayers) { INT32 i; @@ -2006,12 +1898,12 @@ static boolean M_DeviceAvailable(UINT8 deviceID, UINT8 numPlayers) return true; } -static void M_HandlePressStart(setup_player_t *p, UINT8 num) +static boolean M_HandlePressStart(setup_player_t *p, UINT8 num) { INT32 i, j; // Detect B press first ... this means P1 can actually exit out of the menu. - if (menucmd[num].buttons & MBT_B) + if (M_MenuButtonPressed(num, MBT_B) || M_MenuButtonPressed(num, MBT_Y)) { M_SetMenuDelay(num); @@ -2019,66 +1911,67 @@ static void M_HandlePressStart(setup_player_t *p, UINT8 num) { // We're done here. M_GoBack(0); - return; + return true; } // Don't allow this press to ever count as "start". - return; - } - - // Ensure their device is unset - if (cv_usejoystick[num].value != -1) - { - CV_SetValue(&cv_usejoystick[num], -1); + return false; } if (num != setup_numplayers) { // Only detect devices for the last player. - return; + return false; } // Now detect new devices trying to join. for (i = 0; i < MAXDEVICES; i++) { - if (deviceResponding[i] == false) + if (deviceResponding[i] != true) { // No buttons are being pushed. continue; } - if (M_DeviceAvailable(i, num) == true) + if (M_DeviceAvailable(i, setup_numplayers) == true) { // Available!! Let's use this one!! CV_SetValue(&cv_usejoystick[num], i); CONS_Printf("Device for %d set to %d\n", num, i); + CONS_Printf("========\n"); for (j = num+1; j < MAXSPLITSCREENPLAYERS; j++) { - if (cv_usejoystick[j].value == i) - { - // Un-set devices for other players. - CV_SetValue(&cv_usejoystick[j], -1); - CONS_Printf("Device for %d set to %d\n", j, -1); - } - - // Prevent excess presses for new players. - setup_player[j].delay = TICRATE; + // Un-set devices for other players. + CV_SetValue(&cv_usejoystick[j], -1); + CONS_Printf("Device for %d set to %d\n", j, -1); } + CONS_Printf("========\n"); - setup_numplayers++; + //setup_numplayers++; p->mdepth = CSSTEP_CHARS; S_StartSound(NULL, sfx_s3k65); - // Prevent quick presses - p->delay = MENUDELAYTIME; - M_SetMenuDelay(num); + // Prevent quick presses for multiple players + for (j = 0; j < MAXSPLITSCREENPLAYERS; j++) + { + setup_player[j].delay = MENUDELAYTIME; + M_SetMenuDelay(j); + menucmd[j].buttonsHeld |= (MBT_B|MBT_Y); + } + + memset(deviceResponding, false, sizeof(deviceResponding)); + return true; } } + + return false; } -static void M_HandleCharacterGrid(setup_player_t *p, UINT8 num) +static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) { + INT32 i; + if (menucmd[num].dpad_ud > 0) { p->gridy++; @@ -2113,7 +2006,7 @@ static void M_HandleCharacterGrid(setup_player_t *p, UINT8 num) M_SetMenuDelay(num); } - if ((menucmd[num].buttons & MBT_A) || (menucmd[num].buttons & MBT_X) /*|| (menucmd[num].buttons & MBT_START)*/) + if (M_MenuButtonPressed(num, MBT_A) || M_MenuButtonPressed(num, MBT_X) /*|| M_MenuButtonPressed(num, MBT_START)*/) { if (setup_chargrid[p->gridx][p->gridy].numskins == 0) { @@ -2131,12 +2024,22 @@ static void M_HandleCharacterGrid(setup_player_t *p, UINT8 num) M_SetMenuDelay(num); } - else if ((menucmd[num].buttons & MBT_B) || (menucmd[num].buttons & MBT_Y)) + else if (M_MenuButtonPressed(num, MBT_B) || M_MenuButtonPressed(num, MBT_Y)) { if (num == setup_numplayers-1) { p->mdepth = CSSTEP_NONE; S_StartSound(NULL, sfx_s3k5b); + + // Prevent quick presses for multiple players + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + setup_player[i].delay = MENUDELAYTIME; + M_SetMenuDelay(i); + menucmd[i].buttonsHeld |= (MBT_B|MBT_Y); + } + + return true; } else { @@ -2145,6 +2048,8 @@ static void M_HandleCharacterGrid(setup_player_t *p, UINT8 num) M_SetMenuDelay(num); } + + return false; } static void M_HandleCharRotate(setup_player_t *p, UINT8 num) @@ -2170,13 +2075,13 @@ static void M_HandleCharRotate(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_s3kc3s); } - if ((menucmd[num].buttons & MBT_A) || (menucmd[num].buttons & MBT_X) /*|| (menucmd[num].buttons & MBT_START)*/) + if (M_MenuButtonPressed(num, MBT_A) || M_MenuButtonPressed(num, MBT_X) /*|| M_MenuButtonPressed(num, MBT_START)*/) { p->mdepth = CSSTEP_COLORS; S_StartSound(NULL, sfx_s3k63); M_SetMenuDelay(num); } - else if ((menucmd[num].buttons & MBT_B) || (menucmd[num].buttons & MBT_Y)) + else if (M_MenuButtonPressed(num, MBT_B) || M_MenuButtonPressed(num, MBT_Y)) { p->mdepth = CSSTEP_CHARS; S_StartSound(NULL, sfx_s3k5b); @@ -2205,7 +2110,7 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s } - if ((menucmd[num].buttons & MBT_A) || (menucmd[num].buttons & MBT_X) /*|| (menucmd[num].buttons & MBT_START)*/) + if (M_MenuButtonPressed(num, MBT_A) || M_MenuButtonPressed(num, MBT_X) /*|| M_MenuButtonPressed(num, MBT_START)*/) { p->mdepth = CSSTEP_READY; p->delay = TICRATE; @@ -2213,7 +2118,7 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_s3k4e); M_SetMenuDelay(num); } - else if ((menucmd[num].buttons & MBT_B) || (menucmd[num].buttons & MBT_Y)) + else if (M_MenuButtonPressed(num, MBT_B) || M_MenuButtonPressed(num, MBT_Y)) { if (setup_chargrid[p->gridx][p->gridy].numskins == 1) p->mdepth = CSSTEP_CHARS; // Skip clones menu @@ -2226,23 +2131,24 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num) boolean M_CharacterSelectHandler(INT32 choice) { - UINT8 i; + INT32 i; (void)choice; - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + for (i = MAXSPLITSCREENPLAYERS-1; i >= 0; i--) { setup_player_t *p = &setup_player[i]; + boolean playersChanged = false; if (p->delay == 0 && menucmd[i].delay == 0) { switch (p->mdepth) { case CSSTEP_NONE: // Enter Game - M_HandlePressStart(p, i); + playersChanged = M_HandlePressStart(p, i); break; case CSSTEP_CHARS: // Character Select grid - M_HandleCharacterGrid(p, i); + playersChanged = M_HandleCharacterGrid(p, i); break; case CSSTEP_ALTS: // Select clone M_HandleCharRotate(p, i); @@ -2252,7 +2158,7 @@ boolean M_CharacterSelectHandler(INT32 choice) break; case CSSTEP_READY: default: // Unready - if (G_PlayerInputDown(i, gc_b, true) == true) + if (M_MenuButtonPressed(i, MBT_B) || M_MenuButtonPressed(i, MBT_Y)) { p->mdepth = CSSTEP_COLORS; S_StartSound(NULL, sfx_s3k5b); @@ -2266,7 +2172,14 @@ boolean M_CharacterSelectHandler(INT32 choice) p->skin = setup_chargrid[p->gridx][p->gridy].skinlist[p->clonenum]; if (p->mdepth < CSSTEP_COLORS) + { p->color = skins[p->skin].prefcolor; + } + + if (playersChanged == true) + { + break; + } } // Setup new numplayers @@ -2359,7 +2272,6 @@ void M_CharacterSelectTick(void) if (setupnext && setup_numplayers > 0) { - // Selecting from the menu if (gamestate == GS_MENU) { @@ -2406,7 +2318,6 @@ void M_CharacterSelectTick(void) boolean M_CharacterSelectQuit(void) { - M_CharacterSelectInit(0); return true; } @@ -3674,9 +3585,6 @@ void M_OpenPauseMenu(void) PAUSE_Main[mpause_entergame].status = IT_STRING | IT_CALL; } } - - - } void M_QuitPauseMenu(void) diff --git a/src/p_tick.c b/src/p_tick.c index 1330eb0f5..bbfbc8e0a 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -684,7 +684,7 @@ void P_Ticker(boolean run) G_WriteAllGhostTics(); if (cv_recordmultiplayerdemos.value && (demo.savemode == DSM_NOTSAVING || demo.savemode == DSM_WILLAUTOSAVE)) - if (demo.savebutton && demo.savebutton + 3*TICRATE < leveltime && G_PlayerInputDown(0, gc_y, false)) + if (demo.savebutton && demo.savebutton + 3*TICRATE < leveltime && G_PlayerInputDown(0, gc_y, 0)) demo.savemode = DSM_TITLEENTRY; } else if (demo.playback) // Use Ghost data for consistency checks. diff --git a/src/y_inter.c b/src/y_inter.c index 1acceda26..cf932fb09 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -619,7 +619,7 @@ void Y_Ticker(void) if (demo.recording) { - if (demo.savemode == DSM_NOTSAVING && G_PlayerInputDown(0, gc_y, false)) + if (demo.savemode == DSM_NOTSAVING && G_PlayerInputDown(0, gc_y, 0)) demo.savemode = DSM_TITLEENTRY; if (demo.savemode == DSM_WILLSAVE || demo.savemode == DSM_WILLAUTOSAVE) @@ -1476,13 +1476,13 @@ void Y_VoteTicker(void) && !voteclient.playerinfo[i].delay && pickedvote == -1 && votes[p] == -1) { - if (G_PlayerInputDown(i, gc_up, false)) + if (G_PlayerInputDown(i, gc_up, 0)) { voteclient.playerinfo[i].selection--; pressed = true; } - if (G_PlayerInputDown(i, gc_down, false) && pressed == false) + if (G_PlayerInputDown(i, gc_down, 0) && pressed == false) { voteclient.playerinfo[i].selection++; pressed = true; @@ -1493,7 +1493,7 @@ void Y_VoteTicker(void) if (voteclient.playerinfo[i].selection > 3) voteclient.playerinfo[i].selection = 0; - if (G_PlayerInputDown(i, gc_a, false) && pressed == false) + if (G_PlayerInputDown(i, gc_a, 0) && pressed == false) { D_ModifyClientVote(consoleplayer, voteclient.playerinfo[i].selection, i); pressed = true; From a7022af93f39a25a3b0c1a0c7ba3a58550bdb184 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 28 Dec 2021 12:27:59 -0500 Subject: [PATCH 074/379] Fix cup select & level select to both use the new input handling systems --- src/k_menufunc.c | 466 ++++++++++++++++++++++++----------------------- 1 file changed, 242 insertions(+), 224 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 7331ac1f2..f422aad5d 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1163,17 +1163,11 @@ static void M_HandleMenuInput(void) routine = currentMenu->menuitems[itemOn].itemaction; // Handle menuitems which need a specific key handling - - /* - // NOPE, we need a generic "typing" menu - // (sort of like the generic message menu) - // so that it can be gamepad friendly. if (routine && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_KEYHANDLER) { routine(-1); return; } - */ // TODO: Move this to message menu code if (currentMenu->menuitems[itemOn].status == IT_MSGHANDLER) @@ -2553,6 +2547,9 @@ void M_LevelSelectInit(INT32 choice) void M_CupSelectHandler(INT32 choice) { cupheader_t *newcup = kartcupheaders; + const UINT8 pid = 0; + + (void)choice; while (newcup) { @@ -2561,119 +2558,130 @@ void M_CupSelectHandler(INT32 choice) newcup = newcup->next; } - switch (choice) + if (menucmd[pid].dpad_lr > 0) { - case KEY_RIGHTARROW: - cupgrid.x++; - if (cupgrid.x >= CUPMENU_COLUMNS) + cupgrid.x++; + if (cupgrid.x >= CUPMENU_COLUMNS) + { + cupgrid.x = 0; + cupgrid.pageno++; + if (cupgrid.pageno >= cupgrid.numpages) + cupgrid.pageno = 0; + } + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + } + else if (menucmd[pid].dpad_lr < 0) + { + cupgrid.x--; + if (cupgrid.x < 0) + { + cupgrid.x = CUPMENU_COLUMNS-1; + cupgrid.pageno--; + if (cupgrid.pageno < 0) + cupgrid.pageno = cupgrid.numpages-1; + } + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + } + + if (menucmd[pid].dpad_ud > 0) + { + cupgrid.y++; + if (cupgrid.y >= CUPMENU_ROWS) + cupgrid.y = 0; + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + } + else if (menucmd[pid].dpad_ud < 0) + { + cupgrid.y--; + if (cupgrid.y < 0) + cupgrid.y = CUPMENU_ROWS-1; + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + } + + if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X) /*|| M_MenuButtonPressed(pid, MBT_START)*/) + { + M_SetMenuDelay(pid); + + if ((!newcup) || (newcup && newcup->unlockrequired != -1 && !unlockables[newcup->unlockrequired].unlocked)) + { + S_StartSound(NULL, sfx_s3kb2); + return; + } + + if (cupgrid.grandprix == true) + { + S_StartSound(NULL, sfx_s3k63); + + // Early fadeout to let the sound finish playing + F_WipeStartScreen(); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeEndScreen(); + F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); + + memset(&grandprixinfo, 0, sizeof(struct grandprixinfo)); + + // read our dummy cvars + + grandprixinfo.gamespeed = min(KARTSPEED_HARD, cv_dummygpdifficulty.value); + grandprixinfo.masterbots = (cv_dummygpdifficulty.value == 3); + grandprixinfo.encore = (boolean)cv_dummygpencore.value; + + grandprixinfo.cup = newcup; + + grandprixinfo.gp = true; + grandprixinfo.roundnum = 1; + grandprixinfo.initalize = true; + + paused = false; + + // Don't restart the server if we're already in a game lol + if (gamestate == GS_MENU) { - cupgrid.x = 0; - cupgrid.pageno++; - if (cupgrid.pageno >= cupgrid.numpages) - cupgrid.pageno = 0; - } - S_StartSound(NULL, sfx_s3k5b); - break; - case KEY_LEFTARROW: - cupgrid.x--; - if (cupgrid.x < 0) - { - cupgrid.x = CUPMENU_COLUMNS-1; - cupgrid.pageno--; - if (cupgrid.pageno < 0) - cupgrid.pageno = cupgrid.numpages-1; - } - S_StartSound(NULL, sfx_s3k5b); - break; - case KEY_UPARROW: - cupgrid.y++; - if (cupgrid.y >= CUPMENU_ROWS) - cupgrid.y = 0; - S_StartSound(NULL, sfx_s3k5b); - break; - case KEY_DOWNARROW: - cupgrid.y--; - if (cupgrid.y < 0) - cupgrid.y = CUPMENU_ROWS-1; - S_StartSound(NULL, sfx_s3k5b); - break; - case KEY_ENTER: - if ((!newcup) || (newcup && newcup->unlockrequired != -1 && !unlockables[newcup->unlockrequired].unlocked)) - { - S_StartSound(NULL, sfx_s3kb2); - break; + SV_StartSinglePlayerServer(); + multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... + netgame = levellist.netgame; // ^ ditto. } - if (cupgrid.grandprix == true) + D_MapChange( + grandprixinfo.cup->levellist[0] + 1, + GT_RACE, + grandprixinfo.encore, + true, + 1, + false, + false + ); + + M_ClearMenus(true); + } + else + { + // Keep cursor position if you select the same cup again, reset if it's a different cup + if (!levellist.selectedcup || newcup->id != levellist.selectedcup->id) { - S_StartSound(NULL, sfx_s3k63); - - // Early fadeout to let the sound finish playing - F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); - F_WipeEndScreen(); - F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); - - memset(&grandprixinfo, 0, sizeof(struct grandprixinfo)); - - // read our dummy cvars - - grandprixinfo.gamespeed = min(KARTSPEED_HARD, cv_dummygpdifficulty.value); - grandprixinfo.masterbots = (cv_dummygpdifficulty.value == 3); - grandprixinfo.encore = (boolean)cv_dummygpencore.value; - - grandprixinfo.cup = newcup; - - grandprixinfo.gp = true; - grandprixinfo.roundnum = 1; - grandprixinfo.initalize = true; - - paused = false; - - // Don't restart the server if we're already in a game lol - if (gamestate == GS_MENU) - { - SV_StartSinglePlayerServer(); - multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... - netgame = levellist.netgame; // ^ ditto. - } - - D_MapChange( - grandprixinfo.cup->levellist[0] + 1, - GT_RACE, - grandprixinfo.encore, - true, - 1, - false, - false - ); - - M_ClearMenus(true); + levellist.cursor = 0; + levellist.selectedcup = newcup; } - else - { - // Keep cursor position if you select the same cup again, reset if it's a different cup - if (!levellist.selectedcup || newcup->id != levellist.selectedcup->id) - { - levellist.cursor = 0; - levellist.selectedcup = newcup; - } - M_LevelSelectScrollDest(); - levellist.y = levellist.dest; + M_LevelSelectScrollDest(); + levellist.y = levellist.dest; - M_SetupNextMenu(&PLAY_LevelSelectDef, false); - S_StartSound(NULL, sfx_s3k63); - } - break; - case KEY_ESCAPE: - if (currentMenu->prevMenu) - M_SetupNextMenu(currentMenu->prevMenu, false); - else - M_ClearMenus(true); - break; - default: - break; + M_SetupNextMenu(&PLAY_LevelSelectDef, false); + S_StartSound(NULL, sfx_s3k63); + } + } + else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + { + M_SetMenuDelay(pid); + + if (currentMenu->prevMenu) + M_SetupNextMenu(currentMenu->prevMenu, false); + else + M_ClearMenus(true); } } @@ -2686,124 +2694,136 @@ void M_LevelSelectHandler(INT32 choice) { INT16 start = M_GetFirstLevelInList(levellist.newgametype); INT16 maxlevels = M_CountLevelsToShowInList(levellist.newgametype); + const UINT8 pid = 0; + + (void)choice; if (levellist.y != levellist.dest) - return; - - switch (choice) { - case KEY_UPARROW: - levellist.cursor--; - if (levellist.cursor < 0) - levellist.cursor = maxlevels-1; - S_StartSound(NULL, sfx_s3k5b); - break; - case KEY_DOWNARROW: - levellist.cursor++; - if (levellist.cursor >= maxlevels) - levellist.cursor = 0; - S_StartSound(NULL, sfx_s3k5b); - break; - case KEY_ENTER: - { - INT16 map = start; - INT16 add = levellist.cursor; + return; + } - while (add > 0) - { - map++; - - while (!M_CanShowLevelInList(map, levellist.newgametype) && map < NUMMAPS) - map++; - - if (map >= NUMMAPS) - break; - - add--; - } - - if (map >= NUMMAPS) - break; - - levellist.choosemap = map; - - if (levellist.timeattack) - { - M_SetupNextMenu(&PLAY_TimeAttackDef, false); - S_StartSound(NULL, sfx_s3k63); - } - else - { - if (gamestate == GS_MENU) - { - UINT8 ssplayers = cv_splitplayers.value-1; - - netgame = false; - multiplayer = true; - - strncpy(connectedservername, cv_servername.string, MAXSERVERNAME); - - // Still need to reset devmode - cv_debug = 0; - - if (demo.playback) - G_StopDemo(); - if (metalrecording) - G_StopMetalDemo(); - - /*if (levellist.choosemap == 0) - levellist.choosemap = G_RandMap(G_TOLFlag(levellist.newgametype), -1, false, 0, false, NULL);*/ - - if (cv_maxplayers.value < ssplayers+1) - CV_SetValue(&cv_maxplayers, ssplayers+1); - - if (splitscreen != ssplayers) - { - splitscreen = ssplayers; - SplitScreen_OnChange(); - } - - S_StartSound(NULL, sfx_s3k63); - - paused = false; - - // Early fadeout to let the sound finish playing - F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); - F_WipeEndScreen(); - F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); - - SV_StartSinglePlayerServer(); - multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... - netgame = levellist.netgame; // ^ ditto. - - // this is considered to be CV_CHEAT however... - CV_StealthSet(&cv_kartbot, cv_dummymatchbots.string); // Match the kartbot value to the dummy match bots value. - - if (netgame) // check for the dummy kartspeed value - CV_StealthSet(&cv_kartspeed, cv_dummykartspeed.string); - - - D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_dummygpencore.value == 1), 1, 1, false, false); - } - else // directly do the map change - D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false); - - M_ClearMenus(true); - } - } - break; - case KEY_ESCAPE: - if (currentMenu->prevMenu) - M_SetupNextMenu(currentMenu->prevMenu, false); - else - M_ClearMenus(true); - break; - default: - break; + if (menucmd[pid].dpad_ud > 0) + { + levellist.cursor++; + if (levellist.cursor >= maxlevels) + levellist.cursor = 0; + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + } + else if (menucmd[pid].dpad_ud < 0) + { + levellist.cursor--; + if (levellist.cursor < 0) + levellist.cursor = maxlevels-1; + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); } M_LevelSelectScrollDest(); + + if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X) /*|| M_MenuButtonPressed(pid, MBT_START)*/) + { + INT16 map = start; + INT16 add = levellist.cursor; + + M_SetMenuDelay(pid); + + while (add > 0) + { + map++; + + while (!M_CanShowLevelInList(map, levellist.newgametype) && map < NUMMAPS) + map++; + + if (map >= NUMMAPS) + break; + + add--; + } + + if (map >= NUMMAPS) + { + // This shouldn't happen + return; + } + + levellist.choosemap = map; + + if (levellist.timeattack) + { + M_SetupNextMenu(&PLAY_TimeAttackDef, false); + S_StartSound(NULL, sfx_s3k63); + } + else + { + if (gamestate == GS_MENU) + { + UINT8 ssplayers = cv_splitplayers.value-1; + + netgame = false; + multiplayer = true; + + strncpy(connectedservername, cv_servername.string, MAXSERVERNAME); + + // Still need to reset devmode + cv_debug = 0; + + if (demo.playback) + G_StopDemo(); + if (metalrecording) + G_StopMetalDemo(); + + /*if (levellist.choosemap == 0) + levellist.choosemap = G_RandMap(G_TOLFlag(levellist.newgametype), -1, false, 0, false, NULL);*/ + + if (cv_maxplayers.value < ssplayers+1) + CV_SetValue(&cv_maxplayers, ssplayers+1); + + if (splitscreen != ssplayers) + { + splitscreen = ssplayers; + SplitScreen_OnChange(); + } + + S_StartSound(NULL, sfx_s3k63); + + paused = false; + + // Early fadeout to let the sound finish playing + F_WipeStartScreen(); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeEndScreen(); + F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); + + SV_StartSinglePlayerServer(); + multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... + netgame = levellist.netgame; // ^ ditto. + + // this is considered to be CV_CHEAT however... + CV_StealthSet(&cv_kartbot, cv_dummymatchbots.string); // Match the kartbot value to the dummy match bots value. + + if (netgame) // check for the dummy kartspeed value + CV_StealthSet(&cv_kartspeed, cv_dummykartspeed.string); + + + D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_dummygpencore.value == 1), 1, 1, false, false); + } + else // directly do the map change + D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false); + + M_ClearMenus(true); + } + } + else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + { + M_SetMenuDelay(pid); + + if (currentMenu->prevMenu) + M_SetupNextMenu(currentMenu->prevMenu, false); + else + M_ClearMenus(true); + } } void M_LevelSelectTick(void) @@ -2824,8 +2844,6 @@ void M_LevelSelectTick(void) } } - - struct mpmenu_s mpmenu; // MULTIPLAYER OPTION SELECT From d3f8e61591ead51bedfe3a802a8f6c917a75a1a5 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 28 Dec 2021 12:49:33 -0500 Subject: [PATCH 075/379] In-game control shenanigans - Fix gamekeydown using FRACUNIT instead of JOYAXISRANGE - Re-add spindash button functionality - I put custom buttons back ... but they're now called "Lua" buttons A thru C because Custom was a dumb misleading name --- src/d_ticcmd.h | 21 +++++++++++---------- src/deh_tables.c | 6 +++--- src/g_game.c | 30 +++++++++++++++++++++--------- src/g_input.c | 6 +++++- src/g_input.h | 4 +++- 5 files changed, 43 insertions(+), 24 deletions(-) diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index 226c8f4d8..c806f2642 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -26,22 +26,23 @@ // Button/action code definitions. typedef enum { - BT_ACCELERATE = 1, // Accelerate - BT_DRIFT = 1<<2, // Drift (direction is cmd->turning) - BT_BRAKE = 1<<3, // Brake - BT_ATTACK = 1<<4, // Use Item - BT_FORWARD = 1<<5, // Aim Item Forward - BT_BACKWARD = 1<<6, // Aim Item Backward - BT_LOOKBACK = 1<<7, // Look Backward + BT_ACCELERATE = 1, // Accelerate + BT_DRIFT = 1<<2, // Drift (direction is cmd->turning) + BT_BRAKE = 1<<3, // Brake + BT_ATTACK = 1<<4, // Use Item + BT_FORWARD = 1<<5, // Aim Item Forward + BT_BACKWARD = 1<<6, // Aim Item Backward + BT_LOOKBACK = 1<<7, // Look Backward BT_EBRAKEMASK = (BT_ACCELERATE|BT_BRAKE), + BT_SPINDASHMASK = (BT_ACCELERATE|BT_BRAKE|BT_DRIFT), // free: 1<<9 to 1<<12 // Lua garbage - BT_CUSTOM1 = 1<<13, - BT_CUSTOM2 = 1<<14, - BT_CUSTOM3 = 1<<15, + BT_LUAA = 1<<13, + BT_LUAB = 1<<14, + BT_LUAC = 1<<15, } buttoncode_t; // The data sampled per tick (single player) diff --git a/src/deh_tables.c b/src/deh_tables.c index 33854d522..f2e0dc966 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -6661,9 +6661,9 @@ struct int_const_s const INT_CONST[] = { {"BT_ATTACK",BT_ATTACK}, {"BT_FORWARD",BT_FORWARD}, {"BT_BACKWARD",BT_BACKWARD}, - {"BT_CUSTOM1",BT_CUSTOM1}, // Lua customizable - {"BT_CUSTOM2",BT_CUSTOM2}, // Lua customizable - {"BT_CUSTOM3",BT_CUSTOM3}, // Lua customizable + {"BT_LUAA",BT_LUAA}, // Lua customizable + {"BT_LUAB",BT_LUAB}, // Lua customizable + {"BT_LUAC",BT_LUAC}, // Lua customizable // Lua command registration flags {"COM_ADMIN",COM_ADMIN}, diff --git a/src/g_game.c b/src/g_game.c index ab2307b9f..ae0c2036b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -956,7 +956,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) else { // forward with key or button // SRB2kart - we use an accel/brake instead of forward/backward. - fixed_t value = G_PlayerInputAnalog(forplayer, gc_a, 0); + INT32 value = G_PlayerInputAnalog(forplayer, gc_a, 0); if (value != 0) { cmd->buttons |= BT_ACCELERATE; @@ -982,24 +982,36 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) } } - // fire with any button/key + // drift if (G_PlayerInputDown(forplayer, gc_c, 0)) - { - cmd->buttons |= BT_ATTACK; - } - - // drift with any button/key - if (G_PlayerInputDown(forplayer, gc_x, 0)) { cmd->buttons |= BT_DRIFT; } - // rear view with any button/key + // A + B + C shortcut + if (G_PlayerInputDown(forplayer, gc_abc, 0)) + { + forward = 0; + cmd->buttons |= BT_SPINDASHMASK; + } + + // fire + if (G_PlayerInputDown(forplayer, gc_x, 0)) + { + cmd->buttons |= BT_ATTACK; + } + + // rear view if (G_PlayerInputDown(forplayer, gc_y, 0)) { cmd->buttons |= BT_LOOKBACK; } + // lua buttons a thru c + if (G_PlayerInputDown(forplayer, gc_luaa, 0)) { cmd->buttons |= BT_LUAA; } + if (G_PlayerInputDown(forplayer, gc_luab, 0)) { cmd->buttons |= BT_LUAB; } + if (G_PlayerInputDown(forplayer, gc_luac, 0)) { cmd->buttons |= BT_LUAC; } + // spectator aiming shit, ahhhh... /* { diff --git a/src/g_input.c b/src/g_input.c index 3ecc35724..35e5f7da9 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -18,6 +18,7 @@ #include "hu_stuff.h" // need HUFONT start & end #include "d_net.h" #include "console.h" +#include "i_joy.h" // JOYAXISRANGE #define MAXMOUSESENSITIVITY 100 // sensitivity steps @@ -118,7 +119,7 @@ void G_MapEventsToControls(event_t *ev) case ev_keydown: if (ev->data1 < NUMINPUTS) { - gamekeydown[ev->device][ev->data1] = FRACUNIT; + gamekeydown[ev->device][ev->data1] = JOYAXISRANGE; } #ifdef PARANOIA else @@ -404,6 +405,9 @@ static const char *gamecontrolname[num_gamecontrols] = "r", "start", "abc", + "luaa", + "luab", + "luac", "console", "talk", "teamtalk", diff --git a/src/g_input.h b/src/g_input.h index 38f0f4af3..d699a528c 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -67,12 +67,14 @@ typedef enum // special keys gc_abc, + gc_luaa, + gc_luab, + gc_luac, gc_console, gc_talk, gc_teamtalk, gc_screenshot, gc_recordgif, - num_gamecontrols } gamecontrols_e; From b5e16a520283a844f22c10fabb16efdb26978de3 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 28 Dec 2021 22:16:37 -0500 Subject: [PATCH 076/379] Add 4PREADY graphic --- src/k_menudraw.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index cf49e2669..eb91a2c7b 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -834,12 +834,10 @@ static void M_DrawCharSelectPreview(UINT8 num) { V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PSTART", PU_CACHE)); } - /* else if (p->mdepth >= CSSTEP_READY) { V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PREADY", PU_CACHE)); } - */ } V_DrawScaledPatch(x+9, y+2, 0, W_CachePatchName("FILEBACK", PU_CACHE)); From 7411484bf68672fd5a557fe6b3ed0e66af895ec4 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 2 Jan 2022 21:38:06 -0800 Subject: [PATCH 077/379] More TESTERS guard --- src/m_menu.c | 11579 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 11579 insertions(+) create mode 100644 src/m_menu.c diff --git a/src/m_menu.c b/src/m_menu.c new file mode 100644 index 000000000..3a5be46a9 --- /dev/null +++ b/src/m_menu.c @@ -0,0 +1,11579 @@ +// 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" +#include "i_threads.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_hud.h" // SRB2kart +#include "k_kart.h" // KartItemCVars +#include "k_pwrlv.h" +#include "d_player.h" // KITEM_ constants +#include "k_color.h" +#include "k_grandprix.h" + +#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 + +#ifdef HAVE_DISCORDRPC +//#include "discord_rpc.h" +#include "discord.h" +#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 + +#if defined (NONET) || defined (TESTERS) +#define NOMENUHOST +#endif + +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; + +#ifdef HAVE_THREADS +I_mutex m_menu_mutex; +#endif + +M_waiting_mode_t m_waiting_mode = M_NOT_WAITING; + +const char *quitmsg[NUM_QUITMESSAGES]; + +// Stuff for customizing the player select screen Tails 09-22-2003 +description_t description[MAXSKINS]; + +//static char *char_notes = NULL; +//static fixed_t char_scroll = 0; + +boolean menuactive = false; +boolean fromlevelselect = false; + +typedef enum +{ + LLM_CREATESERVER, + LLM_LEVELSELECT, + LLM_TIMEATTACK, + LLM_BREAKTHECAPSULES +} 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 tic_t followertimer = 0; // Used for smooth follower floating + +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); +#endif + +// Prototyping is fun, innit? +// ========================================================================== +// NEEDED FUNCTION PROTOTYPES GO HERE +// ========================================================================== + +void M_SetWaitingMode(int mode); +int M_GetWaitingMode(void); + +// the haxor message menu +menu_t MessageDef; + +#ifdef HAVE_DISCORDRPC +menu_t MISC_DiscordRequestsDef; +static void M_HandleDiscordRequests(INT32 choice); +static void M_DrawDiscordRequests(void); +#endif + +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 +#ifndef TESTERS +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_GrandPrixTemp(INT32 choice); +static void M_StartGrandPrix(INT32 choice); +static void M_TimeAttack(INT32 choice); +static boolean M_QuitTimeAttackMenu(void); +static void M_BreakTheCapsules(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_ModeAttackEndGame(INT32 choice); +static void M_SetGuestReplay(INT32 choice); +//static void M_ChoosePlayer(INT32 choice); +menu_t SP_LevelStatsDef; +static menu_t SP_GrandPrixTempDef; +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 +#ifndef TESTERS +static void M_StartServerMenu(INT32 choice); +#endif +static void M_ConnectMenu(INT32 choice); +static void M_ConnectMenuModChecks(INT32 choice); +static void M_Refresh(INT32 choice); +static void M_Connect(INT32 choice); +#endif +#ifndef TESTERS +static void M_StartOfflineServerMenu(INT32 choice); +#endif +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; +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 +static void M_OpenGLOptionsMenu(void); +menu_t OP_OpenGLOptionsDef; +#endif +menu_t OP_SoundOptionsDef; +//static void M_RestartAudio(void); + +//Misc +menu_t OP_DataOptionsDef, OP_ScreenshotOptionsDef, OP_EraseDataDef; +#ifdef HAVE_DISCORDRPC +menu_t OP_DiscordOptionsDef; +#endif +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 +menu_t MISC_ReplayHutDef; +menu_t MISC_ReplayOptionsDef; +static void M_HandleReplayHutList(INT32 choice); +static void M_DrawReplayHut(void); +static void M_DrawReplayStartMenu(void); +static boolean M_QuitReplayHut(void); +static void M_HutStartReplay(INT32 choice); + +static void M_DrawPlaybackMenu(void); +static void M_PlaybackRewind(INT32 choice); +static void M_PlaybackPause(INT32 choice); +static void M_PlaybackFastForward(INT32 choice); +static void M_PlaybackAdvance(INT32 choice); +static void M_PlaybackSetViews(INT32 choice); +static void M_PlaybackAdjustView(INT32 choice); +static void M_PlaybackToggleFreecam(INT32 choice); +static void M_PlaybackQuit(INT32 choice); + +static UINT8 playback_enterheld = 0; // horrid hack to prevent holding the button from being extremely fucked + +// 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); +static void M_DrawMPMainMenu(void); +#ifndef NONET +static void M_DrawConnectMenu(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); +static void M_HandleVideoMode(INT32 choice); +static void M_HandleMonitorToggles(INT32 choice); + +// Consvar onchange functions +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 = CVAR_INIT ("showfocuslost", "Yes", CV_SAVE, CV_YesNo, NULL); + +static CV_PossibleValue_t map_cons_t[] = { + {0,"MIN"}, + {NUMMAPS, "MAX"}, + {0, NULL} +}; +consvar_t cv_nextmap = CVAR_INIT ("nextmap", "1", CV_HIDEN|CV_CALL, map_cons_t, Nextmap_OnChange); + +static CV_PossibleValue_t skins_cons_t[MAXSKINS+1] = {{1, DEFAULTSKIN}}; +consvar_t cv_chooseskin = CVAR_INIT ("chooseskin", DEFAULTSKIN, CV_HIDEN|CV_CALL, skins_cons_t, Nextmap_OnChange); + +// This gametype list is integral for many different reasons. +// When you add gametypes here, don't forget to update them in dehacked.c and doomstat.h! +CV_PossibleValue_t gametype_cons_t[NUMGAMETYPES+1]; + +consvar_t cv_newgametype = CVAR_INIT ("newgametype", "Race", CV_HIDEN|CV_CALL, gametype_cons_t, Newgametype_OnChange); + +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 = CVAR_INIT ("serversort", "Ping", CV_CALL, serversort_cons_t, M_SortServerList); + +// first time memory +consvar_t cv_tutorialprompt = CVAR_INIT ("tutorialprompt", "On", CV_SAVE, CV_OnOff, NULL); + +// autorecord demos for time attack +static consvar_t cv_autorecord = CVAR_INIT ("autorecord", "Yes", 0, CV_YesNo, 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 = CVAR_INIT ("ghost_besttime", "Show All", CV_SAVE, ghost_cons_t, NULL); +consvar_t cv_ghost_bestlap = CVAR_INIT ("ghost_bestlap", "Show All", CV_SAVE, ghost_cons_t, NULL); +consvar_t cv_ghost_last = CVAR_INIT ("ghost_last", "Show All", CV_SAVE, ghost_cons_t, NULL); +consvar_t cv_ghost_guest = CVAR_INIT ("ghost_guest", "Show", CV_SAVE, ghost2_cons_t, NULL); +consvar_t cv_ghost_staff = CVAR_INIT ("ghost_staff", "Show", CV_SAVE, ghost2_cons_t, 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 = CVAR_INIT ("splitplayers", "One", CV_CALL, splitplayers_cons_t, Splitplayers_OnChange); + +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[] = {{-20, "MIN"}, {20, "MAX"}, {0, NULL}}; +static CV_PossibleValue_t liveslimit_cons_t[] = {{-1, "MIN"}, {9, "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 = CVAR_INIT ("dummymenuplayer", "P1", CV_HIDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange); +static consvar_t cv_dummyteam = CVAR_INIT ("dummyteam", "Spectator", CV_HIDEN, dummyteam_cons_t, NULL); +static consvar_t cv_dummyspectate = CVAR_INIT ("dummyspectate", "Spectator", CV_HIDEN, dummyspectate_cons_t, NULL); +static consvar_t cv_dummyscramble = CVAR_INIT ("dummyscramble", "Random", CV_HIDEN, dummyscramble_cons_t, NULL); +static consvar_t cv_dummyrings = CVAR_INIT ("dummyrings", "0", CV_HIDEN, ringlimit_cons_t, NULL); +static consvar_t cv_dummylives = CVAR_INIT ("dummylives", "0", CV_HIDEN, liveslimit_cons_t, NULL); +static consvar_t cv_dummystaff = CVAR_INIT ("dummystaff", "0", CV_HIDEN|CV_CALL, dummystaff_cons_t, Dummystaff_OnChange); + +static CV_PossibleValue_t dummygpdifficulty_cons_t[] = {{0, "Easy"}, {1, "Normal"}, {2, "Hard"}, {3, "Master"}, {0, NULL}}; +static CV_PossibleValue_t dummygpcup_cons_t[50] = {{1, "TEMP"}}; // A REALLY BIG NUMBER, SINCE THIS IS TEMP UNTIL NEW MENUS + +static consvar_t cv_dummygpdifficulty = CVAR_INIT ("dummygpdifficulty", "Normal", CV_HIDEN, dummygpdifficulty_cons_t, NULL); +static consvar_t cv_dummygpencore = CVAR_INIT ("dummygpencore", "Off", CV_HIDEN, CV_OnOff, NULL); +static consvar_t cv_dummygpcup = CVAR_INIT ("dummygpcup", "TEMP", CV_HIDEN, dummygpcup_cons_t, 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}, +#ifdef TESTERS + {IT_GRAYEDOUT, NULL, "1 Player", NULL, 84}, +#else + {IT_CALL |IT_STRING, NULL, "1 Player", M_SinglePlayerMenu, 84}, +#endif + {IT_SUBMENU|IT_STRING, NULL, "Multiplayer", &MP_MainDef, 92}, + {IT_CALL |IT_STRING, NULL, "Options", M_Options, 100}, + /* I don't think is useful at all... */ + {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_KEYHANDLER|IT_NOTHING, NULL, "", M_HandleReplayHutList, 0}, // Dummy menuitem for the replay list + {IT_NOTHING, NULL, "", NULL, 0}, // Dummy for handling wrapping to the top of the menu.. +}; + +static menuitem_t MISC_ReplayStartMenu[] = +{ + {IT_CALL |IT_STRING, NULL, "Load Addons and Watch", M_HutStartReplay, 0}, + {IT_CALL |IT_STRING, NULL, "Watch Without Addons", M_HutStartReplay, 10}, + {IT_CALL |IT_STRING, NULL, "Watch Replay", M_HutStartReplay, 10}, + {IT_SUBMENU |IT_STRING, NULL, "Back", &MISC_ReplayHutDef, 30}, +}; + +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}, +}; + +static tic_t playback_last_menu_interaction_leveltime = 0; +static menuitem_t PlaybackMenu[] = +{ + {IT_CALL | IT_STRING, "M_PHIDE", "Hide Menu (Esc)", M_SelectableClearMenus, 0}, + + {IT_CALL | IT_STRING, "M_PREW", "Rewind ([)", M_PlaybackRewind, 20}, + {IT_CALL | IT_STRING, "M_PPAUSE", "Pause (\\)", M_PlaybackPause, 36}, + {IT_CALL | IT_STRING, "M_PFFWD", "Fast-Forward (])", M_PlaybackFastForward, 52}, + {IT_CALL | IT_STRING, "M_PSTEPB", "Backup Frame ([)", M_PlaybackRewind, 20}, + {IT_CALL | IT_STRING, "M_PRESUM", "Resume", M_PlaybackPause, 36}, + {IT_CALL | IT_STRING, "M_PFADV", "Advance Frame (])", M_PlaybackAdvance, 52}, + + {IT_ARROWS | IT_STRING, "M_PVIEWS", "View Count (- and =)", M_PlaybackSetViews, 72}, + {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint (1)", M_PlaybackAdjustView, 88}, + {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint 2 (2)", M_PlaybackAdjustView, 104}, + {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint 3 (3)", M_PlaybackAdjustView, 120}, + {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint 4 (4)", M_PlaybackAdjustView, 136}, + + {IT_CALL | IT_STRING, "M_PVIEWS", "Toggle Free Camera (')", M_PlaybackToggleFreecam, 156}, + {IT_CALL | IT_STRING, "M_PEXIT", "Stop Playback", M_PlaybackQuit, 172}, +}; +typedef enum +{ + playback_hide, + playback_rewind, + playback_pause, + playback_fastforward, + playback_backframe, + playback_resume, + playback_advanceframe, + playback_viewcount, + playback_view1, + playback_view2, + playback_view3, + playback_view4, + playback_freecamera, + //playback_moreoptions, + playback_quit +} playback_e; + +// --------------------------------- +// 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, "Addons...", M_Addons, 8}, + {IT_STRING | IT_SUBMENU, NULL, "Scramble Teams...", &MISC_ScrambleTeamDef, 16}, + {IT_STRING | IT_CALL, NULL, "Switch Map..." , M_MapChange, 24}, + +#ifdef HAVE_DISCORDRPC + {IT_STRING | IT_SUBMENU, NULL, "Ask To Join Requests...", &MISC_DiscordRequestsDef, 24}, +#endif + + {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, +#ifdef HAVE_DISCORDRPC + mpause_discordrequests, +#endif + + 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; + +#ifdef HAVE_DISCORDRPC +static menuitem_t MISC_DiscordRequestsMenu[] = +{ + {IT_KEYHANDLER|IT_NOTHING, NULL, "", M_HandleDiscordRequests, 0}, +}; +#endif + +// ----------------- +// 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, "Gravity", &cv_gravity, 60}, + + {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[] = +{ +#ifndef TESTERS + {IT_STRING|IT_SUBMENU, NULL, "Unlockables", &SR_UnlockChecklistDef, 100}, +#endif + {IT_CALL|IT_STRING|IT_CALL_NOTMODIFIED, NULL, "Statistics", M_Statistics, 108}, + {IT_CALL|IT_STRING, NULL, "Replay Hut", M_ReplayHut, 116}, + {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_STRING|IT_CALL, NULL, "Grand Prix", M_GrandPrixTemp, 92}, + {IT_SECRET, NULL, "Time Attack", M_TimeAttack, 100}, + {IT_SECRET, NULL, "Break the Capsules", M_BreakTheCapsules, 108}, +}; + +enum +{ + spgrandprix, + sptimeattack, + spbreakthecapsules +}; + +// Single Player Load Game +static menuitem_t SP_GrandPrixPlaceholderMenu[] = +{ + {IT_STRING|IT_CVAR, NULL, "Character", &cv_chooseskin, 10}, + {IT_STRING|IT_CVAR, NULL, "Color", &cv_playercolor[0], 20}, + + {IT_STRING|IT_CVAR, NULL, "Difficulty", &cv_dummygpdifficulty, 40}, + {IT_STRING|IT_CVAR, NULL, "Encore Mode", &cv_dummygpencore, 50}, + + {IT_STRING|IT_CVAR, NULL, "Cup", &cv_dummygpcup, 70}, + {IT_STRING|IT_CALL, NULL, "Start", M_StartGrandPrix, 80}, +}; + +// Single Player Time Attack +static menuitem_t SP_TimeAttackMenu[] = +{ + {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Name", &cv_playername[0], 0}, + {IT_STRING|IT_CVAR, NULL, "Character", &cv_chooseskin, 13}, + {IT_STRING|IT_CVAR, NULL, "Color", &cv_playercolor[0], 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 NOMENUHOST + {IT_STRING|IT_CALL, NULL, "Internet/LAN...", M_StartServerMenu, 110-24}, +#else + {IT_GRAYEDOUT, NULL, "Internet/LAN...", NULL, 110-24}, +#endif +#ifdef TESTERS + {IT_GRAYEDOUT, NULL, "Offline...", NULL, 118-24}, +#else + {IT_STRING|IT_CALL, NULL, "Offline...", M_StartOfflineServerMenu, 118-24}, +#endif + + {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, 10}, + {IT_STRING|IT_CVAR, NULL, "Advertise", &cv_advertise, 20}, + {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Server Name", &cv_servername, 30}, + + {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, "Follower", M_HandleSetupMultiPlayer, 26}, + {IT_KEYHANDLER | IT_STRING, NULL, "Color", M_HandleSetupMultiPlayer, 152}, +}; + +#ifndef NONET +static menuitem_t MP_ConnectMenu[] = +{ + {IT_STRING | IT_CVAR, NULL, "Sort By", &cv_serversort, 4}, + {IT_STRING | IT_KEYHANDLER, NULL, "Page", M_HandleServerPage, 12}, + {IT_STRING | IT_CALL, NULL, "Refresh", M_Refresh, 20}, + + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 36}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 48}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 60}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 72}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 84}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 96}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 108}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 120}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 132}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 144}, + {IT_STRING | IT_SPACE, NULL, "", M_Connect, 156}, +}; + +enum +{ + mp_connect_sort, + mp_connect_page, + mp_connect_refresh, + FIRSTSERVERLINE +}; +#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_SUBMENU|IT_STRING, NULL, "Gameplay Options...", &OP_GameOptionsDef, 70}, + {IT_SUBMENU|IT_STRING, NULL, "Server Options...", &OP_ServerOptionsDef, 80}, + + {IT_SUBMENU|IT_STRING, NULL, "Data Options...", &OP_DataOptionsDef, 100}, + + {IT_CALL|IT_STRING, NULL, "Tricks & Secrets (F1)", M_Manual, 120}, + {IT_CALL|IT_STRING, NULL, "Play Credits", M_Credits, 130}, +}; + +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, "Spindash", M_ChangeControl, gc_spindash }, + {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[0] , 30}, + {IT_STRING | IT_CVAR, NULL, "Turn Left/Right" , &cv_turnaxis[0] , 40}, + {IT_STRING | IT_CVAR, NULL, "Accelerate" , &cv_moveaxis[0] , 50}, + {IT_STRING | IT_CVAR, NULL, "Brake" , &cv_brakeaxis[0] , 60}, + {IT_STRING | IT_CVAR, NULL, "Drift" , &cv_driftaxis[0] , 70}, + {IT_STRING | IT_CVAR, NULL, "Use Item" , &cv_fireaxis[0] , 80}, + {IT_STRING | IT_CVAR, NULL, "Look Up/Down" , &cv_lookaxis[0] , 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_aimaxis[1] , 30}, + {IT_STRING | IT_CVAR, NULL, "Turn Left/Right" , &cv_turnaxis[1] , 40}, + {IT_STRING | IT_CVAR, NULL, "Accelerate" , &cv_moveaxis[1] , 50}, + {IT_STRING | IT_CVAR, NULL, "Brake" , &cv_brakeaxis[1] , 60}, + {IT_STRING | IT_CVAR, NULL, "Drift" , &cv_driftaxis[1] , 70}, + {IT_STRING | IT_CVAR, NULL, "Use Item" , &cv_fireaxis[1] , 80}, + {IT_STRING | IT_CVAR, NULL, "Look Up/Down" , &cv_lookaxis[1] , 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_aimaxis[2] , 30}, + {IT_STRING | IT_CVAR, NULL, "Turn Left/Right" , &cv_turnaxis[2] , 40}, + {IT_STRING | IT_CVAR, NULL, "Accelerate" , &cv_moveaxis[2] , 50}, + {IT_STRING | IT_CVAR, NULL, "Brake" , &cv_brakeaxis[2] , 60}, + {IT_STRING | IT_CVAR, NULL, "Drift" , &cv_driftaxis[2] , 70}, + {IT_STRING | IT_CVAR, NULL, "Use Item" , &cv_fireaxis[2] , 80}, + {IT_STRING | IT_CVAR, NULL, "Look Up/Down" , &cv_lookaxis[2] , 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_aimaxis[3] , 30}, + {IT_STRING | IT_CVAR, NULL, "Turn Left/Right" , &cv_turnaxis[3] , 40}, + {IT_STRING | IT_CVAR, NULL, "Accelerate" , &cv_moveaxis[3] , 50}, + {IT_STRING | IT_CVAR, NULL, "Brake" , &cv_brakeaxis[3] , 60}, + {IT_STRING | IT_CVAR, NULL, "Drift" , &cv_driftaxis[3] , 70}, + {IT_STRING | IT_CVAR, NULL, "Use Item" , &cv_fireaxis[3] , 80}, + {IT_STRING | IT_CVAR, NULL, "Look Up/Down" , &cv_lookaxis[3] , 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_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 +#ifdef HWRENDER + {IT_STRING | IT_CVAR, NULL, "Renderer", &cv_renderer, 30}, +#else + {IT_TRANSTEXT | IT_PAIR, "Renderer", "Software", &cv_renderer, 30}, +#endif + {IT_STRING | IT_CVAR | IT_CV_SLIDER, + NULL, "Gamma", &cv_globalgamma, 50}, + + {IT_STRING | IT_CVAR, NULL, "Show FPS", &cv_ticrate, 60}, + {IT_STRING | IT_CVAR, NULL, "Vertical Sync", &cv_vidwait, 70}, + + {IT_STRING | IT_CVAR, NULL, "Draw Distance", &cv_drawdist, 90}, + {IT_STRING | IT_CVAR, NULL, "Weather Draw Distance",&cv_drawdist_precip, 100}, + {IT_STRING | IT_CVAR, NULL, "Skyboxes", &cv_skybox, 110}, + +#ifdef HWRENDER + {IT_CALL | IT_STRING, NULL, "OpenGL Options...", &M_OpenGLOptionsMenu, 140}, +#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_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_STRING | IT_CVAR, NULL, "3D Models", &cv_glmodels, 10}, + {IT_STRING|IT_CVAR, NULL, "Shaders", &cv_glshaders, 20}, + + {IT_STRING|IT_CVAR, NULL, "Texture Quality", &cv_scr_depth, 40}, + {IT_STRING|IT_CVAR, NULL, "Texture Filter", &cv_glfiltermode, 50}, + {IT_STRING|IT_CVAR, NULL, "Anisotropic", &cv_glanisotropicmode, 60}, + + {IT_STRING|IT_CVAR, NULL, "Wall Contrast Style", &cv_glfakecontrast, 80}, + {IT_STRING|IT_CVAR, NULL, "Sprite Billboarding", &cv_glspritebillboarding, 90}, + {IT_STRING|IT_CVAR, NULL, "Software Perspective", &cv_glshearing, 100}, +}; +#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}, + + //{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_CALL, NULL, "Addon Options...", M_AddonsOptions, 20}, + {IT_STRING | IT_SUBMENU, NULL, "Replay Options...", &MISC_ReplayOptionsDef, 30}, +#ifdef HAVE_DISCORDRPC + {IT_STRING | IT_SUBMENU, NULL, "Discord Options...", &OP_DiscordOptionsDef, 40}, + + {IT_STRING | IT_SUBMENU, NULL, "Erase Data...", &OP_EraseDataDef, 60}, +#else + {IT_STRING | IT_SUBMENU, NULL, "Erase Data...", &OP_EraseDataDef, 50}, +#endif +}; + +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 addons 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, +}; + +#ifdef HAVE_DISCORDRPC +static menuitem_t OP_DiscordOptionsMenu[] = +{ + {IT_STRING | IT_CVAR, NULL, "Rich Presence", &cv_discordrp, 10}, + + {IT_HEADER, NULL, "Rich Presence Settings", NULL, 30}, + {IT_STRING | IT_CVAR, NULL, "Streamer Mode", &cv_discordstreamer, 40}, + + {IT_STRING | IT_CVAR, NULL, "Allow Ask To Join", &cv_discordasks, 60}, + {IT_STRING | IT_CVAR, NULL, "Allow Invites", &cv_discordinvites, 70}, +}; +#endif + +static menuitem_t OP_HUDOptionsMenu[] = +{ + {IT_STRING | IT_CVAR, NULL, "Show HUD (F3)", &cv_showhud, 20}, + {IT_STRING | IT_CVAR | IT_CV_SLIDER, + NULL, "HUD Visibility", &cv_translucenthud, 30}, + + {IT_STRING | IT_SUBMENU, NULL, "Online HUD options...",&OP_ChatOptionsDef, 45}, + {IT_STRING | IT_CVAR, NULL, "Background Glass", &cons_backcolor, 55}, + + {IT_STRING | IT_CVAR | IT_CV_SLIDER, + NULL, "Minimap Visibility", &cv_kartminimap, 70}, + {IT_STRING | IT_CVAR, NULL, "Speedometer Display", &cv_kartspeedometer, 80}, + {IT_STRING | IT_CVAR, NULL, "Show \"CHECK\"", &cv_kartcheck, 90}, + + {IT_STRING | IT_CVAR, NULL, "Menu Highlights", &cons_menuhighlight, 105}, + // highlight info - (GOOD HIGHLIGHT, WARNING HIGHLIGHT) - 105 (see M_DrawHUDOptions) + + {IT_STRING | IT_CVAR, NULL, "Console Text Size", &cv_constextsize, 130}, + + {IT_STRING | IT_CVAR, NULL, "Show \"FOCUS LOST\"", &cv_showfocuslost, 145}, +}; + +// 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, "Track Power Levels", &cv_kartusepwrlv, 140}, +}; + +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 Addon 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, "Super Rings", M_HandleMonitorToggles, KITEM_SUPERRING}, + {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(MN_NONE, NULL, MainMenu, NULL, 72); + +menu_t MISC_AddonsDef = +{ + MN_NONE, + NULL, + sizeof (MISC_AddonsMenu)/sizeof (menuitem_t), + &OP_DataOptionsDef, + MISC_AddonsMenu, + M_DrawAddons, + 50, 28, + 0, + NULL +}; + +menu_t MISC_ReplayHutDef = +{ + MN_NONE, + NULL, + sizeof (MISC_ReplayHutMenu)/sizeof (menuitem_t), + NULL, + MISC_ReplayHutMenu, + M_DrawReplayHut, + 30, 80, + 0, + M_QuitReplayHut +}; + +menu_t MISC_ReplayOptionsDef = +{ + MN_NONE, + "M_REPOPT", + sizeof (MISC_ReplayOptionsMenu)/sizeof (menuitem_t), + &OP_DataOptionsDef, + MISC_ReplayOptionsMenu, + M_DrawGenericMenu, + 27, 40, + 0, + NULL +}; + +menu_t MISC_ReplayStartDef = +{ + MN_NONE, + NULL, + sizeof (MISC_ReplayStartMenu)/sizeof (menuitem_t), + &MISC_ReplayHutDef, + MISC_ReplayStartMenu, + M_DrawReplayStartMenu, + 30, 90, + 0, + NULL +}; + +menu_t PlaybackMenuDef = { + MN_NONE, + NULL, + sizeof (PlaybackMenu)/sizeof (menuitem_t), + NULL, + PlaybackMenu, + M_DrawPlaybackMenu, + //BASEVIDWIDTH/2 - 94, 2, + BASEVIDWIDTH/2 - 88, 2, + 0, + NULL +}; + +menu_t MAPauseDef = PAUSEMENUSTYLE(MAPauseMenu, 40, 72); +menu_t SPauseDef = PAUSEMENUSTYLE(SPauseMenu, 40, 72); +menu_t MPauseDef = PAUSEMENUSTYLE(MPauseMenu, 40, 72); + +#ifdef HAVE_DISCORDRPC +menu_t MISC_DiscordRequestsDef = { + MN_NONE, + NULL, + sizeof (MISC_DiscordRequestsMenu)/sizeof (menuitem_t), + &MPauseDef, + MISC_DiscordRequestsMenu, + M_DrawDiscordRequests, + 0, 0, + 0, + NULL +}; +#endif + +// Misc Main Menu +menu_t MISC_ScrambleTeamDef = DEFAULTMENUSTYLE(MN_NONE, NULL, MISC_ScrambleTeamMenu, &MPauseDef, 27, 40); +menu_t MISC_ChangeTeamDef = DEFAULTMENUSTYLE(MN_NONE, NULL, MISC_ChangeTeamMenu, &MPauseDef, 27, 40); +menu_t MISC_ChangeSpectateDef = DEFAULTMENUSTYLE(MN_NONE, 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_TIME + || 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_BATTLE) + { + 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 = +{ + MN_NONE, + "M_PANDRA", + sizeof (SR_PandorasBox)/sizeof (menuitem_t), + &SPauseDef, + SR_PandorasBox, + M_DrawGenericMenu, + 60, 40, + 0, + M_ExitPandorasBox +}; +menu_t SR_MainDef = CENTERMENUSTYLE(MN_NONE, NULL, SR_MainMenu, &MainDef, 72); + +//menu_t SR_LevelSelectDef = MAPICONMENUSTYLE(NULL, SR_LevelSelectMenu, &SR_MainDef); + +menu_t SR_UnlockChecklistDef = +{ + MN_NONE, + NULL, + 1, + &SR_MainDef, + SR_UnlockChecklistMenu, + M_DrawChecklist, + 280, 185, + 0, + NULL +}; +menu_t SR_EmblemHintDef = +{ + MN_NONE, + NULL, + sizeof (SR_EmblemHintMenu)/sizeof (menuitem_t), + &SPauseDef, + SR_EmblemHintMenu, + M_DrawEmblemHints, + 60, 150, + 0, + NULL +}; + +// Single Player +menu_t SP_MainDef = CENTERMENUSTYLE(MN_NONE, NULL, SP_MainMenu, &MainDef, 72); +/*menu_t SP_LoadDef = +{ + MN_NONE, + "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 = +{ + MN_NONE, + "M_STATS", + 1, + &SR_MainDef, + SP_LevelStatsMenu, + M_DrawLevelStats, + 280, 185, + 0, + NULL +}; + +static menu_t SP_GrandPrixTempDef = DEFAULTMENUSTYLE(MN_NONE, NULL, SP_GrandPrixPlaceholderMenu, &MainDef, 60, 30); + +static menu_t SP_TimeAttackDef = +{ + MN_NONE, + "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 = +{ + MN_NONE, + "M_ATTACK", + sizeof(SP_ReplayMenu)/sizeof(menuitem_t), + &SP_TimeAttackDef, + SP_ReplayMenu, + M_DrawTimeAttackMenu, + 34, 40, + 0, + NULL +}; +static menu_t SP_GuestReplayDef = +{ + MN_NONE, + "M_ATTACK", + sizeof(SP_GuestReplayMenu)/sizeof(menuitem_t), + &SP_TimeAttackDef, + SP_GuestReplayMenu, + M_DrawTimeAttackMenu, + 34, 40, + 0, + NULL +}; +static menu_t SP_GhostDef = +{ + MN_NONE, + "M_ATTACK", + sizeof(SP_GhostMenu)/sizeof(menuitem_t), + &SP_TimeAttackDef, + SP_GhostMenu, + M_DrawTimeAttackMenu, + 34, 40, + 0, + NULL +}; + +/*menu_t SP_PlayerDef = +{ + MN_NONE, + "M_PICKP", + sizeof (PlayerMenu)/sizeof (menuitem_t),//player_end, + &SP_MainDef, + PlayerMenu, + M_DrawSetupChoosePlayerMenu, + 24, 32, + 0, + NULL +};*/ + +// Multiplayer +menu_t MP_MainDef = +{ + MN_NONE, + "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 = +{ + MN_NONE, + "M_MULTI", + sizeof (MP_ConnectMenu)/sizeof (menuitem_t), + &MP_MainDef, + MP_ConnectMenu, + M_DrawConnectMenu, + 27,24, + 0, + M_CancelConnect +}; +#endif +menu_t MP_PlayerSetupDef = +{ + MN_NONE, + 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 = +{ + MN_NONE, + "M_OPTTTL", + sizeof (OP_MainMenu)/sizeof (menuitem_t), + &MainDef, + OP_MainMenu, + M_DrawGenericMenu, + 60, 30, + 0, + NULL +}; + +menu_t OP_ControlsDef = DEFAULTMENUSTYLE(MN_NONE, "M_CONTRO", OP_ControlsMenu, &OP_MainDef, 60, 30); +menu_t OP_AllControlsDef = CONTROLMENUSTYLE(MN_NONE, OP_AllControlsMenu, &OP_ControlsDef); +menu_t OP_Joystick1Def = DEFAULTMENUSTYLE(MN_NONE, "M_CONTRO", OP_Joystick1Menu, &OP_AllControlsDef, 60, 30); +menu_t OP_Joystick2Def = DEFAULTMENUSTYLE(MN_NONE, "M_CONTRO", OP_Joystick2Menu, &OP_AllControlsDef, 60, 30); +menu_t OP_Joystick3Def = DEFAULTMENUSTYLE(MN_NONE, "M_CONTRO", OP_Joystick3Menu, &OP_AllControlsDef, 60, 30); +menu_t OP_Joystick4Def = DEFAULTMENUSTYLE(MN_NONE, "M_CONTRO", OP_Joystick4Menu, &OP_AllControlsDef, 60, 30); +menu_t OP_JoystickSetDef = +{ + MN_NONE, + "M_CONTRO", + sizeof (OP_JoystickSetMenu)/sizeof (menuitem_t), + &OP_Joystick1Def, + OP_JoystickSetMenu, + M_DrawJoystick, + 50, 40, + 0, + NULL +}; + +menu_t OP_VideoOptionsDef = +{ + MN_NONE, + "M_VIDEO", + sizeof(OP_VideoOptionsMenu)/sizeof(menuitem_t), + &OP_MainDef, + OP_VideoOptionsMenu, + M_DrawVideoMenu, + 30, 30, + 0, + NULL +}; + +menu_t OP_VideoModeDef = +{ + MN_NONE, + "M_VIDEO", + 1, + &OP_VideoOptionsDef, + OP_VideoModeMenu, + M_DrawVideoMode, + 48, 26, + 0, + NULL +}; + +menu_t OP_SoundOptionsDef = +{ + MN_NONE, + "M_SOUND", + sizeof (OP_SoundOptionsMenu)/sizeof (menuitem_t), + &OP_MainDef, + OP_SoundOptionsMenu, + M_DrawSkyRoom, + 30, 30, + 0, + NULL +}; + +menu_t OP_HUDOptionsDef = +{ + MN_NONE, + "M_HUD", + sizeof (OP_HUDOptionsMenu)/sizeof (menuitem_t), + &OP_MainDef, + OP_HUDOptionsMenu, + M_DrawHUDOptions, + 30, 30, + 0, + NULL +}; + +menu_t OP_ChatOptionsDef = DEFAULTMENUSTYLE(MN_NONE, "M_HUD", OP_ChatOptionsMenu, &OP_HUDOptionsDef, 30, 30); + +menu_t OP_GameOptionsDef = DEFAULTMENUSTYLE(MN_NONE, "M_GAME", OP_GameOptionsMenu, &OP_MainDef, 30, 30); +menu_t OP_ServerOptionsDef = DEFAULTMENUSTYLE(MN_NONE, "M_SERVER", OP_ServerOptionsMenu, &OP_MainDef, 24, 30); +#ifndef NONET +menu_t OP_AdvServerOptionsDef = DEFAULTMENUSTYLE(MN_NONE, "M_SERVER", OP_AdvServerOptionsMenu, &OP_ServerOptionsDef, 24, 30); +#endif + +//menu_t OP_NetgameOptionsDef = DEFAULTMENUSTYLE(MN_NONE, "M_SERVER", OP_NetgameOptionsMenu, &OP_ServerOptionsDef, 30, 30); +//menu_t OP_GametypeOptionsDef = DEFAULTMENUSTYLE(MN_NONE, "M_SERVER", OP_GametypeOptionsMenu, &OP_ServerOptionsDef, 30, 30); +//menu_t OP_ChatOptionsDef = DEFAULTMENUSTYLE(MN_NONE, "M_GAME", OP_ChatOptionsMenu, &OP_GameOptionsDef, 30, 30); +menu_t OP_MonitorToggleDef = +{ + MN_NONE, + "M_GAME", + sizeof (OP_MonitorToggleMenu)/sizeof (menuitem_t), + &OP_GameOptionsDef, + OP_MonitorToggleMenu, + M_DrawMonitorToggles, + 47, 30, + 0, + NULL +}; + +#ifdef HWRENDER +static void M_OpenGLOptionsMenu(void) +{ + if (rendermode == render_opengl) + M_SetupNextMenu(&OP_OpenGLOptionsDef); + else + M_StartMessage(M_GetText("You must be in OpenGL mode\nto access this menu.\n\n(Press a key)\n"), NULL, MM_NOTHING); +} + +menu_t OP_OpenGLOptionsDef = DEFAULTMENUSTYLE(MN_NONE, "M_VIDEO", OP_OpenGLOptionsMenu, &OP_VideoOptionsDef, 30, 30); +#endif +menu_t OP_DataOptionsDef = DEFAULTMENUSTYLE(MN_NONE, "M_DATA", OP_DataOptionsMenu, &OP_MainDef, 60, 30); +menu_t OP_ScreenshotOptionsDef = DEFAULTMENUSTYLE(MN_NONE, "M_SCSHOT", OP_ScreenshotOptionsMenu, &OP_DataOptionsDef, 30, 30); +menu_t OP_AddonsOptionsDef = DEFAULTMENUSTYLE(MN_NONE, "M_ADDONS", OP_AddonsOptionsMenu, &OP_DataOptionsDef, 30, 30); +#ifdef HAVE_DISCORDRPC +menu_t OP_DiscordOptionsDef = DEFAULTMENUSTYLE(MN_NONE, NULL, OP_DiscordOptionsMenu, &OP_DataOptionsDef, 30, 30); +#endif +menu_t OP_EraseDataDef = DEFAULTMENUSTYLE(MN_NONE, "M_DATA", OP_EraseDataMenu, &OP_DataOptionsDef, 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. +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("media")+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1; + char *gpath = malloc(glen); + INT32 i; + + if (!gpath) + return; + + sprintf(gpath,"%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value)); + + CV_StealthSetValue(&cv_dummystaff, 0); + + active = 0; + 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 (levellistmode != LLM_BREAKTHECAPSULES) { + 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 (menuactive && cv_nextmap.value) + { + INT32 gt = cv_newgametype.value; + + if (!mapheaderinfo[cv_nextmap.value-1]) + P_AllocMapHeader((INT16)(cv_nextmap.value-1)); + + if (gt >= 0 && gt < gametypecount && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & gametypetol[gt])) + CV_SetValue(&cv_nextmap, M_FindFirstMap(gt)); + } +} + +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); +} + +void Moviemode_option_Onchange(void) +{ + ; +} + +// ========================================================================== +// END ORGANIZATION STUFF. +// ========================================================================== + +// current menudef +menu_t *currentMenu = &MainDef; + +// ========================================================================= +// MENU PRESENTATION PARAMETER HANDLING (BACKGROUNDS) +// ========================================================================= + +// menu IDs are equal to current/prevMenu in most cases, except MN_SPECIAL when we don't want to operate on Message, Pause, etc. +UINT32 prevMenuId = 0; +UINT32 activeMenuId = 0; + +menupres_t menupres[NUMMENUTYPES]; + +void M_InitMenuPresTables(void) +{ + INT32 i; + + // Called in d_main before SOC can get to the tables + // Set menupres defaults + for (i = 0; i < NUMMENUTYPES; i++) + { + // so-called "undefined" + menupres[i].fadestrength = -1; + menupres[i].hidetitlepics = -1; // inherits global hidetitlepics + menupres[i].ttmode = TTMODE_NONE; + menupres[i].ttscale = UINT8_MAX; + menupres[i].ttname[0] = 0; + menupres[i].ttx = INT16_MAX; + menupres[i].tty = INT16_MAX; + menupres[i].ttloop = INT16_MAX; + menupres[i].tttics = UINT16_MAX; + menupres[i].enterwipe = -1; + menupres[i].exitwipe = -1; + menupres[i].bgcolor = -1; + menupres[i].titlescrollxspeed = INT32_MAX; + menupres[i].titlescrollyspeed = INT32_MAX; + menupres[i].bghide = true; + // default true + menupres[i].enterbubble = true; + menupres[i].exitbubble = true; + + if (i != MN_MAIN) + { + menupres[i].muslooping = true; + } + if (i == MN_SP_TIMEATTACK) + strncpy(menupres[i].musname, "_recat", 7); + else if (i == MN_SP_NIGHTSATTACK) + strncpy(menupres[i].musname, "_nitat", 7); + else if (i == MN_SP_MARATHON) + strncpy(menupres[i].musname, "spec8", 6); + else if (i == MN_SP_PLAYER || i == MN_SR_PLAYER) + strncpy(menupres[i].musname, "_chsel", 7); + else if (i == MN_SR_SOUNDTEST) + { + *menupres[i].musname = '\0'; + menupres[i].musstop = true; + } + } +} + +// ========================================================================= +// 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[0]) + { + 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)) + { + if (cv == &cv_digmusicvolume || cv == &cv_soundvolume) + { + choice *= 5; + } + + 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; + else if (cv == &cv_maxping) + choice *= 50; +#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 + + 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 + + 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-1) * cv_deadzone[0].value) >> FRACBITS; + if (ev->data3 != INT32_MAX) + { + if (Joystick[0].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[0].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; + } + } + } + + if (ch == -1) + return false; + else if (ch == gamecontrol[0][gc_systemmenu][0] || ch == gamecontrol[0][gc_systemmenu][1]) // allow remappable ESC key + ch = KEY_ESCAPE; + else if ((ch == gamecontrol[0][gc_accelerate][0] || ch == gamecontrol[0][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: // Fullscreen + CV_AddValue(&cv_fullscreen, 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[0][gc_brake][0] || ch == gamecontrol[0][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_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) + { + + if (shiftdown && ch >= 32 && ch <= 127) + ch = shiftxform[ch]; + if (M_ChangeStringCvar(ch)) + return true; + else + routine = NULL; + } + else + routine = M_ChangeCvar; + } + + if (currentMenu == &PlaybackMenuDef && !con_destlines) + { + playback_last_menu_interaction_leveltime = leveltime; + // Flip left/right with up/down for the playback menu, since it's a horizontal icon row. + switch (ch) + { + case KEY_LEFTARROW: ch = KEY_UPARROW; break; + case KEY_UPARROW: ch = KEY_RIGHTARROW; break; + case KEY_RIGHTARROW: ch = KEY_DOWNARROW; break; + case KEY_DOWNARROW: ch = KEY_LEFTARROW; break; + + // arbitrary keyboard shortcuts because fuck you + + case '\'': // toggle freecam + M_PlaybackToggleFreecam(0); + break; + + case ']': // ffw / advance frame (depends on if paused or not) + if (paused) + M_PlaybackAdvance(0); + else + M_PlaybackFastForward(0); + break; + + case '[': // rewind /backupframe, uses the same function + M_PlaybackRewind(0); + break; + + case '\\': // pause + M_PlaybackPause(0); + break; + + // viewpoints, an annoyance (tm) + case '-': // viewpoint minus + M_PlaybackSetViews(-1); // yeah lol. + break; + + case '=': // viewpoint plus + M_PlaybackSetViews(1); // yeah lol. + break; + + // switch viewpoints: + case '1': // viewpoint for p1 (also f12) + // maximum laziness: + if (!demo.freecam) + G_AdjustView(1, 1, true); + break; + case '2': // viewpoint for p2 + if (!demo.freecam) + G_AdjustView(2, 1, true); + break; + case '3': // viewpoint for p3 + if (!demo.freecam) + G_AdjustView(3, 1, true); + break; + case '4': // viewpoint for p4 + if (!demo.freecam) + G_AdjustView(4, 1, true); + break; + + default: break; + } + } + + // 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 (currentMenu == &PlaybackMenuDef) + { + boolean held = (boolean)playback_enterheld; + if (held) + return true; + playback_enterheld = 3; + } + + 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 addons\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) + { + 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; +} + +// special responder for demos +boolean M_DemoResponder(event_t *ev) +{ + + INT32 ch = -1; // cur event data + boolean eatinput = false; // :omnom: + + //should be accounted for beforehand but just to be safe... + if (!demo.playback || demo.title) + 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 && !con_destlines) // not while the console is on please + { + ch = ev->data1; + // since this is ONLY for demos, there isn't MUCH for us to do. + // mirrored from m_responder + + switch (ch) + { + // arbitrary keyboard shortcuts because fuck you + + case '\'': // toggle freecam + M_PlaybackToggleFreecam(0); + eatinput = true; + break; + + case ']': // ffw / advance frame (depends on if paused or not) + if (paused) + M_PlaybackAdvance(0); + else + M_PlaybackFastForward(0); + eatinput = true; + break; + + case '[': // rewind /backupframe, uses the same function + M_PlaybackRewind(0); + break; + + case '\\': // pause + M_PlaybackPause(0); + eatinput = true; + break; + + // viewpoints, an annoyance (tm) + case '-': // viewpoint minus + M_PlaybackSetViews(-1); // yeah lol. + eatinput = true; + break; + + case '=': // viewpoint plus + M_PlaybackSetViews(1); // yeah lol. + eatinput = true; + break; + + // switch viewpoints: + case '1': // viewpoint for p1 (also f12) + // maximum laziness: + if (!demo.freecam) + G_AdjustView(1, 1, true); + break; + case '2': // viewpoint for p2 + if (!demo.freecam) + G_AdjustView(2, 1, true); + break; + case '3': // viewpoint for p3 + if (!demo.freecam) + G_AdjustView(3, 1, true); + break; + case '4': // viewpoint for p4 + if (!demo.freecam) + G_AdjustView(4, 1, true); + break; + + default: break; + } + + } + return eatinput; +} + + +// +// 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 && currentMenu != &PlaybackMenuDef) // Replay playback has its own background + 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) +{ + // intro might call this repeatedly + if (menuactive) + { + CON_ToggleOff(); // move away console + return; + } + + menuactive = true; + + if (demo.playback) + { + currentMenu = &PlaybackMenuDef; + playback_last_menu_interaction_leveltime = leveltime; + } + else if (!Playing()) + { + // Secret menu! + //MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED); + + currentMenu = &MainDef; +#ifdef TESTERS + itemOn = multiplr; +#else + itemOn = singleplr; +#endif + } + 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_addons].alphaKey = 8; + MPauseMenu[mpause_scramble].alphaKey = 8; + MPauseMenu[mpause_switchmap].alphaKey = 24; + + 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; + } + +#ifdef HAVE_DISCORDRPC + { + UINT8 i; + + for (i = 0; i < mpause_discordrequests; i++) + MPauseMenu[i].alphaKey -= 8; + + MPauseMenu[mpause_discordrequests].alphaKey = MPauseMenu[i].alphaKey; + + M_RefreshPauseMenu(); + } +#endif + + 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; + hidetitlemap = 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; + } + } + } + + hidetitlemap = false; +} + +// Guess I'll put this here, idk +boolean M_MouseNeeded(void) +{ + return false; +} + +// +// M_Ticker +// +void M_Ticker(void) +{ + // reset input trigger + noFurtherInput = false; + + if (dedicated) + return; + + if (--skullAnimCounter <= 0) + skullAnimCounter = 8; + + followertimer++; + + if (currentMenu == &PlaybackMenuDef) + { + if (playback_enterheld > 0) + playback_enterheld--; + } + else + playback_enterheld = 0; + + //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; + } + +#if defined (MASTERSERVER) && defined (HAVE_THREADS) + I_lock_mutex(&ms_ServerList_mutex); + { + if (ms_ServerList) + { + CL_QueryServerList(ms_ServerList); + free(ms_ServerList); + ms_ServerList = NULL; + } + } + I_unlock_mutex(ms_ServerList_mutex); +#endif +} + +// +// M_Init +// +void M_Init(void) +{ + UINT8 i; + + CV_RegisterVar(&cv_nextmap); + CV_RegisterVar(&cv_newgametype); + CV_RegisterVar(&cv_chooseskin); + CV_RegisterVar(&cv_autorecord); + + if (dedicated) + return; + + COM_AddCommand("manual", Command_Manual_f); + + // 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_dummystaff); + + CV_RegisterVar(&cv_dummygpdifficulty); + CV_RegisterVar(&cv_dummygpencore); + CV_RegisterVar(&cv_dummygpcup); + + 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 = IT_DISABLED; +#endif + +#ifndef NONET + CV_RegisterVar(&cv_serversort); +#endif +} + +void M_InitCharacterTables(void) +{ + UINT8 i; + + // Setup PlayerMenu table + for (i = 0; i < MAXSKINS; i++) + { + PlayerMenu[i].status = (i < 4 ? IT_CALL : IT_DISABLED); + PlayerMenu[i].patch = PlayerMenu[i].text = NULL; + PlayerMenu[i].itemaction = M_ChoosePlayer; + PlayerMenu[i].alphaKey = 0; + } + + // Setup description table + for (i = 0; i < MAXSKINS; i++) + { + if (i == 0) + { + strcpy(description[i].notes, "\x82Sonic\x80 is the fastest of the three, but also the hardest to control. Beginners beware, but experts will find Sonic very powerful.\n\n\x82""Ability:\x80 Speed Thok\nDouble jump to zoom forward with a huge burst of speed.\n\n\x82Tip:\x80 Simply letting go of forward does not slow down in SRB2. To slow down, hold the opposite direction."); + strcpy(description[i].picname, ""); + strcpy(description[i].skinname, "sonic"); + } + else if (i == 1) + { + strcpy(description[i].notes, "\x82Tails\x80 is the most mobile of the three, but has the slowest speed. Because of his mobility, he's well-\nsuited to beginners.\n\n\x82""Ability:\x80 Fly\nDouble jump to start flying for a limited time. Repetitively hit the jump button to ascend.\n\n\x82Tip:\x80 To quickly descend while flying, hit the spin button."); + strcpy(description[i].picname, ""); + strcpy(description[i].skinname, "tails"); + } + else if (i == 2) + { + strcpy(description[i].notes, "\x82Knuckles\x80 is well-\nrounded and can destroy breakable walls simply by touching them, but he can't jump as high as the other two.\n\n\x82""Ability:\x80 Glide & Climb\nDouble jump to glide in the air as long as jump is held. Glide into a wall to climb it.\n\n\x82Tip:\x80 Press spin while climbing to jump off the wall; press jump instead to jump off\nand face away from\nthe wall."); + strcpy(description[i].picname, ""); + strcpy(description[i].skinname, "knuckles"); + } + else if (i == 3) + { + strcpy(description[i].notes, "\x82Sonic & Tails\x80 team up to take on Dr. Eggman!\nControl Sonic while Tails desperately struggles to keep up.\n\nPlayer 2 can control Tails directly by setting the controls in the options menu.\nTails's directional controls are relative to Player 1's camera.\n\nTails can pick up Sonic while flying and carry him around."); + strcpy(description[i].picname, "CHRS&T"); + strcpy(description[i].skinname, "sonic&tails"); + } + else + { + strcpy(description[i].notes, "???"); + strcpy(description[i].picname, ""); + strcpy(description[i].skinname, ""); + } + } +} + +// ========================================================================== +// 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, 0, 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, 159); + //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, 0, W_CachePatchNum(viewborderlump[BRDR_T], PU_CACHE)); + V_DrawScaledPatch(cx, y + boff + boxlines*step, 0, 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, false), 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; + 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_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 (mapheaderinfo[gamemap-1]->actnum > 0) + V_DrawString(40, 28, highlightflags, va("%s %s %d", 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 (mapheaderinfo[gamemap-1]->actnum > 0) + V_DrawString(40, 28, highlightflags, va("%s %d", 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, false), 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 + +#ifdef HAVE_DISCORDRPC + // kind of hackily baked in here + if (currentMenu == &MPauseDef && discordRequestList != NULL) + { + const tic_t freq = TICRATE/2; + + if ((leveltime % freq) >= freq/2) + { + V_DrawFixedPatch(204 * FRACUNIT, + (currentMenu->y + MPauseMenu[mpause_discordrequests].alphaKey - 1) * FRACUNIT, + FRACUNIT, + 0, + W_CachePatchName("K_REQUE2", PU_CACHE), + NULL + ); + } + } +#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; + case IT_TRANSTEXT: + if (currentMenu->menuitems[i].alphaKey) + y = currentMenu->y+currentMenu->menuitems[i].alphaKey; + /* FALLTHRU */ + case IT_TRANSTEXT2: + V_DrawCenteredString(x, y, V_TRANSLUCENT, 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(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[0].string); + if (j == -1) + j = 0; + + CV_SetValue(&cv_chooseskin, j+1); // This causes crash sometimes?! + + return; +} + +// +// M_PrepareCupList +// +static void M_PrepareCupList(void) +{ + cupheader_t *cup = kartcupheaders; + INT32 i = 0; + + memset(dummygpcup_cons_t, 0, sizeof (dummygpcup_cons_t)); + + while (cup != NULL) + { + dummygpcup_cons_t[i].strvalue = cup->name; + dummygpcup_cons_t[i].value = i+1; + // this will probably crash or do something stupid at over 50 cups, + // but this is all behavior that gets completely overwritten in new-menus, so I'm not worried + i++; + cup = cup->next; + } + + for (; i < 50; i++) + { + dummygpcup_cons_t[i].strvalue = NULL; + dummygpcup_cons_t[i].value = 0; + } + + CV_SetValue(&cv_dummygpcup, 1); // This causes crash sometimes?! +} + +// 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 >= 0 && gt < gametypecount && mapheaderinfo[mapnum]->typeoflevel & gametypetol[gt]) + 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_TIMEATTACK: + case LLM_BREAKTHECAPSULES: + if (!(mapheaderinfo[mapnum]->menuflags & LF2_TIMEATTACK)) + return false; + + if ((levellistmode == LLM_TIMEATTACK && !(mapheaderinfo[mapnum]->typeoflevel & TOL_RACE)) + || (levellistmode == LLM_BREAKTHECAPSULES && !(mapheaderinfo[mapnum]->typeoflevel & TOL_BATTLE))) + 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_VISITNEEDED) && !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 = +{ + MN_NONE, // id + 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 = ((demo.playback) ? &PlaybackMenuDef : &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) +{ + patch_t *patch = W_CachePatchName(currentMenu->menuitems[itemOn].text,PU_CACHE); + if (patch->width <= BASEVIDWIDTH) + 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 addons!" +#define LOCATIONSTRING2 "Visit \x88SRB2.ORG/MODS\x80 to get & make addons!" + +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, 0); + V_DrawFill(x + width, vpadding, 1, h, 0); + V_DrawFill(x - 1, vpadding-1, width+2, 1, 0); + V_DrawFill(x - 1, vpadding+h, width+2, 1, 0); + + // 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 addons reached.\nA file could not be loaded.\nIf you wish to play with this addon, 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\nYou've loaded a gameplay-modifying addon.\n\nRecord Attack has been disabled, but you\ncan still play alone in local Multiplayer.\n\nIf you wish to play Record Attack mode, restart the game to disable loaded addons.\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 + { + y = FixedDiv(((ssize_t)(numwadfiles) - (ssize_t)(mainwads+1))< 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)[0]; + + 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, 159); + + // 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: + 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 replayScrollTitle = 0; +static SINT8 replayScrollDelay = TICRATE, replayScrollDir = 1; + +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_UP) + { + demolist[i].type = MD_SUBDIR; + sprintf(demolist[i].title, "UP"); + } + else 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, "....."); + } + } +} + +void M_ReplayHut(INT32 choice) +{ + (void)choice; + + if (!demo.inreplayhut) + { + snprintf(menupath, 1024, "%s"PATHSEP"media"PATHSEP"replay"PATHSEP"online"PATHSEP, srb2home); + menupathindex[(menudepthleft = menudepth-1)] = strlen(menupath); + } + if (!preparefilemenu(false, true)) + { + M_StartMessage("No replays found.\n\n(Press a key)\n", NULL, MM_NOTHING); + return; + } + else if (!demo.inreplayhut) + dir_on[menudepthleft] = 0; + demo.inreplayhut = true; + + replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; + + PrepReplayList(); + + menuactive = true; + M_SetupNextMenu(&MISC_ReplayHutDef); + G_SetGamestate(GS_TIMEATTACK); + titlemapinaction = TITLEMAP_OFF; // Nope don't give us HOMs please + + demo.rewinding = false; + CL_ClearRewinds(); + + S_ChangeMusicInternal("replst", true); +} + +static void M_HandleReplayHutList(INT32 choice) +{ + switch (choice) + { + case KEY_UPARROW: + if (dir_on[menudepthleft]) + dir_on[menudepthleft]--; + else + return; + //M_PrevOpt(); + + S_StartSound(NULL, sfx_menu1); + replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; + break; + + case KEY_DOWNARROW: + if (dir_on[menudepthleft] < sizedirmenu-1) + dir_on[menudepthleft]++; + else + return; + //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: + 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, true)) + { + 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, true)) + { + M_QuitReplayHut(); + return; + } + } + else + { + S_StartSound(NULL, sfx_menu1); + dir_on[menudepthleft] = 1; + PrepReplayList(); + } + } + 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, true)) + { + M_QuitReplayHut(); + return; + } + PrepReplayList(); + break; + default: + // We can't just use M_SetupNextMenu because that'll run ReplayDef's quitroutine and boot us back to the title screen! + currentMenu->lastOn = itemOn; + currentMenu = &MISC_ReplayStartDef; + + replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; + + switch (demolist[dir_on[menudepthleft]].addonstatus) + { + case DFILE_ERROR_CANNOTLOAD: + // Only show "Watch Replay Without Addons" + MISC_ReplayStartMenu[0].status = IT_DISABLED; + MISC_ReplayStartMenu[1].status = IT_CALL|IT_STRING; + //MISC_ReplayStartMenu[1].alphaKey = 0; + MISC_ReplayStartMenu[2].status = IT_DISABLED; + itemOn = 1; + break; + + case DFILE_ERROR_NOTLOADED: + case DFILE_ERROR_INCOMPLETEOUTOFORDER: + // Show "Load Addons and Watch Replay" and "Watch Replay Without Addons" + MISC_ReplayStartMenu[0].status = IT_CALL|IT_STRING; + MISC_ReplayStartMenu[1].status = IT_CALL|IT_STRING; + //MISC_ReplayStartMenu[1].alphaKey = 10; + MISC_ReplayStartMenu[2].status = IT_DISABLED; + itemOn = 0; + break; + + case DFILE_ERROR_EXTRAFILES: + case DFILE_ERROR_OUTOFORDER: + default: + // Show "Watch Replay" + MISC_ReplayStartMenu[0].status = IT_DISABLED; + MISC_ReplayStartMenu[1].status = IT_DISABLED; + MISC_ReplayStartMenu[2].status = IT_CALL|IT_STRING; + //MISC_ReplayStartMenu[2].alphaKey = 0; + itemOn = 2; + break; + } + } + + break; + } +} + +#define SCALEDVIEWWIDTH (vid.width/vid.dupx) +#define SCALEDVIEWHEIGHT (vid.height/vid.dupy) +static void DrawReplayHutReplayInfo(void) +{ + lumpnum_t lumpnum; + patch_t *patch; + UINT8 *colormap; + INT32 x, y, w, h; + + switch (demolist[dir_on[menudepthleft]].type) + { + case MD_NOTLOADED: + V_DrawCenteredString(160, 40, V_SNAPTOTOP, "Loading replay information..."); + break; + + case MD_INVALID: + V_DrawCenteredString(160, 40, V_SNAPTOTOP|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, 64, V_SNAPTOTOP|V_ALLOWLOWERCASE|V_TRANSLUCENT|highlightflags, "Recorded on an outdated version."); + /* FALLTHRU */ + default: + // Draw level stuff + x = 15; y = 15; + + // A 160x100 image of the level as entry MAPxxP + //CONS_Printf("%d %s\n", demolist[dir_on[menudepthleft]].map, G_BuildMapName(demolist[dir_on[menudepthleft]].map)); + lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(demolist[dir_on[menudepthleft]].map))); + if (lumpnum != LUMPERROR) + patch = W_CachePatchNum(lumpnum, PU_CACHE); + else + patch = W_CachePatchName("M_NOLVL", PU_CACHE); + + if (!(demolist[dir_on[menudepthleft]].kartspeed & DF_ENCORE)) + V_DrawSmallScaledPatch(x, y, V_SNAPTOTOP, patch); + else + { + w = SHORT(patch->width); + h = SHORT(patch->height); + V_DrawSmallScaledPatch(x+(w>>1), y, V_SNAPTOTOP|V_FLIP, patch); + + { + static angle_t rubyfloattime = 0; + const fixed_t rubyheight = FINESINE(rubyfloattime>>ANGLETOFINESHIFT); + V_DrawFixedPatch((x+(w>>2))<>2))<width), y+20, V_SNAPTOTOP, patch, colormap); + + 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)); + + if (cv_vhseffect.value) + V_DrawVhsEffect(false); + + // Draw menu choices + x = currentMenu->x; + y = currentMenu->y; + + if (itemOn > replaylistitem) + { + itemOn = replaylistitem; + dir_on[menudepthleft] = sizedirmenu-1; + replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; + } + else if (itemOn < replaylistitem) + { + dir_on[menudepthleft] = 0; + replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; + } + + if (itemOn == replaylistitem) + { + INT32 maxy; + // Scroll menu items if needed + cursory = y + currentMenu->menuitems[replaylistitem].alphaKey + dir_on[menudepthleft]*10; + maxy = y + currentMenu->menuitems[replaylistitem].alphaKey + sizedirmenu*10; + + if (cursory > maxy - 20) + cursory = maxy - 20; + + if (cursory - replayhutmenuy > SCALEDVIEWHEIGHT-50) + replayhutmenuy += (cursory-SCALEDVIEWHEIGHT-replayhutmenuy + 51)/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++) + { + INT32 localy = y + currentMenu->menuitems[i].alphaKey; + + if (localy < 65) + continue; + + if (i == itemOn) + cursory = localy; + + if ((currentMenu->menuitems[i].status & IT_DISPLAY)==IT_STRING) + V_DrawString(x, localy, V_SNAPTOTOP|V_SNAPTOLEFT, currentMenu->menuitems[i].text); + else + V_DrawString(x, localy, V_SNAPTOTOP|V_SNAPTOLEFT|highlightflags, currentMenu->menuitems[i].text); + } + + y += currentMenu->menuitems[replaylistitem].alphaKey; + + for (i = 0; i < (INT16)sizedirmenu; i++) + { + INT32 localy = y+i*10; + INT32 localx = x; + + if (localy < 65) + continue; + if (localy >= SCALEDVIEWHEIGHT) + break; + + if (demolist[i].type == MD_NOTLOADED && !processed_one_this_frame) + { + processed_one_this_frame = true; + G_LoadDemoInfo(&demolist[i]); + } + + if (demolist[i].type == MD_SUBDIR) + { + localx += 8; + V_DrawScaledPatch(x - 4, localy, V_SNAPTOTOP|V_SNAPTOLEFT, W_CachePatchName(dirmenu[i][DIR_TYPE] == EXT_UP ? "M_RBACK" : "M_RFLDR", PU_CACHE)); + } + + if (itemOn == replaylistitem && i == (INT16)dir_on[menudepthleft]) + { + cursory = localy; + + if (replayScrollDelay) + replayScrollDelay--; + else if (replayScrollDir > 0) + { + if (replayScrollTitle < (V_StringWidth(demolist[i].title, 0) - (SCALEDVIEWWIDTH - (x<<1)))<<1) + replayScrollTitle++; + else + { + replayScrollDelay = TICRATE; + replayScrollDir = -1; + } + } + else + { + if (replayScrollTitle > 0) + replayScrollTitle--; + else + { + replayScrollDelay = TICRATE; + replayScrollDir = 1; + } + } + + V_DrawString(localx - (replayScrollTitle>>1), localy, V_SNAPTOTOP|V_SNAPTOLEFT|highlightflags|V_ALLOWLOWERCASE, demolist[i].title); + } + else + V_DrawString(localx, localy, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, demolist[i].title); + } + + // Draw scrollbar + y = sizedirmenu*10 + currentMenu->menuitems[replaylistitem].alphaKey + 30; + if (y > SCALEDVIEWHEIGHT-80) + { + V_DrawFill(BASEVIDWIDTH-4, 75, 4, SCALEDVIEWHEIGHT-80, V_SNAPTOTOP|V_SNAPTORIGHT|159); + V_DrawFill(BASEVIDWIDTH-3, 76 + (SCALEDVIEWHEIGHT-80) * replayhutmenuy / y, 2, (((SCALEDVIEWHEIGHT-80) * (SCALEDVIEWHEIGHT-80))-1) / y - 1, V_SNAPTOTOP|V_SNAPTORIGHT|149); + } + + // Draw the cursor + V_DrawScaledPatch(currentMenu->x - 24, cursory, V_SNAPTOTOP|V_SNAPTOLEFT, + W_CachePatchName("M_CURSOR", PU_CACHE)); + V_DrawString(currentMenu->x, cursory, V_SNAPTOTOP|V_SNAPTOLEFT|highlightflags, currentMenu->menuitems[itemOn].text); + + // Now draw some replay info! + V_DrawFill(10, 10, 300, 60, V_SNAPTOTOP|159); + + if (itemOn == replaylistitem) + { + DrawReplayHutReplayInfo(); + } +} + +static void M_DrawReplayStartMenu(void) +{ + const char *warning; + UINT8 i; + + M_DrawGenericBackgroundMenu(); + +#define STARTY 62-(replayScrollTitle>>1) + // Draw rankings beyond first + for (i = 1; i < MAXPLAYERS && demolist[dir_on[menudepthleft]].standings[i].ranking; i++) + { + patch_t *patch; + UINT8 *colormap; + + V_DrawRightAlignedString(BASEVIDWIDTH-100, STARTY + i*20, V_SNAPTOTOP|highlightflags, va("%2d", demolist[dir_on[menudepthleft]].standings[i].ranking)); + V_DrawThinString(BASEVIDWIDTH-96, STARTY + i*20, V_SNAPTOTOP|V_ALLOWLOWERCASE, demolist[dir_on[menudepthleft]].standings[i].name); + + if (demolist[dir_on[menudepthleft]].standings[i].timeorscore == UINT32_MAX-1) + V_DrawThinString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, "NO CONTEST"); + else if (demolist[dir_on[menudepthleft]].gametype == GT_RACE) + V_DrawRightAlignedString(BASEVIDWIDTH-40, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d'%02d\"%02d", + G_TicsToMinutes(demolist[dir_on[menudepthleft]].standings[i].timeorscore, true), + G_TicsToSeconds(demolist[dir_on[menudepthleft]].standings[i].timeorscore), + G_TicsToCentiseconds(demolist[dir_on[menudepthleft]].standings[i].timeorscore) + )); + else + V_DrawString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d", demolist[dir_on[menudepthleft]].standings[i].timeorscore)); + + // Character face! + + // Lat: 08/06/2020: For some reason missing skins have their value set to 255 (don't even ask me why I didn't write this) + // and for an even STRANGER reason this passes the first check below, so we're going to make sure that the skin here ISN'T 255 before we do anything stupid. + + if (demolist[dir_on[menudepthleft]].standings[i].skin != 0xFF) + { + patch = faceprefix[demolist[dir_on[menudepthleft]].standings[i].skin][FACE_RANK]; + colormap = R_GetTranslationColormap( + demolist[dir_on[menudepthleft]].standings[i].skin, + demolist[dir_on[menudepthleft]].standings[i].color, + GTC_MENUCACHE); + } + else + { + patch = W_CachePatchName("M_NORANK", PU_CACHE); + colormap = R_GetTranslationColormap( + TC_RAINBOW, + demolist[dir_on[menudepthleft]].standings[i].color, + GTC_MENUCACHE); + } + + V_DrawMappedPatch(BASEVIDWIDTH-5 - SHORT(patch->width), STARTY + i*20, V_SNAPTOTOP, patch, colormap); + } +#undef STARTY + + // Handle scrolling rankings + if (replayScrollDelay) + replayScrollDelay--; + else if (replayScrollDir > 0) + { + if (replayScrollTitle < (i*20 - SCALEDVIEWHEIGHT + 100)<<1) + replayScrollTitle++; + else + { + replayScrollDelay = TICRATE; + replayScrollDir = -1; + } + } + else + { + if (replayScrollTitle > 0) + replayScrollTitle--; + else + { + replayScrollDelay = TICRATE; + replayScrollDir = 1; + } + } + + V_DrawFill(10, 10, 300, 60, V_SNAPTOTOP|159); + DrawReplayHutReplayInfo(); + + V_DrawString(10, 72, V_SNAPTOTOP|highlightflags|V_ALLOWLOWERCASE, demolist[dir_on[menudepthleft]].title); + + // Draw a warning prompt if needed + switch (demolist[dir_on[menudepthleft]].addonstatus) + { + case DFILE_ERROR_CANNOTLOAD: + warning = "Some addons in this replay cannot be loaded.\nYou can watch anyway, but desyncs may occur."; + break; + + case DFILE_ERROR_NOTLOADED: + case DFILE_ERROR_INCOMPLETEOUTOFORDER: + warning = "Loading addons will mark your game as modified, and Record Attack may be unavailable.\nYou can watch without loading addons, but desyncs may occur."; + break; + + case DFILE_ERROR_EXTRAFILES: + warning = "You have addons loaded that were not present in this replay.\nYou can watch anyway, but desyncs may occur."; + break; + + case DFILE_ERROR_OUTOFORDER: + warning = "You have this replay's addons loaded, but they are out of order.\nYou can watch anyway, but desyncs may occur."; + break; + + default: + return; + } + + V_DrawSmallString(4, BASEVIDHEIGHT-14, V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, warning); +} + +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; + + demo.inreplayhut = false; + + return true; +} + +static void M_HutStartReplay(INT32 choice) +{ + (void)choice; + + M_ClearMenus(false); + demo.loadfiles = (itemOn == 0); + demo.ignorefiles = (itemOn != 0); + + G_DoPlayDemo(demolist[dir_on[menudepthleft]].filepath); +} + +void M_SetPlaybackMenuPointer(void) +{ + itemOn = playback_pause; +} + +static void M_DrawPlaybackMenu(void) +{ + INT16 i; + patch_t *icon; + UINT8 *activemap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GOLD, GTC_MENUCACHE); + UINT32 transmap = max(0, (INT32)(leveltime - playback_last_menu_interaction_leveltime - 4*TICRATE)) / 5; + transmap = min(8, transmap) << V_ALPHASHIFT; + + if (leveltime - playback_last_menu_interaction_leveltime >= 6*TICRATE) + playback_last_menu_interaction_leveltime = leveltime - 6*TICRATE; + + // Toggle items + if (paused && !demo.rewinding) + { + PlaybackMenu[playback_pause].status = PlaybackMenu[playback_fastforward].status = PlaybackMenu[playback_rewind].status = IT_DISABLED; + PlaybackMenu[playback_resume].status = PlaybackMenu[playback_advanceframe].status = PlaybackMenu[playback_backframe].status = IT_CALL|IT_STRING; + + if (itemOn >= playback_rewind && itemOn <= playback_fastforward) + itemOn += playback_backframe - playback_rewind; + } + else + { + PlaybackMenu[playback_pause].status = PlaybackMenu[playback_fastforward].status = PlaybackMenu[playback_rewind].status = IT_CALL|IT_STRING; + PlaybackMenu[playback_resume].status = PlaybackMenu[playback_advanceframe].status = PlaybackMenu[playback_backframe].status = IT_DISABLED; + + if (itemOn >= playback_backframe && itemOn <= playback_advanceframe) + itemOn -= playback_backframe - playback_rewind; + } + + if (modeattacking) + { + for (i = playback_viewcount; i <= playback_view4; i++) + PlaybackMenu[i].status = IT_DISABLED; + PlaybackMenu[playback_freecamera].alphaKey = 72; + PlaybackMenu[playback_quit].alphaKey = 88; + + currentMenu->x = BASEVIDWIDTH/2 - 52; + } + else + { + PlaybackMenu[playback_viewcount].status = IT_ARROWS|IT_STRING; + + for (i = 0; i <= r_splitscreen; i++) + PlaybackMenu[playback_view1+i].status = IT_ARROWS|IT_STRING; + for (i = r_splitscreen+1; i < 4; i++) + PlaybackMenu[playback_view1+i].status = IT_DISABLED; + + PlaybackMenu[playback_freecamera].alphaKey = 156; + PlaybackMenu[playback_quit].alphaKey = 172; + currentMenu->x = BASEVIDWIDTH/2 - 88; + } + + // wip + //M_DrawTextBox(currentMenu->x-68, currentMenu->y-7, 15, 15); + //M_DrawCenteredMenu(); + + for (i = 0; i < currentMenu->numitems; i++) + { + UINT8 *inactivemap = NULL; + + if (i >= playback_view1 && i <= playback_view4) + { + if (modeattacking) continue; + + if (r_splitscreen >= i - playback_view1) + { + INT32 ply = displayplayers[i - playback_view1]; + + icon = faceprefix[players[ply].skin][FACE_RANK]; + if (i != itemOn) + inactivemap = R_GetTranslationColormap(players[ply].skin, players[ply].skincolor, GTC_MENUCACHE); + } + else if (currentMenu->menuitems[i].patch && W_CheckNumForName(currentMenu->menuitems[i].patch) != LUMPERROR) + icon = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE); + else + icon = W_CachePatchName("PLAYRANK", PU_CACHE); // temp + } + else if (currentMenu->menuitems[i].status == IT_DISABLED) + continue; + else if (currentMenu->menuitems[i].patch && W_CheckNumForName(currentMenu->menuitems[i].patch) != LUMPERROR) + icon = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE); + else + icon = W_CachePatchName("PLAYRANK", PU_CACHE); // temp + + if ((i == playback_fastforward && cv_playbackspeed.value > 1) || (i == playback_rewind && demo.rewinding)) + V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].alphaKey, currentMenu->y, transmap|V_SNAPTOTOP, icon, R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_JAWZ, GTC_MENUCACHE)); + else + V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].alphaKey, currentMenu->y, transmap|V_SNAPTOTOP, icon, (i == itemOn) ? activemap : inactivemap); + + if (i == itemOn) + { + V_DrawCharacter(currentMenu->x + currentMenu->menuitems[i].alphaKey + 4, currentMenu->y + 14, + '\x1A' | transmap|V_SNAPTOTOP|highlightflags, false); + + V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y + 18, transmap|V_SNAPTOTOP|V_ALLOWLOWERCASE, currentMenu->menuitems[i].text); + + if ((currentMenu->menuitems[i].status & IT_TYPE) == IT_ARROWS) + { + char *str; + + if (!(i == playback_viewcount && r_splitscreen == 3)) + V_DrawCharacter(BASEVIDWIDTH/2 - 4, currentMenu->y + 28 - (skullAnimCounter/5), + '\x1A' | transmap|V_SNAPTOTOP|highlightflags, false); // up arrow + + if (!(i == playback_viewcount && r_splitscreen == 0)) + V_DrawCharacter(BASEVIDWIDTH/2 - 4, currentMenu->y + 48 + (skullAnimCounter/5), + '\x1B' | transmap|V_SNAPTOTOP|highlightflags, false); // down arrow + + switch (i) + { + case playback_viewcount: + str = va("%d", r_splitscreen+1); + break; + + case playback_view1: + case playback_view2: + case playback_view3: + case playback_view4: + str = player_names[displayplayers[i - playback_view1]]; // 0 to 3 + break; + + default: // shouldn't ever be reached but whatever + continue; + } + + V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y + 38, transmap|V_SNAPTOTOP|V_ALLOWLOWERCASE|highlightflags, str); + } + } + } +} + +static void M_PlaybackRewind(INT32 choice) +{ + static tic_t lastconfirmtime; + + (void)choice; + + if (!demo.rewinding) + { + if (paused) + { + G_ConfirmRewind(leveltime-1); + paused = true; + S_PauseAudio(); + } + else + demo.rewinding = paused = true; + } + else if (lastconfirmtime + TICRATE/2 < I_GetTime()) + { + lastconfirmtime = I_GetTime(); + G_ConfirmRewind(leveltime); + } + + CV_SetValue(&cv_playbackspeed, 1); +} + +static void M_PlaybackPause(INT32 choice) +{ + (void)choice; + + paused = !paused; + + if (demo.rewinding) + { + G_ConfirmRewind(leveltime); + paused = true; + S_PauseAudio(); + } + else if (paused) + S_PauseAudio(); + else + S_ResumeAudio(); + + CV_SetValue(&cv_playbackspeed, 1); +} + +static void M_PlaybackFastForward(INT32 choice) +{ + (void)choice; + + if (demo.rewinding) + { + G_ConfirmRewind(leveltime); + paused = false; + S_ResumeAudio(); + } + CV_SetValue(&cv_playbackspeed, cv_playbackspeed.value == 1 ? 4 : 1); +} + +static void M_PlaybackAdvance(INT32 choice) +{ + (void)choice; + + paused = false; + TryRunTics(1); + paused = true; +} + + +static void M_PlaybackSetViews(INT32 choice) +{ + + if (demo.freecam) + return; // not here. + + if (choice > 0) + { + if (r_splitscreen < 3) + G_AdjustView(r_splitscreen + 2, 0, true); + } + else if (r_splitscreen) + { + r_splitscreen--; + R_ExecuteSetViewSize(); + } +} + +static void M_PlaybackAdjustView(INT32 choice) +{ + G_AdjustView(itemOn - playback_viewcount, (choice > 0) ? 1 : -1, true); +} + +// this one's rather tricky +static void M_PlaybackToggleFreecam(INT32 choice) +{ + (void)choice; + M_ClearMenus(true); + + // remove splitscreen: + splitscreen = 0; + R_ExecuteSetViewSize(); + + P_InitCameraCmd(); // init camera controls + if (!demo.freecam) // toggle on + { + demo.freecam = true; + democam.cam = &camera[0]; // this is rather useful + } + else // toggle off + { + demo.freecam = false; + // reset democam vars: + democam.cam = NULL; + democam.keyboardlook = false; // reset only these. localangle / aiming gets set before the cam does anything anyway + } +} + + +static void M_PlaybackQuit(INT32 choice) +{ + (void)choice; + G_StopDemo(); + + if (demo.inreplayhut) + M_ReplayHut(choice); + else if (modeattacking) + { + M_EndModeAttackRun(); + S_ChangeMusicInternal("racent", true); + } + else + D_StartTitle(); +} + +static void M_PandorasBox(INT32 choice) +{ + (void)choice; + CV_StealthSetValue(&cv_dummyrings, players[consoleplayer].rings); + CV_StealthSetValue(&cv_dummylives, players[consoleplayer].lives); + M_SetupNextMenu(&SR_PandoraDef); +} + +static boolean M_ExitPandorasBox(void) +{ + if (cv_dummyrings.value != players[consoleplayer].rings) + COM_ImmedExecute(va("setrings %d", cv_dummyrings.value)); + if (cv_dummylives.value != players[consoleplayer].lives) + COM_ImmedExecute(va("setlives %d", cv_dummylives.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[4].status = OP_MainMenu[5].status = (Playing() && !(server || IsPlayerAdmin(consoleplayer))) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); + + OP_MainMenu[8].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL); // Play credits + +#ifdef HAVE_DISCORDRPC + OP_DataOptionsMenu[4].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); // Erase data +#else + OP_DataOptionsMenu[3].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); // Erase data +#endif + + 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); +} + +void M_RefreshPauseMenu(void) +{ +#ifdef HAVE_DISCORDRPC + if (discordRequestList != NULL) + { + MPauseMenu[mpause_discordrequests].status = IT_STRING | IT_SUBMENU; + } + else + { + MPauseMenu[mpause_discordrequests].status = IT_GRAYEDOUT; + } +#endif +} + +// ====== +// CHEATS +// ====== + +static void M_UltimateCheat(INT32 choice) +{ + (void)choice; + I_Quit(); +} + +static void M_GetAllEmeralds(INT32 choice) +{ + (void)choice; + + emeralds = EMERALD_ALL; + 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_POWERLEVEL: + return va("Reach power level %d in %s", cond.requirement, (cond.extrainfo1 == PWRLV_BATTLE ? "Battle" : "Race")); + case UC_GAMECLEAR: + if (cond.requirement > 1) + return va("Beat game %d times", cond.requirement); + else + return va("Beat the game"); + case UC_OVERALLTIME: + return va("Get overall Time Attack 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_MAPENCORE: + return va("Beat %s in Encore Mode", 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), (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_GLOBAL) + continue; + + if (emblem->collected) + { + collected = recommendedflags; + V_DrawMappedPatch(12, 12+(28*j), 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), 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_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")); + + if (itemOn == 0) + lengthstring = 8*(sound_disabled ? 3 : 2); + else if (itemOn == 2) + lengthstring = 8*(digital_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 +// ================== + +#ifndef TESTERS +static void M_SinglePlayerMenu(INT32 choice) +{ + (void)choice; + + SP_MainMenu[spgrandprix].status = IT_CALL|IT_STRING; + SP_MainMenu[sptimeattack].status = + (M_SecretUnlocked(SECRET_TIMEATTACK)) ? IT_CALL|IT_STRING : IT_SECRET; + SP_MainMenu[spbreakthecapsules].status = + (M_SecretUnlocked(SECRET_BREAKTHECAPSULES)) ? 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_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]->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 = 88, 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 %d", + mapheaderinfo[mnum]->lvlttl, + mapheaderinfo[mnum]->actnum)); + else + V_DrawString(20, y, 0, va("%s %s %d", + 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, false), 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)); + + V_DrawString(20, 52, highlightflags, "Online Power Level:"); + V_DrawRightAlignedString(BASEVIDWIDTH-16, 52, 0, va("Race: %i", vspowerlevel[PWRLV_RACE])); + V_DrawRightAlignedString(BASEVIDWIDTH-16, 60, 0, va("Battle: %i", vspowerlevel[PWRLV_BATTLE])); + + for (i = 0; i < NUMMAPS; i++) + { + if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_TIMEATTACK)) + continue; + + if (!mainrecords[i] || mainrecords[i]->time <= 0) + { + mapsunfinished++; + continue; + } + + besttime += mainrecords[i]->time; + } + + V_DrawString(20, 70, 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, 70, (mapsunfinished ? warningflags : 0), beststr); + + if (mapsunfinished) + V_DrawRightAlignedString(BASEVIDWIDTH-16, 78, warningflags, va("(%d unfinished)", mapsunfinished)); + else + V_DrawRightAlignedString(BASEVIDWIDTH-16, 78, recommendedflags, "(complete)"); + + V_DrawString(32, 78, V_ALLOWLOWERCASE, va("x %d/%d", M_CountEmblems(), numemblems+numextraemblems)); + V_DrawSmallScaledPatch(20, 78, 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); + } +} + +static void M_GrandPrixTemp(INT32 choice) +{ + (void)choice; + M_PatchSkinNameTable(); + M_PrepareCupList(); + M_SetupNextMenu(&SP_GrandPrixTempDef); +} + +// Start Grand Prix! +static void M_StartGrandPrix(INT32 choice) +{ + cupheader_t *gpcup = kartcupheaders; + + (void)choice; + + if (gpcup == NULL) + { + // welp + I_Error("No cup definitions for GP\n"); + return; + } + + M_ClearMenus(true); + + memset(&grandprixinfo, 0, sizeof(struct grandprixinfo)); + + switch (cv_dummygpdifficulty.value) + { + case KARTSPEED_EASY: + case KARTSPEED_NORMAL: + case KARTSPEED_HARD: + grandprixinfo.gamespeed = cv_dummygpdifficulty.value; + break; + case KARTGP_MASTER: + grandprixinfo.gamespeed = KARTSPEED_HARD; + grandprixinfo.masterbots = true; + break; + default: + CONS_Alert(CONS_WARNING, "Invalid GP difficulty\n"); + grandprixinfo.gamespeed = KARTSPEED_NORMAL; + break; + } + + grandprixinfo.encore = (boolean)(cv_dummygpencore.value); + + while (gpcup != NULL && gpcup->id != cv_dummygpcup.value-1) + { + gpcup = gpcup->next; + } + + if (gpcup == NULL) + { + gpcup = kartcupheaders; + } + + grandprixinfo.cup = gpcup; + + grandprixinfo.gp = true; + grandprixinfo.roundnum = 1; + grandprixinfo.wonround = false; + + grandprixinfo.initalize = true; + + G_DeferedInitNew( + false, + G_BuildMapName(grandprixinfo.cup->levellist[0] + 1), + (UINT8)(cv_chooseskin.value - 1), + (UINT8)(cv_splitplayers.value - 1), + false + ); +} + +// =========== +// 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! + { + UINT8 *colormap = R_GetTranslationColormap(cv_chooseskin.value-1, cv_playercolor[0].value, GTC_MENUCACHE); + V_DrawMappedPatch(BASEVIDWIDTH-x - SHORT(faceprefix[cv_chooseskin.value-1][FACE_WANTED]->width), y, 0, faceprefix[cv_chooseskin.value-1][FACE_WANTED], 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, 159); + + if (levellistmode != LLM_BREAKTHECAPSULES) + { + 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, false), 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_TIMEATTACK; // Don't be dependent on cv_newgametype + + if (M_CountLevelsToShowInList() == 0) + { + M_StartMessage(M_GetText("No levels found for Time Attack.\n"),NULL,MM_NOTHING); + return; + } + + M_PatchSkinNameTable(); + + M_PrepareLevelSelect(); + M_SetupNextMenu(&SP_TimeAttackDef); + + G_SetGamestate(GS_TIMEATTACK); + titlemapinaction = TITLEMAP_OFF; // Nope don't give us HOMs please + + if (cv_nextmap.value) + Nextmap_OnChange(); + else + CV_AddValue(&cv_nextmap, 1); + + itemOn = tastart; // "Start" is selected. + + S_ChangeMusicInternal("racent", true); +} + +// Same as above, but sets a different levellistmode. Should probably be merged... +static void M_BreakTheCapsules(INT32 choice) +{ + (void)choice; + + memset(skins_cons_t, 0, sizeof (skins_cons_t)); + + levellistmode = LLM_BREAKTHECAPSULES; // Don't be dependent on cv_newgametype + + if (M_CountLevelsToShowInList() == 0) + { + M_StartMessage(M_GetText("No levels found for Break the Capsules.\n"),NULL,MM_NOTHING); + return; + } + + M_PatchSkinNameTable(); + + M_PrepareLevelSelect(); + M_SetupNextMenu(&SP_TimeAttackDef); + + G_SetGamestate(GS_TIMEATTACK); + titlemapinaction = TITLEMAP_OFF; // Nope don't give us HOMs please + + 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; +} + +// Player has selected the "START" from the time attack screen +static void M_ChooseTimeAttack(INT32 choice) +{ + char *gpath; + const size_t glen = strlen("media")+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1; + char nameofdemo[256]; + (void)choice; + emeralds = 0; + M_ClearMenus(true); + modeattacking = (levellistmode == LLM_BREAKTHECAPSULES ? ATTACKING_CAPSULES : ATTACKING_TIME); + + gpath = va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s", + srb2home, timeattackfolder); + M_MkdirEach(gpath, M_PathParts(gpath) - 3, 0755); + + if ((gpath = malloc(glen)) == NULL) + I_Error("Out of memory for replay filepath\n"); + + sprintf(gpath,"media"PATHSEP"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 = (levellistmode == LLM_BREAKTHECAPSULES ? ATTACKING_CAPSULES : ATTACKING_TIME); + 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 = (levellistmode == LLM_BREAKTHECAPSULES ? ATTACKING_CAPSULES : ATTACKING_TIME); // 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"media"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"media"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"media"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"media"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"media"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"media"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"media"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); +} + +void M_ModeAttackRetry(INT32 choice) +{ + (void)choice; + G_CheckDemoStatus(); // Cancel recording + if (modeattacking) + M_ChooseTimeAttack(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(); + + if (modeattacking) + currentMenu = &SP_TimeAttackDef; + + 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 +//=========================================================================== + +void +M_SetWaitingMode (int mode) +{ +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif + { + m_waiting_mode = mode; + } +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif +} + +int +M_GetWaitingMode (void) +{ + int mode; + +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif + { + mode = m_waiting_mode; + } +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif + + return mode; +} + +#ifdef MASTERSERVER +#ifdef HAVE_THREADS +static void +Spawn_masterserver_thread (const char *name, void (*thread)(int*)) +{ + int *id = malloc(sizeof *id); + + I_lock_mutex(&ms_QueryId_mutex); + { + *id = ms_QueryId; + } + I_unlock_mutex(ms_QueryId_mutex); + + I_spawn_thread(name, (I_thread_fn)thread, id); +} + +static int +Same_instance (int id) +{ + int okay; + + I_lock_mutex(&ms_QueryId_mutex); + { + okay = ( id == ms_QueryId ); + } + I_unlock_mutex(ms_QueryId_mutex); + + return okay; +} +#endif/*HAVE_THREADS*/ + +static void +Fetch_servers_thread (int *id) +{ + msg_server_t * server_list; + + (void)id; + + M_SetWaitingMode(M_WAITING_SERVERS); + +#ifdef HAVE_THREADS + server_list = GetShortServersList(*id); +#else + server_list = GetShortServersList(0); +#endif + + if (server_list) + { +#ifdef HAVE_THREADS + if (Same_instance(*id)) +#endif + { + M_SetWaitingMode(M_NOT_WAITING); + +#ifdef HAVE_THREADS + I_lock_mutex(&ms_ServerList_mutex); + { + ms_ServerList = server_list; + } + I_unlock_mutex(ms_ServerList_mutex); +#else + CL_QueryServerList(server_list); + free(server_list); +#endif + } +#ifdef HAVE_THREADS + else + { + free(server_list); + } +#endif + } + +#ifdef HAVE_THREADS + free(id); +#endif +} +#endif/*MASTERSERVER*/ + +#define SERVERHEADERHEIGHT 36 +#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 + + // first page of servers + serverlistpage = 0; + +#ifdef MASTERSERVER +#ifdef HAVE_THREADS + Spawn_masterserver_thread("fetch-servers", Fetch_servers_thread); +#else/*HAVE_THREADS*/ + Fetch_servers_thread(NULL); +#endif/*HAVE_THREADS*/ +#else/*MASTERSERVER*/ + CL_UpdateServerList(); +#endif/*MASTERSERVER*/ +} + +static void M_DrawConnectMenu(void) +{ + UINT16 i; + //const char *gt = "Unknown"; + //const char *spd = ""; + const char *pwr = "----"; + INT32 numPages = (serverlistcount+(SERVERS_PER_PAGE-1))/SERVERS_PER_PAGE; + int waiting; + + for (i = FIRSTSERVERLINE; i < min(localservercount, SERVERS_PER_PAGE)+FIRSTSERVERLINE; i++) + MP_ConnectMenu[i].status = IT_STRING | IT_SPACE; + + if (!numPages) + numPages = 1; + + // Page num + V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ConnectMenu[mp_connect_page].alphaKey, + highlightflags, va("%u of %d", serverlistpage+1, numPages)); + + // Horizontal line! + V_DrawFill(1, currentMenu->y+32, 318, 1, 0); + + if (serverlistcount <= 0) + V_DrawString(currentMenu->x,currentMenu->y+SERVERHEADERHEIGHT, 0, "No servers found"); + else + for (i = 0; i < min(serverlistcount - serverlistpage * SERVERS_PER_PAGE, SERVERS_PER_PAGE); i++) + { + INT32 slindex = i + serverlistpage * SERVERS_PER_PAGE; + UINT32 globalflags = ((serverlist[slindex].info.numberofplayer >= serverlist[slindex].info.maxplayer) ? V_TRANSLUCENT : 0) + |((itemOn == FIRSTSERVERLINE+i) ? highlightflags : 0)|V_ALLOWLOWERCASE; + + V_DrawString(currentMenu->x, S_LINEY(i), globalflags, serverlist[slindex].info.servername); + + V_DrawSmallString(currentMenu->x, S_LINEY(i)+8, globalflags, + va("Ping: %u", (UINT32)LONG(serverlist[slindex].info.time))); + + V_DrawSmallString(currentMenu->x+44,S_LINEY(i)+8, globalflags, + va("Players: %02d/%02d", serverlist[slindex].info.numberofplayer, serverlist[slindex].info.maxplayer)); + + V_DrawSmallString(currentMenu->x+108, S_LINEY(i)+8, globalflags, serverlist[slindex].info.gametypename); + + // display game speed for race gametypes + /* todo: send if the gametype is GTR_CIRCUIT, and uses game speed + if (serverlist[slindex].info.gametype == GT_RACE) + { + spd = kartspeed_cons_t[(serverlist[slindex].info.kartvars & SV_SPEEDMASK)+1].strvalue; + V_DrawSmallString(currentMenu->x+128, S_LINEY(i)+8, globalflags, va("(%s)", spd)); + } + */ + + pwr = "----"; + if (serverlist[slindex].info.avgpwrlv == -1) + pwr = "Off"; + else if (serverlist[slindex].info.avgpwrlv > 0) + pwr = va("%04d", serverlist[slindex].info.avgpwrlv); + V_DrawSmallString(currentMenu->x+171, S_LINEY(i)+8, globalflags, va("Power Level: %s", pwr)); + + // Don't use color flags intentionally, the global yellow color will auto override the text color code + if (serverlist[slindex].info.modifiedgame) + V_DrawSmallString(currentMenu->x+245, S_LINEY(i)+8, globalflags, "\x85" "Mod"); + if (serverlist[slindex].info.cheatsenabled) + V_DrawSmallString(currentMenu->x+265, S_LINEY(i)+8, globalflags, "\x83" "Cheats"); + + MP_ConnectMenu[i+FIRSTSERVERLINE].status = IT_STRING | IT_CALL; + } + + localservercount = serverlistcount; + + M_DrawGenericMenu(); + + waiting = M_GetWaitingMode(); + + if (waiting) + { + const char *message; + + if (waiting == M_WAITING_VERSION) + message = "Checking for updates..."; + else + message = "Searching for servers..."; + + // Display a little "please wait" message. + M_DrawTextBox(52, BASEVIDHEIGHT/2-10, 25, 3); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, 0, message); + V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2)+12, 0, "Please wait."); + } +} + +static boolean M_CancelConnect(void) +{ + D_CloseConnection(); + return true; +} + +// Ascending order, not descending. +// The casts are safe as long as the caller doesn't do anything stupid. +#define SERVER_LIST_ENTRY_COMPARATOR(key) \ +static int ServerListEntryComparator_##key(const void *entry1, const void *entry2) \ +{ \ + const serverelem_t *sa = (const serverelem_t*)entry1, *sb = (const serverelem_t*)entry2; \ + if (sa->info.key != sb->info.key) \ + return sa->info.key - sb->info.key; \ + return strcmp(sa->info.servername, sb->info.servername); \ +} + +// This does descending instead of ascending. +#define SERVER_LIST_ENTRY_COMPARATOR_REVERSE(key) \ +static int ServerListEntryComparator_##key##_reverse(const void *entry1, const void *entry2) \ +{ \ + const serverelem_t *sa = (const serverelem_t*)entry1, *sb = (const serverelem_t*)entry2; \ + if (sb->info.key != sa->info.key) \ + return sb->info.key - sa->info.key; \ + return strcmp(sb->info.servername, sa->info.servername); \ +} + +SERVER_LIST_ENTRY_COMPARATOR(time) +SERVER_LIST_ENTRY_COMPARATOR(numberofplayer) +SERVER_LIST_ENTRY_COMPARATOR_REVERSE(numberofplayer) +SERVER_LIST_ENTRY_COMPARATOR_REVERSE(maxplayer) + +static int ServerListEntryComparator_gametypename(const void *entry1, const void *entry2) +{ + const serverelem_t *sa = (const serverelem_t*)entry1, *sb = (const serverelem_t*)entry2; + int c; + if (( c = strcasecmp(sa->info.gametypename, sb->info.gametypename) )) + return c; + return strcmp(sa->info.servername, sb->info.servername); \ +} + +// Special one for modified state. +static int ServerListEntryComparator_modified(const void *entry1, const void *entry2) +{ + const serverelem_t *sa = (const serverelem_t*)entry1, *sb = (const serverelem_t*)entry2; + + // Modified acts as 2 points, cheats act as one point. + int modstate_a = (sa->info.cheatsenabled ? 1 : 0) | (sa->info.modifiedgame ? 2 : 0); + int modstate_b = (sb->info.cheatsenabled ? 1 : 0) | (sb->info.modifiedgame ? 2 : 0); + + if (modstate_a != modstate_b) + return modstate_a - modstate_b; + + // Default to strcmp. + return strcmp(sa->info.servername, sb->info.servername); +} +#endif + +void M_SortServerList(void) +{ +#ifndef NONET + switch(cv_serversort.value) + { + case 0: // Ping. + qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_time); + break; + case 1: // Modified state. + qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_modified); + break; + case 2: // Most players. + qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer_reverse); + break; + case 3: // Least players. + qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer); + break; + case 4: // Max players. + qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_maxplayer_reverse); + break; + case 5: // Gametype. + qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametypename); + break; + } +#endif +} + +#ifndef NONET +#ifdef UPDATE_ALERT +static void M_CheckMODVersion(int id) +{ + char updatestring[500]; + const char *updatecheck = GetMODVersion(id); + if(updatecheck) + { + sprintf(updatestring, UPDATE_ALERT_STRING, VERSIONSTRING, updatecheck); +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif + M_StartMessage(updatestring, NULL, MM_NOTHING); +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif + } +} +#endif/*UPDATE_ALERT*/ + +#if defined (UPDATE_ALERT) && defined (HAVE_THREADS) +static void +Check_new_version_thread (int *id) +{ + M_SetWaitingMode(M_WAITING_VERSION); + + M_CheckMODVersion(*id); + + if (Same_instance(*id)) + { + Fetch_servers_thread(id); + } + else + { + free(id); + } +} +#endif/*defined (UPDATE_ALERT) && defined (HAVE_THREADS)*/ + +static void M_ConnectMenu(INT32 choice) +{ + (void)choice; + // modified game check: no longer handled + // we don't request a restart unless the filelist differs + + // first page of servers + serverlistpage = 0; + M_SetupNextMenu(&MP_ConnectDef); + itemOn = 0; + +#if defined (MASTERSERVER) && defined (HAVE_THREADS) + I_lock_mutex(&ms_QueryId_mutex); + { + ms_QueryId++; + } + I_unlock_mutex(ms_QueryId_mutex); + + I_lock_mutex(&ms_ServerList_mutex); + { + if (ms_ServerList) + { + free(ms_ServerList); + ms_ServerList = NULL; + } + } + I_unlock_mutex(ms_ServerList_mutex); + +#ifdef UPDATE_ALERT + Spawn_masterserver_thread("check-new-version", Check_new_version_thread); +#else/*UPDATE_ALERT*/ + Spawn_masterserver_thread("fetch-servers", Fetch_servers_thread); +#endif/*UPDATE_ALERT*/ +#else/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/ +#ifdef UPDATE_ALERT + M_CheckMODVersion(0); +#endif/*UPDATE_ALERT*/ + M_Refresh(0); +#endif/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/ +} + +static void M_ConnectMenuModChecks(INT32 choice) +{ + (void)choice; + // okay never mind we want to COMMUNICATE to the player pre-emptively instead of letting them try and then get confused when it doesn't work + + if (modifiedgame) + { + M_StartMessage(M_GetText("You have addons loaded.\nYou won't be able to join netgames!\n\nTo play online, restart the game\nand don't load any addons.\nSRB2Kart will automatically add\neverything you need when you join.\n\n(Press a key)\n"),M_ConnectMenu,MM_EVENTHANDLER); + return; + } + + M_ConnectMenu(-1); +} +#endif //NONET + +//=========================================================================== +// Start Server Menu +//=========================================================================== + +// +// FindFirstMap +// +// Finds the first map of a particular gametype (or returns the current map) +// Defaults to 1 if nothing found. +// +static INT32 M_FindFirstMap(INT32 gtype) +{ + INT32 i; + + if (mapheaderinfo[gamemap] && (mapheaderinfo[gamemap]->typeoflevel & gtype)) + return gamemap; + + for (i = 0; i < NUMMAPS; i++) + { + if (!mapheaderinfo[i]) + continue; + if (!(mapheaderinfo[i]->typeoflevel & gtype)) + continue; + return i + 1; + } + + return 1; +} + +static void M_StartServer(INT32 choice) +{ + UINT8 ssplayers = cv_splitplayers.value-1; + + (void)choice; + + if (currentMenu == &MP_OfflineServerDef) + netgame = false; + else + netgame = true; + + multiplayer = true; + + strncpy(connectedservername, cv_servername.string, MAXSERVERNAME); + + // Still need to reset devmode + cv_debug = 0; + + if (demo.playback) + G_StopDemo(); + if (metalrecording) + G_StopMetalDemo(); + + if (!cv_nextmap.value) + CV_SetValue(&cv_nextmap, G_RandMap(G_TOLFlag(cv_newgametype.value), -1, false, 0, false, NULL)+1); + + if (cv_maxplayers.value < ssplayers+1) + CV_SetValue(&cv_maxplayers, ssplayers+1); + + if (splitscreen != ssplayers) + { + splitscreen = ssplayers; + SplitScreen_OnChange(); + } + + if (currentMenu == &MP_OfflineServerDef) // offline server + { + paused = false; + SV_StartSinglePlayerServer(); + multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... + D_MapChange(cv_nextmap.value, cv_newgametype.value, (cv_kartencore.value == 1), 1, 1, false, false); + } + else + { + D_MapChange(cv_nextmap.value, cv_newgametype.value, (cv_kartencore.value == 1), 1, 1, false, false); + COM_BufAddText("dummyconsvar 1\n"); + } + + M_ClearMenus(true); +} + +static void M_DrawLevelSelectOnly(boolean leftfade, boolean rightfade) +{ + lumpnum_t lumpnum; + patch_t *PictureOfLevel; + INT32 x, y, w, i, oldval, trans, dupadjust = ((vid.width/vid.dupx) - BASEVIDWIDTH)>>1; + + // A 160x100 image of the level as entry MAPxxP + if (cv_nextmap.value) + { + 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); + } + else + PictureOfLevel = W_CachePatchName("RANDOMLV", PU_CACHE); + + w = SHORT(PictureOfLevel->width)/2; + i = SHORT(PictureOfLevel->height)/2; + x = BASEVIDWIDTH/2 - w/2; + y = currentMenu->y + 130 + 8 - i; + + if (currentMenu->menuitems[itemOn].itemaction == &cv_nextmap && skullAnimCounter < 4) + trans = 0; + else + trans = G_GetGametypeColor(cv_newgametype.value); + + V_DrawFill(x-1, y-1, w+2, i+2, trans); // variable reuse... + + if ((cv_kartencore.value != 1) || gamestate == GS_TIMEATTACK || cv_newgametype.value != GT_RACE) + V_DrawSmallScaledPatch(x, y, 0, PictureOfLevel); + else + { + /*UINT8 *mappingforencore = NULL; + if ((lumpnum = W_CheckNumForName(va("%sE", mapname))) != LUMPERROR) + mappingforencore = W_CachePatchNum(lumpnum, PU_CACHE);*/ + + V_DrawFixedPatch((x+w)<>ANGLETOFINESHIFT); + V_DrawFixedPatch((x+w/2)< horizspac-dupadjust); + + x = (BASEVIDWIDTH + w)/2 + horizspac; + i = cv_nextmap.value - 1; + trans = (rightfade ? V_TRANSLUCENT : 0); + + while (x < BASEVIDWIDTH+dupadjust-horizspac) + { + oldval = i; + do + { + i++; + if (i == NUMMAPS) + i = -1; + + if (i == oldval) + return; + + if(!mapheaderinfo[i]) + continue; // Don't allocate the header. That just makes memory usage skyrocket. + + } while (!M_CanShowLevelInList(i, cv_newgametype.value)); + + // A 160x100 image of the level as entry MAPxxP + if (i+1) + { + lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(i+1))); + if (lumpnum != LUMPERROR) + PictureOfLevel = W_CachePatchNum(lumpnum, PU_CACHE); + else + PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE); + } + else + PictureOfLevel = W_CachePatchName("RANDOMLV", PU_CACHE); + + V_DrawTinyScaledPatch(x, y, trans, PictureOfLevel); + + x += horizspac + w/2; + } +#undef horizspac +} + +static void M_DrawServerMenu(void) +{ + M_DrawLevelSelectOnly(false, false); + M_DrawGenericMenu(); +} + +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); +} + +#ifndef TESTERS +static void M_StartOfflineServerMenu(INT32 choice) +{ + (void)choice; + levellistmode = LLM_CREATESERVER; + M_PrepareLevelSelect(); + M_SetupNextMenu(&MP_OfflineServerDef); +} +#endif + +#ifndef NONET +#ifndef TESTERS +static void M_StartServerMenu(INT32 choice) +{ + (void)choice; + levellistmode = LLM_CREATESERVER; + M_PrepareLevelSelect(); + M_SetupNextMenu(&MP_ServerDef); + +} +#endif + +// ============== +// 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 NOMENUHOST +#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 + +#ifndef TESTERS + V_DrawRightAlignedString(BASEVIDWIDTH-x, y+MP_MainMenu[5].alphaKey, + ((itemOn == 5) ? highlightflags : 0), + "(2-4 players)" + ); +#endif + +#ifndef NONET + y += MP_MainMenu[8].alphaKey; + + V_DrawFill(x+5, y+4+5, /*16*8 + 6,*/ BASEVIDWIDTH - 2*(x+5), 8+6, 159); + + // 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) + { + pskin = R_SkinAvailable(cv_skin[i-1].string); + pcol = cv_playercolor[i-1].value; + + 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 + +// used for skin display on player setup menu +static INT32 multi_tics; +static state_t *multi_state; +static UINT8 multi_spr2; + +// used for follower display on player setup menu +static INT32 follower_tics; +static UINT32 follower_frame; // used for FF_ANIMATE garbo +static state_t *follower_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 consvar_t *setupm_cvfollower; +static INT32 setupm_fakeskin; +static menucolor_t *setupm_fakecolor; +static INT32 setupm_fakefollower; // -1 is for none, our followers start at 0 + +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; + const UINT8 *flashcol = V_GetStringColormap(highlightflags); + INT32 statx, staty; + char *fname; + INT16 i; + + 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 follower string + fname = malloc(SKINNAMESIZE+1); + + if (setupm_fakefollower == -1) + strcpy(fname, "None"); + else + strcpy(fname, followers[setupm_fakefollower].name); + + st = V_StringWidth(fname, 0); + V_DrawString(BASEVIDWIDTH - mx - st, my + 26, + ((MP_PlayerSetupMenu[2].status & IT_TYPE) == IT_SPACE ? V_TRANSLUCENT : 0)|highlightflags|V_ALLOWLOWERCASE, + fname); + if (itemOn == 2) + { + V_DrawCharacter(BASEVIDWIDTH - mx - 10 - st - (skullAnimCounter/5), my + 26, + '\x1C' | highlightflags, false); // left arrow + V_DrawCharacter(BASEVIDWIDTH - mx + 2 + (skullAnimCounter/5), my + 26, + '\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(skincolors[setupm_fakecolor->color].name, 0); + V_DrawString(BASEVIDWIDTH - mx - st, my + 152, highlightflags|V_ALLOWLOWERCASE, skincolors[setupm_fakecolor->color].name); // SRB2kart + if (itemOn == 3) + { + 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)<color) + V_DrawFixedPatch(((BASEVIDWIDTH - mx - 80) + ((speed-1)*8))<color, GTC_MENUCACHE)); + + // 2.2 color bar backported with permission +#define charw 72 +#define indexwidth 8 + { + const INT32 numcolors = (250-charw)/(2*indexwidth); // Number of colors per side + INT32 x = mx; + INT32 w = indexwidth; // Width of a singular color block + menucolor_t *mc = setupm_fakecolor->prev; // Last accessed color + UINT8 h; + + // Draw color in the middle + x += numcolors*w; + for (h = 0; h < 16; h++) + V_DrawFill(x, my+162+h, charw, 1, skincolors[setupm_fakecolor->color].ramp[h]); + + //Draw colors from middle to left + for (i=0; icolor].accessible) + mc = mc->prev; + for (h = 0; h < 16; h++) + V_DrawFill(x, my+162+h, w, 1, skincolors[mc->color].ramp[h]); + mc = mc->prev; + } + + // Draw colors from middle to right + mc = setupm_fakecolor->next; + x += numcolors*w + charw; + for (i=0; icolor].accessible) + mc = mc->next; + for (h = 0; h < 16; h++) + V_DrawFill(x, my+162+h, w, 1, skincolors[mc->color].ramp[h]); + x += w; + mc = mc->next; + } + } +#undef indexwidth + + // character bar, ripped off the color bar :V + if (setupm_fakecolor->color) // inverse should never happen +#define iconwidth 32 + { + const INT32 icons = 4; + INT32 k = -icons; + INT16 col = (setupm_fakeskin - icons) % numskins; + 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 = faceprefix[col][FACE_WANTED]; + offx = 12; + offy = 0; + } + else + { + scale = FRACUNIT/2; + face = faceprefix[col][FACE_RANK]; + offx = 8; + offy = 8; + } + colmap = R_GetTranslationColormap(col, setupm_fakecolor->color, GTC_MENUCACHE); + if (face) + 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 + sprdef = &skins[setupm_fakeskin].sprites[multi_spr2]; + + 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 & 2) // 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, 159); + + // draw player sprite + if (setupm_fakecolor->color) // inverse should never happen + { + UINT8 *colormap = R_GetTranslationColormap(setupm_fakeskin, setupm_fakecolor->color, GTC_MENUCACHE); + + if (skins[setupm_fakeskin].flags & SF_HIRES) + { + V_DrawFixedPatch((mx+43)< -1 && setupm_fakefollower < numfollowers) + { + // animate the follower + + if (--follower_tics <= 0) + { + + // FF_ANIMATE; cycle through FRAMES and get back afterwards. This will be prominent amongst followers hence why it's being supported here. + if (follower_state->frame & FF_ANIMATE) + { + follower_frame++; + follower_tics = follower_state->var2; + if (follower_frame > (follower_state->frame & FF_FRAMEMASK) + follower_state->var1) // that's how it works, right? + follower_frame = follower_state->frame & FF_FRAMEMASK; + } + else + { + st = follower_state->nextstate; + if (st != S_NULL) + follower_state = &states[st]; + follower_tics = follower_state->tics; + if (follower_tics == -1) + follower_tics = 15; // er, what? + // get spritedef: + follower_frame = follower_state->frame & FF_FRAMEMASK; + } + } + sprdef = &sprites[follower_state->sprite]; + + // draw the follower + + if (follower_frame >= sprdef->numframes) + follower_frame = 0; // frame doesn't exist, we went beyond it... what? + sprframe = &sprdef->spriteframes[follower_frame]; + patch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE); + if (sprframe->flip & 2) // Only for first sprite + flags |= V_FLIP; // This sprite is left/right flipped! + + // @TODO: Reminder that followers on the menu right now do NOT support the 'followercolor' command, considering this whole menu is getting remade anyway, I see no point in incorporating it in right now. + + // draw follower sprite + if (setupm_fakecolor->color) // inverse should never happen + { + + // Fake the follower's in game appearance by now also applying some of its variables! coolio, eh? + follower_t fl = followers[setupm_fakefollower]; // shortcut for our sanity + // smooth floating, totally not stolen from rocket sneakers. + const fixed_t pi = (22<>ANGLETOFINESHIFT) & FINEMASK); + + UINT8 *colormap = R_GetTranslationColormap(-1, setupm_fakecolor->color, 0); + V_DrawFixedPatch((mx+65)*FRACUNIT, (my+131-fl.zoffs)*FRACUNIT+sine, fl.scale, flags, patch, colormap); + Z_Free(colormap); + } + } + +#undef charw +} + +// follower state update. This is its own function so that it's at least somewhat clean +static void M_GetFollowerState(void) +{ + + if (setupm_fakefollower <= -1 || setupm_fakefollower > numfollowers-1) // yikes, there's none! + return; + // ^ we don't actually need to set anything since it won't be displayed anyway. + + //followertimer = 0; // reset timer. not like it'll overflow anytime soon but whatever. + + // set follower state + follower_state = &states[followers[setupm_fakefollower].followstate]; + + if (follower_state->frame & FF_ANIMATE) + follower_tics = follower_state->var2; // support for FF_ANIMATE + else + follower_tics = follower_state->tics; + + follower_frame = follower_state->frame & FF_FRAMEMASK; +} + +// Handle 1P/2P MP Setup +static void M_HandleSetupMultiPlayer(INT32 choice) +{ + size_t l; + INT32 prev_setupm_fakeskin; + boolean exitmenu = false; // exit to previous menu and send name change + + if ((choice == gamecontrol[0][gc_fire][0] || choice == gamecontrol[0][gc_fire][1]) && itemOn == 2) + choice = KEY_BACKSPACE; // Hack to allow resetting prefcolor on controllers + + 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_LEFTARROW: + if (itemOn == 1) //player skin + { + S_StartSound(NULL,sfx_menu1); // Tails + prev_setupm_fakeskin = setupm_fakeskin; + do + { + setupm_fakeskin--; + if (setupm_fakeskin < 0) + setupm_fakeskin = numskins-1; + } + while ((prev_setupm_fakeskin != setupm_fakeskin) && !(R_SkinUsable(-1, setupm_fakeskin))); + multi_spr2 = P_GetSkinSprite2(&skins[setupm_fakeskin], SPR2_FSTN, NULL); + } + else if (itemOn == 2) // follower + { + S_StartSound(NULL,sfx_menu1); + setupm_fakefollower--; + M_GetFollowerState(); // update follower state + } + else if (itemOn == 3) // player color + { + S_StartSound(NULL,sfx_menu1); // Tails + setupm_fakecolor = setupm_fakecolor->prev; + } + break; + + case KEY_RIGHTARROW: + if (itemOn == 1) //player skin + { + S_StartSound(NULL,sfx_menu1); // Tails + prev_setupm_fakeskin = setupm_fakeskin; + do + { + setupm_fakeskin++; + if (setupm_fakeskin > numskins-1) + setupm_fakeskin = 0; + } + while ((prev_setupm_fakeskin != setupm_fakeskin) && !(R_SkinUsable(-1, setupm_fakeskin))); + multi_spr2 = P_GetSkinSprite2(&skins[setupm_fakeskin], SPR2_FSTN, NULL); + } + else if (itemOn == 2) // follower + { + S_StartSound(NULL,sfx_menu1); + setupm_fakefollower++; + M_GetFollowerState(); + } + else if (itemOn == 3) // player color + { + S_StartSound(NULL,sfx_menu1); // Tails + setupm_fakecolor = setupm_fakecolor->next; + } + break; + + case KEY_ESCAPE: + exitmenu = true; + break; + + case KEY_BACKSPACE: + if (itemOn == 0) + { + if ((l = strlen(setupm_name))!=0) + { + S_StartSound(NULL,sfx_menu1); // Tails + setupm_name[l-1] =0; + } + } + else if (itemOn == 2) // follower + { + S_StartSound(NULL,sfx_menu1); + setupm_fakefollower = -1; + } + else if (itemOn == 3) + { + UINT16 col = skins[setupm_fakeskin].prefcolor; + if ((setupm_fakecolor->color != col) && skincolors[col].accessible) + { + S_StartSound(NULL,sfx_menu1); // Tails + for (setupm_fakecolor=menucolorhead;;setupm_fakecolor=setupm_fakecolor->next) + if (setupm_fakecolor->color == col || setupm_fakecolor == menucolortail) + break; + } + } + break; + + case KEY_DEL: + if (itemOn == 0 && (l = strlen(setupm_name))!=0) + { + S_StartSound(NULL,sfx_menu1); // Tails + setupm_name[0] = 0; + } + break; + + default: + if (choice < 32 || choice > 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 followers: + if (setupm_fakefollower < -1) + { + setupm_fakefollower = numfollowers-1; + M_GetFollowerState(); // update follower state + } + if (setupm_fakefollower > numfollowers-1) + { + setupm_fakefollower = -1; + M_GetFollowerState(); // update follower state + } + + // check color + if (itemOn == 2 && !skincolors[setupm_fakecolor->color].accessible) { + if (choice == KEY_LEFTARROW) + while (!skincolors[setupm_fakecolor->color].accessible) + setupm_fakecolor = setupm_fakecolor->prev; + else if (choice == KEY_RIGHTARROW || choice == KEY_ENTER) + while (!skincolors[setupm_fakecolor->color].accessible) + setupm_fakecolor = setupm_fakecolor->next; + } + + 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[0].string); + + // set for player 1 + setupm_player = &players[consoleplayer]; + setupm_cvskin = &cv_skin[0]; + setupm_cvcolor = &cv_playercolor[0]; + setupm_cvname = &cv_playername[0]; + setupm_cvfollower = &cv_follower[0]; + + setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value + + // yikes, we don't want none of that... + if (setupm_fakefollower > numfollowers-1) + setupm_fakefollower = -1; + + M_GetFollowerState(); // update follower state + + // 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; + + for (setupm_fakecolor=menucolorhead;;setupm_fakecolor=setupm_fakecolor->next) + if (setupm_fakecolor->color == setupm_cvcolor->value || setupm_fakecolor == menucolortail) + break; + + // 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_playername[1].string); + + // set for splitscreen secondary player + setupm_player = &players[g_localplayers[1]]; + setupm_cvskin = &cv_skin[1]; + setupm_cvcolor = &cv_playercolor[1]; + setupm_cvname = &cv_playername[1]; + setupm_cvfollower = &cv_follower[1]; + + setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value + + // yikes, we don't want none of that... + if (setupm_fakefollower > numfollowers-1) + setupm_fakefollower = -1; + + M_GetFollowerState(); // update follower state + + // 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; + + for (setupm_fakecolor=menucolorhead;;setupm_fakecolor=setupm_fakecolor->next) + if (setupm_fakecolor->color == setupm_cvcolor->value || setupm_fakecolor == menucolortail) + break; + + // disable skin changes if we can't actually change skins + if (splitscreen && !CanChangeSkin(g_localplayers[1])) + 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_playername[2].string); + + // set for splitscreen third player + setupm_player = &players[g_localplayers[2]]; + setupm_cvskin = &cv_skin[2]; + setupm_cvcolor = &cv_playercolor[2]; + setupm_cvname = &cv_playername[2]; + setupm_cvfollower = &cv_follower[2]; + + setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value + + // yikes, we don't want none of that... + if (setupm_fakefollower > numfollowers-1) + setupm_fakefollower = -1; + + M_GetFollowerState(); // update follower state + + // 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; + + for (setupm_fakecolor=menucolorhead;;setupm_fakecolor=setupm_fakecolor->next) + if (setupm_fakecolor->color == setupm_cvcolor->value || setupm_fakecolor == menucolortail) + break; + + // disable skin changes if we can't actually change skins + if (splitscreen > 1 && !CanChangeSkin(g_localplayers[2])) + 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_playername[3].string); + + // set for splitscreen fourth player + setupm_player = &players[g_localplayers[3]]; + setupm_cvskin = &cv_skin[3]; + setupm_cvcolor = &cv_playercolor[3]; + setupm_cvname = &cv_playername[3]; + setupm_cvfollower = &cv_follower[3]; + + setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value + + // yikes, we don't want none of that... + if (setupm_fakefollower > numfollowers-1) + setupm_fakefollower = -1; + + M_GetFollowerState(); // update follower state + + // 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; + + for (setupm_fakecolor=menucolorhead;;setupm_fakecolor=setupm_fakecolor->next) + if (setupm_fakecolor->color == setupm_cvcolor->value || setupm_fakecolor == menucolortail) + break; + + // disable skin changes if we can't actually change skins + if (splitscreen > 2 && !CanChangeSkin(g_localplayers[3])) + 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->color)); + COM_BufAddText (va("%s %d\n",setupm_cvfollower->name,setupm_fakefollower)); + return true; +} + +void M_AddMenuColor(UINT16 color) { + menucolor_t *c; + + // SRB2Kart: I do not understand vanilla doesn't need this but WE do???!?!??! + if (!skincolors[color].accessible) { + return; + } + + if (color >= numskincolors) { + CONS_Printf("M_AddMenuColor: color %d does not exist.",color); + return; + } + + c = (menucolor_t *)malloc(sizeof(menucolor_t)); + c->color = color; + if (menucolorhead == NULL) { + c->next = c; + c->prev = c; + menucolorhead = c; + menucolortail = c; + } else { + c->next = menucolorhead; + c->prev = menucolortail; + menucolortail->next = c; + menucolorhead->prev = c; + menucolortail = c; + } +} + +void M_MoveColorBefore(UINT16 color, UINT16 targ) { + menucolor_t *look, *c = NULL, *t = NULL; + + if (color == targ) + return; + if (color >= numskincolors) { + CONS_Printf("M_MoveColorBefore: color %d does not exist.",color); + return; + } + if (targ >= numskincolors) { + CONS_Printf("M_MoveColorBefore: target color %d does not exist.",targ); + return; + } + + for (look=menucolorhead;;look=look->next) { + if (look->color == color) + c = look; + else if (look->color == targ) + t = look; + if (c != NULL && t != NULL) + break; + if (look==menucolortail) + return; + } + + if (c == t->prev) + return; + + if (t==menucolorhead) + menucolorhead = c; + if (c==menucolortail) + menucolortail = c->prev; + + c->prev->next = c->next; + c->next->prev = c->prev; + + c->prev = t->prev; + c->next = t; + t->prev->next = c; + t->prev = c; +} + +void M_MoveColorAfter(UINT16 color, UINT16 targ) { + menucolor_t *look, *c = NULL, *t = NULL; + + if (color == targ) + return; + if (color >= numskincolors) { + CONS_Printf("M_MoveColorAfter: color %d does not exist.\n",color); + return; + } + if (targ >= numskincolors) { + CONS_Printf("M_MoveColorAfter: target color %d does not exist.\n",targ); + return; + } + + for (look=menucolorhead;;look=look->next) { + if (look->color == color) + c = look; + else if (look->color == targ) + t = look; + if (c != NULL && t != NULL) + break; + if (look==menucolortail) + return; + } + + if (t == c->prev) + return; + + if (t==menucolortail) + menucolortail = c; + else if (c==menucolortail) + menucolortail = c->prev; + + c->prev->next = c->next; + c->next->prev = c->prev; + + c->next = t->next; + c->prev = t; + t->next->prev = c; + t->next = c; +} + +UINT16 M_GetColorBefore(UINT16 color) { + menucolor_t *look; + + if (color >= numskincolors) { + CONS_Printf("M_GetColorBefore: color %d does not exist.\n",color); + return 0; + } + + for (look=menucolorhead;;look=look->next) { + if (look->color == color) + return look->prev->color; + if (look==menucolortail) + return 0; + } +} + +UINT16 M_GetColorAfter(UINT16 color) { + menucolor_t *look; + + if (color >= numskincolors) { + CONS_Printf("M_GetColorAfter: color %d does not exist.\n",color); + return 0; + } + + for (look=menucolorhead;;look=look->next) { + if (look->color == color) + return look->next->color; + if (look==menucolortail) + return 0; + } +} + +void M_InitPlayerSetupColors(void) { + UINT8 i; + numskincolors = SKINCOLOR_FIRSTFREESLOT; + menucolorhead = menucolortail = NULL; + for (i=0; inext; + free(tmp); + } else { + free(look); + return; + } + } + + menucolorhead = menucolortail = NULL; +} + +// ================= +// DATA OPTIONS MENU +// ================= +static UINT8 erasecontext = 0; + +static void M_EraseDataResponse(INT32 ch) +{ + UINT8 i; + + 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; + for (i = 0; i < PWRLV_NUMTYPES; i++) + vspowerlevel[i] = PWRLVRECORD_START; + 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; + INT32 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_usejoystick[setupcontrolplayer-1].string) > I_NumJoys()) + compareval = atoi(cv_usejoystick[setupcontrolplayer-1].string); + else +#endif + compareval = cv_usejoystick[setupcontrolplayer-1].value; + + V_DrawString(OP_JoystickSetDef.x, OP_JoystickSetDef.y+LINEHEIGHT*i-4, (i == compareval) ? V_GREENMAP : 0, joystickInfo[i]); + } +} + +void M_SetupJoystickMenu(INT32 choice) +{ + const char *joyNA = "Unavailable"; + const INT32 n = I_NumJoys(); + + INT32 i = 0; + INT32 j; + + (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. + for (j = 0; j < MAXSPLITSCREENPLAYERS; j++) + { + if (i == cv_usejoystick[j].value) + CV_SetValue(&cv_usejoystick[j], 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) +{ + const UINT8 p = setupcontrolplayer-1; + +#ifdef JOYSTICK_HOTPLUG + INT32 oldchoice, oldstringchoice; + INT32 numjoys = I_NumJoys(); + + oldchoice = oldstringchoice = atoi(cv_usejoystick[p].string) > numjoys ? atoi(cv_usejoystick[p].string) : cv_usejoystick[p].value; + CV_SetValue(&cv_usejoystick[p], 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[p], cv_usejoystick[p].value); + + // reset this so the comparison is valid + if (oldchoice > numjoys) + oldchoice = cv_usejoystick[p].value; + + if (oldchoice != choice) + { + if (choice && oldstringchoice > numjoys) // if we did not select "None", we likely selected a used device + CV_SetValue(&cv_usejoystick[p], (oldstringchoice > numjoys ? oldstringchoice : oldchoice)); + + if (oldstringchoice == + (atoi(cv_usejoystick[p].string) > numjoys ? atoi(cv_usejoystick[p].string) : cv_usejoystick[p].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 + CV_SetValue(&cv_usejoystick[p], choice); +#endif +} + +// ============= +// CONTROLS MENU +// ============= + +static void M_Setup1PControlsMenu(INT32 choice) +{ + (void)choice; + setupcontrolplayer = 1; + setupcontrols = gamecontrol[0]; // 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[16].status = IT_CONTROL; // Chat + //OP_AllControlsMenu[17].status = IT_CONTROL; // Team-chat + OP_AllControlsMenu[17].status = IT_CONTROL; // Rankings + //OP_AllControlsMenu[18].status = IT_CONTROL; // Viewpoint + // 19 is Reset Camera, 20 is Toggle Chasecam + OP_AllControlsMenu[21].status = IT_CONTROL; // Pause + OP_AllControlsMenu[22].status = IT_CONTROL; // Screenshot + OP_AllControlsMenu[23].status = IT_CONTROL; // GIF + OP_AllControlsMenu[24].status = IT_CONTROL; // System Menu + OP_AllControlsMenu[25].status = IT_CONTROL; // Console + /*OP_AllControlsMenu[26].status = IT_HEADER; // Spectator Controls header + OP_AllControlsMenu[27].status = IT_SPACE; // Spectator Controls space + OP_AllControlsMenu[28].status = IT_CONTROL; // Spectate + OP_AllControlsMenu[29].status = IT_CONTROL; // Look Up + OP_AllControlsMenu[30].status = IT_CONTROL; // Look Down + OP_AllControlsMenu[31].status = IT_CONTROL; // Center View + */ + + M_SetupNextMenu(&OP_AllControlsDef); +} + +static void M_Setup2PControlsMenu(INT32 choice) +{ + (void)choice; + setupcontrolplayer = 2; + setupcontrols = gamecontrol[1]; + currentMenu->lastOn = itemOn; + + // Set proper gamepad options + OP_AllControlsMenu[0].itemaction = &OP_Joystick2Def; + + // Hide P1-only controls + OP_AllControlsMenu[16].status = IT_GRAYEDOUT2; // Chat + //OP_AllControlsMenu[17].status = IT_GRAYEDOUT2; // Team-chat + OP_AllControlsMenu[17].status = IT_GRAYEDOUT2; // Rankings + //OP_AllControlsMenu[18].status = IT_GRAYEDOUT2; // Viewpoint + // 19 is Reset Camera, 20 is Toggle Chasecam + OP_AllControlsMenu[21].status = IT_GRAYEDOUT2; // Pause + OP_AllControlsMenu[22].status = IT_GRAYEDOUT2; // Screenshot + OP_AllControlsMenu[23].status = IT_GRAYEDOUT2; // GIF + OP_AllControlsMenu[24].status = IT_GRAYEDOUT2; // System Menu + OP_AllControlsMenu[25].status = IT_GRAYEDOUT2; // Console + /*OP_AllControlsMenu[26].status = IT_GRAYEDOUT2; // Spectator Controls header + OP_AllControlsMenu[27].status = IT_GRAYEDOUT2; // Spectator Controls space + OP_AllControlsMenu[28].status = IT_GRAYEDOUT2; // Spectate + OP_AllControlsMenu[29].status = IT_GRAYEDOUT2; // Look Up + OP_AllControlsMenu[30].status = IT_GRAYEDOUT2; // Look Down + OP_AllControlsMenu[31].status = IT_GRAYEDOUT2; // Center View + */ + + M_SetupNextMenu(&OP_AllControlsDef); +} + +static void M_Setup3PControlsMenu(INT32 choice) +{ + (void)choice; + setupcontrolplayer = 3; + setupcontrols = gamecontrol[2]; + currentMenu->lastOn = itemOn; + + // Set proper gamepad options + OP_AllControlsMenu[0].itemaction = &OP_Joystick3Def; + + // Hide P1-only controls + OP_AllControlsMenu[16].status = IT_GRAYEDOUT2; // Chat + //OP_AllControlsMenu[17].status = IT_GRAYEDOUT2; // Team-chat + OP_AllControlsMenu[17].status = IT_GRAYEDOUT2; // Rankings + //OP_AllControlsMenu[18].status = IT_GRAYEDOUT2; // Viewpoint + // 19 is Reset Camera, 20 is Toggle Chasecam + OP_AllControlsMenu[21].status = IT_GRAYEDOUT2; // Pause + OP_AllControlsMenu[22].status = IT_GRAYEDOUT2; // Screenshot + OP_AllControlsMenu[23].status = IT_GRAYEDOUT2; // GIF + OP_AllControlsMenu[24].status = IT_GRAYEDOUT2; // System Menu + OP_AllControlsMenu[25].status = IT_GRAYEDOUT2; // Console + /*OP_AllControlsMenu[26].status = IT_GRAYEDOUT2; // Spectator Controls header + OP_AllControlsMenu[27].status = IT_GRAYEDOUT2; // Spectator Controls space + OP_AllControlsMenu[28].status = IT_GRAYEDOUT2; // Spectate + OP_AllControlsMenu[29].status = IT_GRAYEDOUT2; // Look Up + OP_AllControlsMenu[30].status = IT_GRAYEDOUT2; // Look Down + OP_AllControlsMenu[31].status = IT_GRAYEDOUT2; // Center View + */ + + M_SetupNextMenu(&OP_AllControlsDef); +} + +static void M_Setup4PControlsMenu(INT32 choice) +{ + (void)choice; + setupcontrolplayer = 4; + setupcontrols = gamecontrol[3]; + currentMenu->lastOn = itemOn; + + // Set proper gamepad options + OP_AllControlsMenu[0].itemaction = &OP_Joystick4Def; + + // Hide P1-only controls + OP_AllControlsMenu[16].status = IT_GRAYEDOUT2; // Chat + //OP_AllControlsMenu[17].status = IT_GRAYEDOUT2; // Team-chat + OP_AllControlsMenu[17].status = IT_GRAYEDOUT2; // Rankings + //OP_AllControlsMenu[18].status = IT_GRAYEDOUT2; // Viewpoint + // 19 is Reset Camera, 20 is Toggle Chasecam + OP_AllControlsMenu[21].status = IT_GRAYEDOUT2; // Pause + OP_AllControlsMenu[22].status = IT_GRAYEDOUT2; // Screenshot + OP_AllControlsMenu[23].status = IT_GRAYEDOUT2; // GIF + OP_AllControlsMenu[24].status = IT_GRAYEDOUT2; // System Menu + OP_AllControlsMenu[25].status = IT_GRAYEDOUT2; // Console + /*OP_AllControlsMenu[26].status = IT_GRAYEDOUT2; // Spectator Controls header + OP_AllControlsMenu[27].status = IT_GRAYEDOUT2; // Spectator Controls space + OP_AllControlsMenu[28].status = IT_GRAYEDOUT2; // Spectate + OP_AllControlsMenu[29].status = IT_GRAYEDOUT2; // Look Up + OP_AllControlsMenu[30].status = IT_GRAYEDOUT2; // Look Down + OP_AllControlsMenu[31].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_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) +{ + const UINT8 p = setupcontrolplayer-1; + INT32 i; + + if (ch != 'y' && ch != KEY_ENTER) + return; + + // clear all controls + for (i = 0; i < num_gamecontrols; i++) + { + G_ClearControlKeys(gamecontrol[p], i); + } + + // Setup original defaults + G_CopyControls(gamecontrol[p], gamecontroldefault[p][gcs_kart], NULL, 0); + + // Setup gamepad option defaults (yucky) + CV_StealthSet(&cv_usejoystick[p], cv_usejoystick[p].defaultvalue); + CV_StealthSet(&cv_turnaxis[p], cv_turnaxis[p].defaultvalue); + CV_StealthSet(&cv_moveaxis[p], cv_moveaxis[p].defaultvalue); + CV_StealthSet(&cv_brakeaxis[p], cv_brakeaxis[p].defaultvalue); + CV_StealthSet(&cv_aimaxis[p], cv_aimaxis[p].defaultvalue); + CV_StealthSet(&cv_lookaxis[p], cv_lookaxis[p].defaultvalue); + CV_StealthSet(&cv_fireaxis[p], cv_fireaxis[p].defaultvalue); + CV_StealthSet(&cv_driftaxis[p], cv_driftaxis[p].defaultvalue); + + 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 + 115; + 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 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 = KartItemCVars[currentMenu->menuitems[thisitem].alphaKey-1]; + translucent = (cv->value ? 0 : V_TRANSLUCENT); + + switch (currentMenu->menuitems[thisitem].alphaKey) + { + case KRITEM_DUALSNEAKER: + 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 = KartItemCVars[currentMenu->menuitems[itemOn].alphaKey-1]; + translucent = (cv->value ? 0 : V_TRANSLUCENT); + + switch (currentMenu->menuitems[itemOn].alphaKey) + { + case KRITEM_DUALSNEAKER: + 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 (KartItemCVars[i]->value == v) + CV_AddValue(KartItemCVars[i], 1); + } + } + else + { + S_StartSound(NULL, sfx_s1ba); + CV_AddValue(KartItemCVars[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 HAVE_DISCORDRPC +static const tic_t confirmLength = 3*TICRATE/4; +static tic_t confirmDelay = 0; +static boolean confirmAccept = false; + +static void M_HandleDiscordRequests(INT32 choice) +{ + if (confirmDelay > 0) + return; + + switch (choice) + { + case KEY_ENTER: + Discord_Respond(discordRequestList->userID, DISCORD_REPLY_YES); + confirmAccept = true; + confirmDelay = confirmLength; + S_StartSound(NULL, sfx_s3k63); + break; + + case KEY_ESCAPE: + Discord_Respond(discordRequestList->userID, DISCORD_REPLY_NO); + confirmAccept = false; + confirmDelay = confirmLength; + S_StartSound(NULL, sfx_s3kb2); + break; + } +} + +static const char *M_GetDiscordName(discordRequest_t *r) +{ + if (r == NULL) + return ""; + + if (cv_discordstreamer.value) + return r->username; + + return va("%s#%s", r->username, r->discriminator); +} + +// (this goes in k_hud.c when merged into v2) +static void M_DrawSticker(INT32 x, INT32 y, INT32 width, INT32 flags, boolean isSmall) +{ + patch_t *stickerEnd; + INT32 height; + + if (isSmall == true) + { + stickerEnd = W_CachePatchName("K_STIKE2", PU_CACHE); + height = 6; + } + else + { + stickerEnd = W_CachePatchName("K_STIKEN", PU_CACHE); + height = 11; + } + + V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, flags, stickerEnd, NULL); + V_DrawFill(x, y, width, height, 24|flags); + V_DrawFixedPatch((x + width)*FRACUNIT, y*FRACUNIT, FRACUNIT, flags|V_FLIP, stickerEnd, NULL); +} + +static void M_DrawDiscordRequests(void) +{ + discordRequest_t *curRequest = discordRequestList; + UINT8 *colormap; + patch_t *hand = NULL; + boolean removeRequest = false; + + const char *wantText = "...would like to join!"; + const char *controlText = "\x82" "ENTER" "\x80" " - Accept " "\x82" "ESC" "\x80" " - Decline"; + + INT32 x = 100; + INT32 y = 133; + + INT32 slide = 0; + INT32 maxYSlide = 18; + + if (confirmDelay > 0) + { + if (confirmAccept == true) + { + colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_GREEN, GTC_MENUCACHE); + hand = W_CachePatchName("K_LAPH02", PU_CACHE); + } + else + { + colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_RED, GTC_MENUCACHE); + hand = W_CachePatchName("K_LAPH03", PU_CACHE); + } + + slide = confirmLength - confirmDelay; + + confirmDelay--; + + if (confirmDelay == 0) + removeRequest = true; + } + else + { + colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_GREY, GTC_MENUCACHE); + } + + V_DrawFixedPatch(56*FRACUNIT, 150*FRACUNIT, FRACUNIT, 0, W_CachePatchName("K_LAPE01", PU_CACHE), colormap); + + if (hand != NULL) + { + fixed_t handoffset = (4 - abs((signed)(skullAnimCounter - 4))) * FRACUNIT; + V_DrawFixedPatch(56*FRACUNIT, 150*FRACUNIT + handoffset, FRACUNIT, 0, hand, NULL); + } + + M_DrawSticker(x + (slide * 32), y - 1, V_ThinStringWidth(M_GetDiscordName(curRequest), V_ALLOWLOWERCASE|V_6WIDTHSPACE), 0, false); + V_DrawThinString(x + (slide * 32), y, V_ALLOWLOWERCASE|V_6WIDTHSPACE|V_YELLOWMAP, M_GetDiscordName(curRequest)); + + M_DrawSticker(x, y + 12, V_ThinStringWidth(wantText, V_ALLOWLOWERCASE|V_6WIDTHSPACE), 0, true); + V_DrawThinString(x, y + 10, V_ALLOWLOWERCASE|V_6WIDTHSPACE, wantText); + + M_DrawSticker(x, y + 26, V_ThinStringWidth(controlText, V_ALLOWLOWERCASE|V_6WIDTHSPACE), 0, true); + V_DrawThinString(x, y + 24, V_ALLOWLOWERCASE|V_6WIDTHSPACE, controlText); + + y -= 18; + + while (curRequest->next != NULL) + { + INT32 ySlide = min(slide * 4, maxYSlide); + + curRequest = curRequest->next; + + M_DrawSticker(x, y - 1 + ySlide, V_ThinStringWidth(M_GetDiscordName(curRequest), V_ALLOWLOWERCASE|V_6WIDTHSPACE), 0, false); + V_DrawThinString(x, y + ySlide, V_ALLOWLOWERCASE|V_6WIDTHSPACE, M_GetDiscordName(curRequest)); + + y -= 12; + maxYSlide = 12; + } + + if (removeRequest == true) + { + DRPC_RemoveRequest(discordRequestList); + + if (discordRequestList == NULL) + { + // No other requests + MPauseMenu[mpause_discordrequests].status = IT_GRAYEDOUT; + + if (currentMenu->prevMenu) + { + M_SetupNextMenu(currentMenu->prevMenu); + if (currentMenu == &MPauseDef) + itemOn = mpause_continue; + } + else + M_ClearMenus(true); + + return; + } + } +} +#endif From 5ed55aca3dc919590d474503ef1de58bca105a22 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 2 Jan 2022 21:50:23 -0800 Subject: [PATCH 078/379] Disable wad loading in TESTERS build --- src/d_main.c | 2 ++ src/d_netcmd.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/d_main.c b/src/d_main.c index b578373f8..4776f4e77 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1256,6 +1256,7 @@ void D_SRB2Main(void) // Do this up here so that WADs loaded through the command line can use ExecCfg COM_Init(); +#ifndef TESTERS // add any files specified on the command line with -file wadfile // to the wad list if (!((M_GetUrlProtocolArg() || M_CheckParm("-connect")) && !M_CheckParm("-server"))) @@ -1273,6 +1274,7 @@ void D_SRB2Main(void) } } } +#endif // get map from parms diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 775cf8598..faecdae4d 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3850,6 +3850,7 @@ static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum) */ static void Command_Addfile(void) { +#ifndef TESTERS size_t argc = COM_Argc(); // amount of arguments total size_t curarg; // current argument index @@ -3979,6 +3980,7 @@ static void Command_Addfile(void) else SendNetXCmd(XD_ADDFILE, buf, buf_p - buf); } +#endif/*TESTERS*/ } static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum) From bf2dc739da740d871c0d056fb02ff01aad572a64 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Dec 2021 11:30:19 -0500 Subject: [PATCH 079/379] Add functions to handle interpolation Much less code duplication --- src/hardware/hw_main.c | 60 +++++--------- src/hardware/hw_md2.c | 29 ++++--- src/k_hud.c | 180 +++++++++++------------------------------ src/r_fps.c | 21 ++++- src/r_fps.h | 3 + src/r_things.c | 52 +++++------- 6 files changed, 123 insertions(+), 222 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 7f6eed52a..63e46e9a2 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -40,9 +40,12 @@ #include "../r_things.h" // R_GetShadowZ #include "../d_main.h" #include "../p_slopes.h" -#include "../k_kart.h" // HITLAGJITTERS #include "hw_md2.h" +// SRB2Kart +#include "../k_kart.h" // HITLAGJITTERS +#include "../r_fps.h" + #ifdef NEWCLIP #include "hw_clip.h" #endif @@ -3641,17 +3644,9 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) fixed_t slopez; pslope_t *groundslope; - fixed_t interpx = thing->x; - fixed_t interpy = thing->y; - fixed_t interpz = thing->z; - - // do interpolation - if (cv_frameinterpolation.value == 1) - { - interpx = thing->old_x + FixedMul(rendertimefrac, thing->x - thing->old_x); - interpy = thing->old_y + FixedMul(rendertimefrac, thing->y - thing->old_y); - interpz = thing->old_z + FixedMul(rendertimefrac, thing->z - thing->old_z); - } + fixed_t interpx = R_InterpolateFixed(thing->old_x, thing->x); + fixed_t interpy = R_InterpolateFixed(thing->old_y, thing->y); + fixed_t interpz = R_InterpolateFixed(thing->old_z, thing->z); // hitlag vibrating (todo: interp somehow?) if (thing->hitlag > 0 && (thing->eflags & MFE_DAMAGEHITLAG)) @@ -5084,25 +5079,18 @@ static void HWR_ProjectSprite(mobj_t *thing) dispoffset = thing->info->dispoffset; - interpx = thing->x; - interpy = thing->y; - interpz = thing->z; - interpangle = (thing->player ? thing->player->drawangle : thing->angle); + interpx = R_InterpolateFixed(thing->old_x, thing->x); + interpy = R_InterpolateFixed(thing->old_y, thing->y); + interpz = R_InterpolateFixed(thing->old_z, thing->z); + interpangle = ANGLE_MAX; - if (cv_frameinterpolation.value == 1) + if (thing->player) { - interpx = thing->old_x + FixedMul(rendertimefrac, thing->x - thing->old_x); - interpy = thing->old_y + FixedMul(rendertimefrac, thing->y - thing->old_y); - interpz = thing->old_z + FixedMul(rendertimefrac, thing->z - thing->old_z); - - if (thing->player) - { - interpangle = thing->player->old_drawangle + FixedMul(rendertimefrac, thing->player->drawangle - thing->player->old_drawangle); - } - else - { - interpangle = thing->old_angle + FixedMul(rendertimefrac, thing->angle - thing->old_angle); - } + interpangle = R_InterpolateAngle(thing->player->old_drawangle, thing->player->drawangle); + } + else + { + interpangle = R_InterpolateAngle(thing->old_angle, thing->angle); } // hitlag vibrating (todo: interp somehow?) @@ -5526,17 +5514,9 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) if (!thing) return; - interpx = thing->x; - interpy = thing->y; - interpz = thing->z; - - // do interpolation - if (cv_frameinterpolation.value == 1) - { - interpx = thing->old_x + FixedMul(rendertimefrac, thing->x - thing->old_x); - interpy = thing->old_y + FixedMul(rendertimefrac, thing->y - thing->old_y); - interpz = thing->old_z + FixedMul(rendertimefrac, thing->z - thing->old_z); - } + interpx = R_InterpolateFixed(thing->old_x, thing->x); + interpy = R_InterpolateFixed(thing->old_y, thing->y); + interpz = R_InterpolateFixed(thing->old_z, thing->z); // transform the origin point tr_x = FIXED_TO_FLOAT(interpx) - gl_viewx; diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 95077a54a..cf9d0b5c8 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -46,6 +46,7 @@ // SRB2Kart #include "../k_color.h" #include "../k_kart.h" // HITLAGJITTERS +#include "../r_fps.h" #ifdef HAVE_PNG @@ -1368,17 +1369,9 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) INT32 mod; float finalscale; - fixed_t interpx = spr->mobj->x; - fixed_t interpy = spr->mobj->y; - fixed_t interpz = spr->mobj->z; - - // do interpolation - if (cv_frameinterpolation.value == 1) - { - interpx = spr->mobj->old_x + FixedMul(rendertimefrac, spr->mobj->x - spr->mobj->old_x); - interpy = spr->mobj->old_y + FixedMul(rendertimefrac, spr->mobj->y - spr->mobj->old_y); - interpz = spr->mobj->old_z + FixedMul(rendertimefrac, spr->mobj->z - spr->mobj->old_z); - } + fixed_t interpx = R_InterpolateFixed(spr->mobj->old_x, spr->mobj->x); + fixed_t interpy = R_InterpolateFixed(spr->mobj->old_y, spr->mobj->y); + fixed_t interpz = R_InterpolateFixed(spr->mobj->old_z, spr->mobj->z); // hitlag vibrating if (spr->mobj->hitlag > 0 && (spr->mobj->eflags & MFE_DAMAGEHITLAG)) @@ -1636,10 +1629,16 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) if (sprframe->rotate || papersprite) { - fixed_t anglef = AngleFixed(spr->mobj->angle); + fixed_t anglef = INT32_MAX; if (spr->mobj->player) - anglef = AngleFixed(spr->mobj->player->drawangle); + { + anglef = AngleFixed(R_InterpolateAngle(spr->mobj->player->old_drawangle, spr->mobj->player->drawangle)); + } + else + { + anglef = AngleFixed(R_InterpolateAngle(spr->mobj->old_angle, spr->mobj->angle)); + } p.angley = FIXED_TO_FLOAT(anglef); } @@ -1671,8 +1670,8 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) } } - p.anglez = FIXED_TO_FLOAT(AngleFixed(spr->mobj->pitch)); - p.anglex = FIXED_TO_FLOAT(AngleFixed(spr->mobj->roll)); + p.anglez = FIXED_TO_FLOAT(AngleFixed(R_InterpolateAngle(spr->mobj->old_pitch, spr->mobj->pitch))); + p.anglex = FIXED_TO_FLOAT(AngleFixed(R_InterpolateAngle(spr->mobj->old_roll, spr->mobj->roll))); // SRB2CBTODO: MD2 scaling support finalscale *= FIXED_TO_FLOAT(spr->mobj->scale); diff --git a/src/k_hud.c b/src/k_hud.c index 7f54abd49..2490139c2 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -31,6 +31,7 @@ #include "r_main.h" #include "s_sound.h" #include "r_things.h" +#include "r_fps.h" #define NUMPOSNUMS 10 #define NUMPOSFRAMES 7 // White, three blues, three reds @@ -835,48 +836,26 @@ void K_ObjectTracking(trackingResult_t *result, vector3_t *point, UINT8 cameraNu return; } - // TODO: needs da interp + // TODO: parts need interp if (cam->chase == true && !player->spectator) { // Use the camera's properties. - viewpointX = cam->x; - viewpointY = cam->y; - viewpointZ = cam->z - point->z; - viewpointAngle = (INT32)cam->angle; - viewpointAiming = (INT32)cam->aiming; - viewpointRoll = (INT32)player->viewrollangle; - - if (cv_frameinterpolation.value == 1) - { - viewpointX = cam->old_x + FixedMul(rendertimefrac, cam->x - cam->old_x); - viewpointY = cam->old_y + FixedMul(rendertimefrac, cam->y - cam->old_y); - viewpointZ = (cam->old_z + FixedMul(rendertimefrac, cam->z - cam->old_z)) - point->z; - - viewpointAngle = (INT32)(cam->old_angle + FixedMul(rendertimefrac, cam->angle - cam->old_angle)); - viewpointAiming = (INT32)(cam->old_aiming + FixedMul(rendertimefrac, cam->aiming - cam->old_aiming)); - viewpointRoll = (INT32)(player->old_viewrollangle + FixedMul(rendertimefrac, player->viewrollangle - player->old_viewrollangle)); - } + viewpointX = R_InterpolateFixed(cam->old_x, cam->x); + viewpointY = R_InterpolateFixed(cam->old_y, cam->y); + viewpointZ = R_InterpolateFixed(cam->old_z, cam->z) - point->z; + viewpointAngle = (INT32)R_InterpolateAngle(cam->old_angle, cam->angle); + viewpointAiming = (INT32)R_InterpolateAngle(cam->old_aiming, cam->aiming); + viewpointRoll = (INT32)R_InterpolateAngle(player->old_viewrollangle, player->viewrollangle); } else { // Use player properties. - viewpointX = player->mo->x; - viewpointY = player->mo->y; - viewpointZ = player->viewz - point->z; - viewpointAngle = (INT32)player->mo->angle; + viewpointX = R_InterpolateFixed(player->mo->old_x, player->mo->x); + viewpointY = R_InterpolateFixed(player->mo->old_y, player->mo->y); + viewpointZ = R_InterpolateFixed(player->mo->old_z, player->mo->z) - point->z; //player->old_viewz + viewpointAngle = (INT32)R_InterpolateAngle(player->mo->old_angle, player->mo->angle); viewpointAiming = (INT32)player->aiming; - viewpointRoll = (INT32)player->viewrollangle; - - if (cv_frameinterpolation.value == 1) - { - viewpointX = player->mo->old_x + FixedMul(rendertimefrac, player->mo->x - player->mo->old_x); - viewpointY = player->mo->old_y + FixedMul(rendertimefrac, player->mo->y - player->mo->old_y); - viewpointZ = (player->mo->old_z + FixedMul(rendertimefrac, player->viewz - player->mo->old_z)) - point->z; //player->old_viewz - - viewpointAngle = (INT32)(player->mo->old_angle + FixedMul(rendertimefrac, player->mo->angle - player->mo->old_angle)); - //viewpointAiming = (INT32)(player->mo->old_aiming + FixedMul(rendertimefrac, player->mo->aiming - player->mo->old_aiming)); - viewpointRoll = (INT32)(player->old_viewrollangle + FixedMul(rendertimefrac, player->viewrollangle - player->old_viewrollangle)); - } + viewpointRoll = (INT32)R_InterpolateAngle(player->old_viewrollangle, player->viewrollangle); } viewpointAngle += (INT32)angleOffset; @@ -2631,24 +2610,13 @@ static void K_drawKartPlayerCheck(void) continue; } - v.x = checkplayer->mo->x; - v.y = checkplayer->mo->y; - v.z = checkplayer->mo->z; + v.x = R_InterpolateFixed(checkplayer->mo->old_x, checkplayer->mo->x); + v.y = R_InterpolateFixed(checkplayer->mo->old_y, checkplayer->mo->y); + v.z = R_InterpolateFixed(checkplayer->mo->old_z, checkplayer->mo->z); - pPos.x = stplyr->mo->x; - pPos.y = stplyr->mo->y; - pPos.z = stplyr->mo->z; - - if (cv_frameinterpolation.value == 1) - { - v.x = checkplayer->mo->old_x + FixedMul(rendertimefrac, checkplayer->mo->x - checkplayer->mo->old_x); - v.y = checkplayer->mo->old_y + FixedMul(rendertimefrac, checkplayer->mo->y - checkplayer->mo->old_y); - v.z = checkplayer->mo->old_z + FixedMul(rendertimefrac, checkplayer->mo->z - checkplayer->mo->old_z); - - pPos.x = stplyr->mo->old_x + FixedMul(rendertimefrac, stplyr->mo->x - stplyr->mo->old_x); - pPos.y = stplyr->mo->old_y + FixedMul(rendertimefrac, stplyr->mo->y - stplyr->mo->old_y); - pPos.z = stplyr->mo->old_z + FixedMul(rendertimefrac, stplyr->mo->z - stplyr->mo->old_z); - } + pPos.x = R_InterpolateFixed(stplyr->mo->old_x, stplyr->mo->x); + pPos.y = R_InterpolateFixed(stplyr->mo->old_y, stplyr->mo->y); + pPos.z = R_InterpolateFixed(stplyr->mo->old_z, stplyr->mo->z); distance = R_PointToDist2(pPos.x, pPos.y, v.x, v.y); @@ -2834,29 +2802,15 @@ static void K_drawKartNameTags(void) if (thiscam->chase == true) { - c.x = thiscam->x; - c.y = thiscam->y; - c.z = thiscam->z; - - if (cv_frameinterpolation.value == 1) - { - c.x = thiscam->old_x + FixedMul(rendertimefrac, thiscam->x - thiscam->old_x); - c.y = thiscam->old_y + FixedMul(rendertimefrac, thiscam->y - thiscam->old_y); - c.z = thiscam->old_z + FixedMul(rendertimefrac, thiscam->z - thiscam->old_z); - } + c.x = R_InterpolateFixed(thiscam->old_x, thiscam->x); + c.y = R_InterpolateFixed(thiscam->old_y, thiscam->y); + c.z = R_InterpolateFixed(thiscam->old_z, thiscam->z); } else { - c.x = stplyr->mo->x; - c.y = stplyr->mo->y; - c.z = stplyr->mo->z; - - if (cv_frameinterpolation.value == 1) - { - c.x = stplyr->mo->old_x + FixedMul(rendertimefrac, stplyr->mo->x - stplyr->mo->old_x); - c.y = stplyr->mo->old_y + FixedMul(rendertimefrac, stplyr->mo->y - stplyr->mo->old_y); - c.z = stplyr->mo->old_z + FixedMul(rendertimefrac, stplyr->mo->z - stplyr->mo->old_z); - } + c.x = R_InterpolateFixed(stplyr->mo->old_x, stplyr->mo->x); + c.y = R_InterpolateFixed(stplyr->mo->old_y, stplyr->mo->y); + c.z = R_InterpolateFixed(stplyr->mo->old_z, stplyr->mo->z); } for (i = 0; i < MAXPLAYERS; i++) @@ -2895,16 +2849,9 @@ static void K_drawKartNameTags(void) continue; } - v.x = ntplayer->mo->x; - v.y = ntplayer->mo->y; - v.z = ntplayer->mo->z; - - if (cv_frameinterpolation.value == 1) - { - v.x = ntplayer->mo->old_x + FixedMul(rendertimefrac, ntplayer->mo->x - ntplayer->mo->old_x); - v.y = ntplayer->mo->old_y + FixedMul(rendertimefrac, ntplayer->mo->y - ntplayer->mo->old_y); - v.z = ntplayer->mo->old_z + FixedMul(rendertimefrac, ntplayer->mo->z - ntplayer->mo->old_z); - } + v.x = R_InterpolateFixed(ntplayer->mo->old_x, ntplayer->mo->x); + v.y = R_InterpolateFixed(ntplayer->mo->old_x, ntplayer->mo->x); + v.z = R_InterpolateFixed(ntplayer->mo->old_x, ntplayer->mo->x); if (!(ntplayer->mo->eflags & MFE_VERTICALFLIP)) { @@ -2959,16 +2906,9 @@ static void K_drawKartNameTags(void) SINT8 localindicator = -1; vector3_t v; - v.x = ntplayer->mo->x; - v.y = ntplayer->mo->y; - v.z = ntplayer->mo->z; - - if (cv_frameinterpolation.value == 1) - { - v.x = ntplayer->mo->old_x + FixedMul(rendertimefrac, ntplayer->mo->x - ntplayer->mo->old_x); - v.y = ntplayer->mo->old_y + FixedMul(rendertimefrac, ntplayer->mo->y - ntplayer->mo->old_y); - v.z = ntplayer->mo->old_z + FixedMul(rendertimefrac, ntplayer->mo->z - ntplayer->mo->old_z); - } + v.x = R_InterpolateFixed(ntplayer->mo->old_x, ntplayer->mo->x); + v.y = R_InterpolateFixed(ntplayer->mo->old_y, ntplayer->mo->y); + v.z = R_InterpolateFixed(ntplayer->mo->old_z, ntplayer->mo->z); v.z += (ntplayer->mo->height / 2); @@ -3208,14 +3148,8 @@ static void K_drawKartMinimap(void) else colormap = NULL; - interpx = g->mo->x; - interpy = g->mo->y; - - if (cv_frameinterpolation.value == 1) - { - interpx = g->mo->old_x + FixedMul(rendertimefrac, g->mo->x - g->mo->old_x); - interpy = g->mo->old_y + FixedMul(rendertimefrac, g->mo->y - g->mo->old_y); - } + interpx = R_InterpolateFixed(g->mo->old_x, g->mo->x); + interpy = R_InterpolateFixed(g->mo->old_y, g->mo->y); K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, faceprefix[skin][FACE_MINIMAP], colormap, AutomapPic); g = g->next; @@ -3273,14 +3207,8 @@ static void K_drawKartMinimap(void) else colormap = NULL; - interpx = players[i].mo->x; - interpy = players[i].mo->y; - - if (cv_frameinterpolation.value == 1) - { - interpx = players[i].mo->old_x + FixedMul(rendertimefrac, players[i].mo->x - players[i].mo->old_x); - interpy = players[i].mo->old_y + FixedMul(rendertimefrac, players[i].mo->y - players[i].mo->old_y); - } + interpx = R_InterpolateFixed(players[i].mo->old_x, players[i].mo->x); + interpy = R_InterpolateFixed(players[i].mo->old_y, players[i].mo->y); K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, faceprefix[skin][FACE_MINIMAP], colormap, AutomapPic); // Target reticule @@ -3308,14 +3236,8 @@ static void K_drawKartMinimap(void) colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->color, GTC_CACHE); } - interpx = mobj->x; - interpy = mobj->y; - - if (cv_frameinterpolation.value == 1) - { - interpx = mobj->old_x + FixedMul(rendertimefrac, mobj->x - mobj->old_x); - interpy = mobj->old_y + FixedMul(rendertimefrac, mobj->y - mobj->old_y); - } + interpx = R_InterpolateFixed(mobj->old_x, mobj->x); + interpy = R_InterpolateFixed(mobj->old_y, mobj->y); K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_spbminimap, colormap, AutomapPic); } @@ -3345,14 +3267,8 @@ static void K_drawKartMinimap(void) else colormap = NULL; - interpx = players[localplayers[i]].mo->x; - interpy = players[localplayers[i]].mo->y; - - if (cv_frameinterpolation.value == 1) - { - interpx = players[localplayers[i]].mo->old_x + FixedMul(rendertimefrac, players[localplayers[i]].mo->x - players[localplayers[i]].mo->old_x); - interpy = players[localplayers[i]].mo->old_y + FixedMul(rendertimefrac, players[localplayers[i]].mo->y - players[localplayers[i]].mo->old_y); - } + interpx = R_InterpolateFixed(players[localplayers[i]].mo->old_x, players[localplayers[i]].mo->x); + interpy = R_InterpolateFixed(players[localplayers[i]].mo->old_y, players[localplayers[i]].mo->y); K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, faceprefix[skin][FACE_MINIMAP], colormap, AutomapPic); @@ -3631,7 +3547,7 @@ static void K_drawKartFinish(void) x = ((TICRATE - stplyr->karthud[khud_cardanimation])*(xval > x ? xval : x))/TICRATE; ox = ((TICRATE - (stplyr->karthud[khud_cardanimation] - 1))*(xval > x ? xval : x))/TICRATE; - interpx = ox + FixedMul(rendertimefrac, x - ox); + interpx = R_InterpolateFixed(ox, x); if (r_splitscreen && stplyr == &players[displayplayers[1]]) interpx = -interpx; @@ -4039,11 +3955,11 @@ static void K_drawLapStartAnim(void) newval = (BASEVIDWIDTH/2 + (32 * max(0, t - 76))) * FRACUNIT; oldval = (BASEVIDWIDTH/2 + (32 * max(0, tOld - 76))) * FRACUNIT; - interpx = oldval + FixedMul(rendertimefrac, newval - oldval); + interpx = R_InterpolateFixed(oldval, newval); newval = (48 - (32 * max(0, progress - 76))) * FRACUNIT; oldval = (48 - (32 * max(0, progressOld - 76))) * FRACUNIT; - interpy = oldval + FixedMul(rendertimefrac, newval - oldval); + interpy = R_InterpolateFixed(oldval, newval); V_DrawFixedPatch( interpx, interpy, @@ -4054,7 +3970,7 @@ static void K_drawLapStartAnim(void) { newval = (4 - abs((signed)((leveltime % 8) - 4))) * FRACUNIT; oldval = (4 - abs((signed)((leveltimeOld % 8) - 4))) * FRACUNIT; - interpy += oldval + FixedMul(rendertimefrac, newval - oldval); + interpy += R_InterpolateFixed(oldval, newval); V_DrawFixedPatch( interpx, interpy, @@ -4066,7 +3982,7 @@ static void K_drawLapStartAnim(void) { newval = (62 - (32 * max(0, progress - 76))) * FRACUNIT; oldval = (62 - (32 * max(0, progressOld - 76))) * FRACUNIT; - interpx = oldval + FixedMul(rendertimefrac, newval - oldval); + interpx = R_InterpolateFixed(oldval, newval); V_DrawFixedPatch( interpx, // 27 @@ -4078,7 +3994,7 @@ static void K_drawLapStartAnim(void) { newval = (188 + (32 * max(0, progress - 76))) * FRACUNIT; oldval = (188 + (32 * max(0, progressOld - 76))) * FRACUNIT; - interpx = oldval + FixedMul(rendertimefrac, newval - oldval); + interpx = R_InterpolateFixed(oldval, newval); V_DrawFixedPatch( interpx, // 194 @@ -4091,7 +4007,7 @@ static void K_drawLapStartAnim(void) { newval = (82 - (32 * max(0, progress - 76))) * FRACUNIT; oldval = (82 - (32 * max(0, progressOld - 76))) * FRACUNIT; - interpx = oldval + FixedMul(rendertimefrac, newval - oldval); + interpx = R_InterpolateFixed(oldval, newval); V_DrawFixedPatch( interpx, // 61 @@ -4103,7 +4019,7 @@ static void K_drawLapStartAnim(void) { newval = (188 + (32 * max(0, progress - 76))) * FRACUNIT; oldval = (188 + (32 * max(0, progressOld - 76))) * FRACUNIT; - interpx = oldval + FixedMul(rendertimefrac, newval - oldval); + interpx = R_InterpolateFixed(oldval, newval); V_DrawFixedPatch( interpx, // 194 @@ -4115,7 +4031,7 @@ static void K_drawLapStartAnim(void) { newval = (208 + (32 * max(0, progress - 76))) * FRACUNIT; oldval = (208 + (32 * max(0, progressOld - 76))) * FRACUNIT; - interpx = oldval + FixedMul(rendertimefrac, newval - oldval); + interpx = R_InterpolateFixed(oldval, newval); V_DrawFixedPatch( interpx, // 221 diff --git a/src/r_fps.c b/src/r_fps.c index 7258d3b75..59f10bae8 100644 --- a/src/r_fps.c +++ b/src/r_fps.c @@ -32,7 +32,6 @@ static viewvars_t skyview_new[MAXSPLITSCREENPLAYERS]; static viewvars_t *oldview = &pview_old[0]; viewvars_t *newview = &pview_new[0]; - enum viewcontext_e viewcontext = VIEWCONTEXT_PLAYER1; static fixed_t R_LerpFixed(fixed_t from, fixed_t to, fixed_t frac) @@ -147,3 +146,23 @@ void R_SetViewContext(enum viewcontext_e _viewcontext) break; } } + +fixed_t R_InterpolateFixed(fixed_t from, fixed_t to) +{ + if (cv_frameinterpolation.value == 0) + { + return to; + } + + return (from + R_LerpFixed(from, to, rendertimefrac)); +} + +angle_t R_InterpolateAngle(angle_t from, angle_t to) +{ + if (cv_frameinterpolation.value == 0) + { + return to; + } + + return (from + R_LerpAngle(from, to, rendertimefrac)); +} diff --git a/src/r_fps.h b/src/r_fps.h index eb674b142..246c16e64 100644 --- a/src/r_fps.h +++ b/src/r_fps.h @@ -56,4 +56,7 @@ void R_UpdateViewInterpolation(void); // Set the current view context (the viewvars pointed to by newview) void R_SetViewContext(enum viewcontext_e _viewcontext); +fixed_t R_InterpolateFixed(fixed_t from, fixed_t to); +angle_t R_InterpolateAngle(angle_t from, angle_t to); + #endif diff --git a/src/r_things.c b/src/r_things.c index 3255d22bf..5fec86a7f 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -46,6 +46,7 @@ // SRB2kart #include "k_color.h" #include "k_kart.h" // HITLAGJITTERS +#include "r_fps.h" #define MINZ (FRACUNIT*4) #define BASEYCENTER (BASEVIDHEIGHT/2) @@ -1468,26 +1469,18 @@ static void R_ProjectSprite(mobj_t *thing) #endif // uncapped/interpolation - fixed_t interpx = thing->x; - fixed_t interpy = thing->y; - fixed_t interpz = thing->z; - angle_t interpangle = (thing->player ? thing->player->drawangle : thing->angle); + fixed_t interpx = R_InterpolateFixed(thing->old_x, thing->x); + fixed_t interpy = R_InterpolateFixed(thing->old_y, thing->y); + fixed_t interpz = R_InterpolateFixed(thing->old_z, thing->z); + angle_t interpangle = ANGLE_MAX; - // do interpolation - if (cv_frameinterpolation.value == 1) + if (thing->player) { - interpx = thing->old_x + FixedMul(rendertimefrac, thing->x - thing->old_x); - interpy = thing->old_y + FixedMul(rendertimefrac, thing->y - thing->old_y); - interpz = thing->old_z + FixedMul(rendertimefrac, thing->z - thing->old_z); - - if (thing->player) - { - interpangle = thing->player->old_drawangle + FixedMul(rendertimefrac, thing->player->drawangle - thing->player->old_drawangle); - } - else - { - interpangle = thing->old_angle + FixedMul(rendertimefrac, thing->angle - thing->old_angle); - } + interpangle = R_InterpolateAngle(thing->player->old_drawangle, thing->player->drawangle); + } + else + { + interpangle = R_InterpolateAngle(thing->old_angle, thing->angle); } // hitlag vibrating (todo: interp somehow?) @@ -1815,11 +1808,10 @@ static void R_ProjectSprite(mobj_t *thing) fixed_t linkscale; thing = thing->tracer; - if (cv_frameinterpolation.value == 1) - { - interpx = thing->old_x + FixedMul(thing->x - thing->old_x, rendertimefrac); - interpy = thing->old_y + FixedMul(thing->y - thing->old_y, rendertimefrac); - } + + interpx = R_InterpolateFixed(thing->old_x, thing->x); + interpy = R_InterpolateFixed(thing->old_y, thing->y); + interpz = R_InterpolateFixed(thing->old_z, thing->z); // hitlag vibrating (todo: interp somehow?) if (thing->hitlag > 0 && (thing->eflags & MFE_DAMAGEHITLAG)) @@ -2158,17 +2150,9 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) fixed_t gz, gzt; // uncapped/interpolation - fixed_t interpx = thing->x; - fixed_t interpy = thing->y; - fixed_t interpz = thing->z; - - // do interpolation - if (cv_frameinterpolation.value == 1) - { - interpx = thing->old_x + FixedMul(rendertimefrac, thing->x - thing->old_x); - interpy = thing->old_y + FixedMul(rendertimefrac, thing->y - thing->old_y); - interpz = thing->old_z + FixedMul(rendertimefrac, thing->z - thing->old_z); - } + fixed_t interpx = R_InterpolateFixed(thing->old_x, thing->x); + fixed_t interpy = R_InterpolateFixed(thing->old_y, thing->y); + fixed_t interpz = R_InterpolateFixed(thing->old_z, thing->z); // transform the origin point tr_x = interpx - viewx; From bfaefe4ee5260db6f19b72486f72ba155915aedd Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Dec 2021 12:45:51 -0500 Subject: [PATCH 080/379] P_InitAngle, to fix angle interpolation on spawning objects --- src/k_battle.c | 4 +- src/k_kart.c | 70 ++++++++++++++++++------------ src/k_race.c | 4 +- src/k_respawn.c | 4 +- src/lua_baselib.c | 41 +++++++++++++++++- src/p_enemy.c | 81 +++++++++++++++++----------------- src/p_inter.c | 20 ++++++--- src/p_local.h | 7 ++- src/p_map.c | 30 +++++++++++-- src/p_mobj.c | 108 +++++++++++++++++++++++++--------------------- src/p_saveg.c | 20 ++++----- src/p_slopes.c | 4 +- src/p_spec.c | 4 +- src/p_telept.c | 4 +- src/p_user.c | 20 ++++----- 15 files changed, 258 insertions(+), 163 deletions(-) diff --git a/src/k_battle.c b/src/k_battle.c index 8a5257206..c4902fb05 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -235,7 +235,7 @@ mobj_t *K_SpawnSphereBox(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 f (void)amount; - drop->angle = angle; + P_InitAngle(drop, angle); P_Thrust(drop, FixedAngle(P_RandomFixed() * 180) + angle, P_RandomRange(4, 12) * mapobjectscale); @@ -529,7 +529,7 @@ static void K_SpawnOvertimeLaser(fixed_t x, fixed_t y, fixed_t scale) mo->eflags |= MFE_VERTICALFLIP; } - mo->angle = R_PointToAngle2(mo->x, mo->y, battleovertime.x, battleovertime.y) + ANGLE_90; + P_InitAngle(mo, R_PointToAngle2(mo->x, mo->y, battleovertime.x, battleovertime.y) + ANGLE_90); mo->renderflags |= (RF_DONTDRAW & ~(K_GetPlayerDontDrawFlag(player))); P_SetScale(mo, scale); diff --git a/src/k_kart.c b/src/k_kart.c index 08b54f30b..4bc39f590 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1963,7 +1963,7 @@ void K_SpawnDashDustRelease(player_t *player) dust = P_SpawnMobj(newx, newy, player->mo->z, MT_FASTDUST); P_SetTarget(&dust->target, player->mo); - dust->angle = travelangle - ((i&1) ? -1 : 1)*ANGLE_45; + P_InitAngle(dust, travelangle - ((i&1) ? -1 : 1) * ANGLE_45); dust->destscale = player->mo->scale; P_SetScale(dust, player->mo->scale); @@ -2043,7 +2043,7 @@ void K_SpawnNormalSpeedLines(player_t *player) MT_FASTLINE); P_SetTarget(&fast->target, player->mo); - fast->angle = K_MomentumAngle(player->mo); + P_InitAngle(fast, K_MomentumAngle(player->mo)); fast->momx = 3*player->mo->momx/4; fast->momy = 3*player->mo->momy/4; fast->momz = 3*P_GetMobjZMovement(player->mo)/4; @@ -2071,7 +2071,7 @@ void K_SpawnInvincibilitySpeedLines(mobj_t *mo) fast->momz = 3*P_GetMobjZMovement(mo)/4; P_SetTarget(&fast->target, mo); - fast->angle = K_MomentumAngle(mo); + P_InitAngle(fast, K_MomentumAngle(mo)); fast->color = mo->color; fast->colorized = true; K_MatchGenericExtraFlags(fast, mo); @@ -3519,7 +3519,7 @@ void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount) P_SetTarget(&newmo->tracer, victim->mo); P_SetTarget(&newmo->target, player->mo); - newmo->angle = (diff * (newbumper-1)); + P_InitAngle(newmo, (diff * (newbumper-1))); newmo->color = victim->skincolor; if (newbumper+1 < 2) @@ -3600,7 +3600,7 @@ void K_SpawnKartExplosion(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 mobj->z -= mobj->height>>1; // change angle - mobj->angle = R_PointToAngle2(mobj->x, mobj->y, x, y); + P_InitAngle(mobj, R_PointToAngle2(mobj->x, mobj->y, x, y)); // change slope dist = P_AproxDistance(P_AproxDistance(x - mobj->x, y - mobj->y), z - mobj->z); @@ -3687,7 +3687,7 @@ void K_SpawnMineExplosion(mobj_t *source, UINT8 color) { dust = P_SpawnMobj(source->x, source->y, source->z, MT_SMOKE); P_SetMobjState(dust, S_OPAQUESMOKE1); - dust->angle = (ANGLE_180/16) * i; + P_InitAngle(dust, (ANGLE_180/16) * i); P_SetScale(dust, source->scale); dust->destscale = source->scale*10; dust->scalespeed = source->scale/12; @@ -3800,7 +3800,7 @@ static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, I th->z = th->floorz; } - th->angle = an; + P_InitAngle(th, an); th->momx = FixedMul(finalspeed, FINECOSINE(an>>ANGLETOFINESHIFT)); th->momy = FixedMul(finalspeed, FINESINE(an>>ANGLETOFINESHIFT)); @@ -3968,7 +3968,7 @@ static void K_SpawnDriftElectricity(player_t *player) y = P_ReturnThrustY(mo, verticalangle, verticalradius) + P_ReturnThrustY(mo, horizonatalangle, horizontalradius); spark = P_SpawnMobjFromMobj(mo, x, y, 0, MT_DRIFTELECTRICITY); - spark->angle = sparkangle; + P_InitAngle(spark, sparkangle); spark->color = color; K_GenericExtraFlagsNoZAdjust(spark, mo); @@ -4010,7 +4010,7 @@ void K_SpawnDriftElectricSparks(player_t *player) fixed_t yoff = P_ReturnThrustY(mo, sparkangle, sparkradius); mobj_t *spark = P_SpawnMobjFromMobj(mo, x + xoff, y + yoff, z, MT_DRIFTELECTRICSPARK); - spark->angle = sparkangle; + P_InitAngle(spark, sparkangle); spark->color = color; P_InstaThrust(spark, mo->angle + ANGLE_90, hspeed); P_SetObjectMomZ(spark, vspeed, false); @@ -4061,7 +4061,7 @@ static void K_SpawnDriftSparks(player_t *player) spark = P_SpawnMobj(newx, newy, player->mo->z, MT_DRIFTSPARK); P_SetTarget(&spark->target, player->mo); - spark->angle = travelangle-(ANGLE_45/5)*player->drift; + P_InitAngle(spark, travelangle-(ANGLE_45/5)*player->drift); spark->destscale = player->mo->scale; P_SetScale(spark, player->mo->scale); @@ -4205,7 +4205,7 @@ static void K_SpawnAIZDust(player_t *player) newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle - (player->aizdriftstrat*ANGLE_45), FixedMul(24*FRACUNIT, player->mo->scale)); spark = P_SpawnMobj(newx, newy, player->mo->z, MT_AIZDRIFTSTRAT); - spark->angle = travelangle+(player->aizdriftstrat*ANGLE_90); + P_InitAngle(spark, travelangle+(player->aizdriftstrat*ANGLE_90)); P_SetScale(spark, (spark->destscale = (3*player->mo->scale)>>2)); spark->momx = (6*player->mo->momx)/5; @@ -4257,7 +4257,7 @@ void K_SpawnBoostTrail(player_t *player) flame = P_SpawnMobj(newx, newy, ground, MT_SNEAKERTRAIL); P_SetTarget(&flame->target, player->mo); - flame->angle = travelangle; + P_InitAngle(flame, travelangle); flame->fuse = TICRATE*2; flame->destscale = player->mo->scale; P_SetScale(flame, player->mo->scale); @@ -4313,7 +4313,7 @@ void K_SpawnSparkleTrail(mobj_t *mo) newz = mo->z + (P_RandomRange(0, mo->height>>FRACBITS)*FRACUNIT); sparkle = P_SpawnMobj(newx, newy, newz, MT_SPARKLETRAIL); - sparkle->angle = R_PointToAngle2(mo->x, mo->y, sparkle->x, sparkle->y); + P_InitAngle(sparkle, R_PointToAngle2(mo->x, mo->y, sparkle->x, sparkle->y)); sparkle->movefactor = R_PointToDist2(mo->x, mo->y, sparkle->x, sparkle->y); // Save the distance we spawned away from the player. //CONS_Printf("movefactor: %d\n", sparkle->movefactor/FRACUNIT); sparkle->extravalue1 = (sparkle->z - mo->z); // Keep track of our Z position relative to the player's, I suppose. @@ -4359,7 +4359,7 @@ void K_SpawnWipeoutTrail(mobj_t *mo, boolean offroad) mo->z, MT_WIPEOUTTRAIL); P_SetTarget(&dust->target, mo); - dust->angle = K_MomentumAngle(mo); + P_InitAngle(dust, K_MomentumAngle(mo)); dust->destscale = mo->scale; P_SetScale(dust, mo->scale); K_FlipFromObject(dust, mo); @@ -4429,7 +4429,7 @@ void K_SpawnDraftDust(mobj_t *mo) P_SetMobjState(dust, S_DRAFTDUST1 + foff); P_SetTarget(&dust->target, mo); - dust->angle = ang - (ANGLE_90 * sign); // point completely perpendicular from the player + P_InitAngle(dust, ang - (ANGLE_90 * sign)); // point completely perpendicular from the player dust->destscale = mo->scale; P_SetScale(dust, mo->scale); K_FlipFromObject(dust, mo); @@ -4852,11 +4852,18 @@ void K_PuntMine(mobj_t *origMine, mobj_t *punter) mine = P_SpawnMobj(origMine->x, origMine->y, origMine->z, MT_SSMINE); P_SetTarget(&mine->target, mineOwner); + mine->angle = origMine->angle; mine->flags2 = origMine->flags2; mine->floorz = origMine->floorz; mine->ceilingz = origMine->ceilingz; + // Copy interp data + mine->old_angle = origMine->old_angle; + mine->old_x = origMine->old_x; + mine->old_y = origMine->old_y; + mine->old_z = origMine->old_z; + // Since we aren't using P_KillMobj, we need to clean up the hnext reference P_SetTarget(&mineOwner->hnext, NULL); mineOwner->player->bananadrag = 0; @@ -4932,7 +4939,7 @@ static void K_DoThunderShield(player_t *player) for (i=0; i<7; i++) { mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THOK); - mo->angle = P_RandomRange(0, 359)*ANG1; + P_InitAngle(mo, P_RandomRange(0, 359)*ANG1); mo->fuse = P_RandomRange(20, 50); P_SetTarget(&mo->target, player->mo); P_SetMobjState(mo, S_KLIT1); @@ -4945,7 +4952,7 @@ static void K_DoThunderShield(player_t *player) sx = player->mo->x + FixedMul((player->mo->scale*THUNDERRADIUS), FINECOSINE((an*i)>>ANGLETOFINESHIFT)); sy = player->mo->y + FixedMul((player->mo->scale*THUNDERRADIUS), FINESINE((an*i)>>ANGLETOFINESHIFT)); mo = P_SpawnMobj(sx, sy, player->mo->z, MT_THOK); - mo-> angle = an*i; + P_InitAngle(mo, an*i); mo->extravalue1 = THUNDERRADIUS; // Used to know whether we should teleport by radius or something. mo->scale = player->mo->scale*3; P_SetTarget(&mo->target, player->mo); @@ -5263,7 +5270,7 @@ static void K_ThrowLandMine(player_t *player) P_SetScale(landMine, player->mo->scale); landMine->destscale = player->mo->destscale; - landMine->angle = player->mo->angle; + P_InitAngle(landMine, player->mo->angle); landMine->momz = (30 * mapobjectscale * P_MobjFlip(player->mo)) + player->mo->momz; landMine->color = player->skincolor; @@ -5419,6 +5426,12 @@ void K_DropHnextList(player_t *player, boolean keepshields) dropwork->floorz = work->floorz; dropwork->ceilingz = work->ceilingz; + // Copy interp data + dropwork->old_angle = work->old_angle; + dropwork->old_x = work->old_x; + dropwork->old_y = work->old_y; + dropwork->old_z = work->old_z; + if (ponground) { // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn @@ -5506,7 +5519,7 @@ mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 P_SetScale(drop, drop->scale>>4); drop->destscale = (3*drop->destscale)/2; - drop->angle = angle; + P_InitAngle(drop, angle); P_Thrust(drop, FixedAngle(P_RandomFixed() * 180) + angle, 16*mapobjectscale); @@ -5864,7 +5877,7 @@ static void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z } //mobj->standingslope = slope; - P_SetPitchRollFromSlope(mobj, slope); + P_InitPitchRollFromSlope(mobj, slope); } // Move the hnext chain! @@ -6956,7 +6969,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) { mobj_t *ring = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_RING); ring->extravalue1 = 1; // Ring collect animation timer - ring->angle = player->mo->angle; // animation angle + P_InitAngle(ring, player->mo->angle); // animation angle P_SetTarget(&ring->target, player->mo); // toucher for thinker player->pickuprings++; if (player->superring <= 3) @@ -7768,6 +7781,7 @@ void K_SpawnDriftBoostExplosion(player_t *player, int stage) { mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_DRIFTEXPLODE); + P_InitAngle(overlay, K_MomentumAngle(player->mo)); P_SetTarget(&overlay->target, player->mo); P_SetScale(overlay, (overlay->destscale = player->mo->scale)); K_FlipFromObject(overlay, player->mo); @@ -8313,9 +8327,9 @@ static void K_KartSpindashWind(mobj_t *parent) P_SetTarget(&wind->target, parent); if (parent->momx || parent->momy) - wind->angle = R_PointToAngle2(0, 0, parent->momx, parent->momy); + P_InitAngle(wind, R_PointToAngle2(0, 0, parent->momx, parent->momy)); else - wind->angle = parent->player->drawangle; + P_InitAngle(wind, parent->player->drawangle); wind->momx = 3 * parent->momx / 4; wind->momy = 3 * parent->momy / 4; @@ -8348,7 +8362,7 @@ static void K_KartSpindash(player_t *player) mobj_t *grease; grease = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_TIREGREASE); P_SetTarget(&grease->target, player->mo); - grease->angle = K_MomentumAngle(player->mo); + P_InitAngle(grease, K_MomentumAngle(player->mo)); grease->extravalue1 = i; } } @@ -8762,7 +8776,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_ROCKETSNEAKER); K_MatchGenericExtraFlags(mo, player->mo); mo->flags |= MF_NOCLIPTHING; - mo->angle = player->mo->angle; + P_InitAngle(mo, player->mo->angle); mo->threshold = 10; mo->movecount = moloop%2; mo->movedir = mo->lastlook = moloop+1; @@ -8872,7 +8886,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) break; } mo->flags |= MF_NOCLIPTHING; - mo->angle = newangle; + P_InitAngle(mo, newangle); mo->threshold = 10; mo->movecount = player->itemamount; mo->movedir = mo->lastlook = moloop+1; @@ -8913,7 +8927,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) break; } mo->flags |= MF_NOCLIPTHING; - mo->angle = newangle; + P_InitAngle(mo, newangle); mo->threshold = 10; mo->movecount = player->itemamount; mo->movedir = mo->lastlook = moloop+1; @@ -9309,7 +9323,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) spdl = P_SpawnMobj(sx, sy, sz, MT_FASTLINE); P_SetTarget(&spdl->target, player->mo); - spdl->angle = R_PointToAngle2(spdl->x, spdl->y, player->mo->x, player->mo->y); + P_InitAngle(spdl, R_PointToAngle2(spdl->x, spdl->y, player->mo->x, player->mo->y)); spdl->rollangle = -ANG1*90*P_MobjFlip(player->mo); // angle them downwards relative to the player's gravity... spdl->spriteyscale = player->trickboostpower+FRACUNIT; spdl->momx = player->mo->momx; diff --git a/src/k_race.c b/src/k_race.c index ec3bd9b4f..f459e7a80 100644 --- a/src/k_race.c +++ b/src/k_race.c @@ -370,7 +370,7 @@ static void K_DrawFinishLineBeamForLine(fixed_t offset, angle_t aiming, line_t * P_SetMobjState(end1, S_FINISHBEAMEND1); end1->renderflags = RF_DONTDRAW & ~K_GetPlayerDontDrawFlag(&players[displayplayers[i]]); - end1->angle = lineangle; + P_InitAngle(end1, lineangle); end2 = P_SpawnMobj( v->x + (8*sx), @@ -381,7 +381,7 @@ static void K_DrawFinishLineBeamForLine(fixed_t offset, angle_t aiming, line_t * P_SetMobjState(end2, S_FINISHBEAMEND2); end2->renderflags = RF_DONTDRAW & ~K_GetPlayerDontDrawFlag(&players[displayplayers[i]]); - end2->angle = lineangle; + P_InitAngle(end2, lineangle); P_SetTarget(&end2->tracer, end1); end2->flags2 |= MF2_LINKDRAW; diff --git a/src/k_respawn.c b/src/k_respawn.c index 2e93a57d8..b79a5ca69 100644 --- a/src/k_respawn.c +++ b/src/k_respawn.c @@ -582,7 +582,7 @@ static void K_MovePlayerToRespawnPoint(player_t *player) P_SetTarget(&lasermo->target, player->mo); - lasermo->angle = stepha + ANGLE_90; + P_InitAngle(lasermo, stepha + ANGLE_90); P_SetScale(lasermo, (lasermo->destscale = player->mo->scale)); } } @@ -645,7 +645,7 @@ static void K_DropDashWait(player_t *player) P_SetTarget(&laser->target, player->mo); - laser->angle = newangle + ANGLE_90; + P_InitAngle(laser, newangle + ANGLE_90); laser->momz = (8 * player->mo->scale) * P_MobjFlip(player->mo); P_SetScale(laser, (laser->destscale = player->mo->scale)); } diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 1f7fb8bc3..d9e57ede1 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -1414,7 +1414,7 @@ static int lib_pTeleportMove(lua_State *L) INLEVEL if (!thing) return LUA_ErrInvalid(L, "mobj_t"); - LUA_Deprecated(L, "P_TeleportMove", "P_SetOrigin or P_MoveOrigin"); + LUA_Deprecated(L, "P_TeleportMove", "P_SetOrigin\" or \"P_MoveOrigin"); lua_pushboolean(L, P_SetOrigin(thing, x, y, z)); LUA_PushUserdata(L, tmthing, META_MOBJ); P_SetTarget(&tmthing, ptmthing); @@ -1455,6 +1455,42 @@ static int lib_pMoveOrigin(lua_State *L) return 2; } +static int lib_pInitAngle(lua_State *L) +{ + mobj_t *thing = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + angle_t newValue = luaL_checkangle(L, 2); + NOHUD + INLEVEL + if (!thing) + return LUA_ErrInvalid(L, "mobj_t"); + P_InitAngle(thing, newValue); + return 0; +} + +static int lib_pInitPitch(lua_State *L) +{ + mobj_t *thing = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + angle_t newValue = luaL_checkangle(L, 2); + NOHUD + INLEVEL + if (!thing) + return LUA_ErrInvalid(L, "mobj_t"); + P_InitPitch(thing, newValue); + return 0; +} + +static int lib_pInitRoll(lua_State *L) +{ + mobj_t *thing = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + angle_t newValue = luaL_checkangle(L, 2); + NOHUD + INLEVEL + if (!thing) + return LUA_ErrInvalid(L, "mobj_t"); + P_InitRoll(thing, newValue); + return 0; +} + static int lib_pSlideMove(lua_State *L) { mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); @@ -3871,6 +3907,9 @@ static luaL_Reg lib[] = { {"P_TeleportMove",lib_pTeleportMove}, {"P_SetOrigin",lib_pSetOrigin}, {"P_MoveOrigin",lib_pMoveOrigin}, + {"P_InitAngle",lib_pInitAngle}, + {"P_InitPitch",lib_pInitPitch}, + {"P_InitRoll",lib_pInitRoll}, {"P_SlideMove",lib_pSlideMove}, {"P_BounceMove",lib_pBounceMove}, {"P_CheckSight", lib_pCheckSight}, diff --git a/src/p_enemy.c b/src/p_enemy.c index 749d0dc09..0eccc5f55 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -1220,7 +1220,7 @@ void A_StatueBurst(mobj_t *actor) if (!locvar1 || !(new = P_SpawnMobjFromMobj(actor, 0, 0, 0, locvar1))) return; - new->angle = actor->angle; + P_InitAngle(new, actor->angle); P_SetTarget(&new->target, actor->target); if (locvar2) P_SetMobjState(new, (statenum_t)locvar2); @@ -2519,8 +2519,8 @@ void A_LobShot(mobj_t *actor) P_SetTarget(&shot->target, actor); // where it came from - shot->angle = an = actor->angle; - an >>= ANGLETOFINESHIFT; + P_InitAngle(shot, actor->angle); + an = actor->angle >> ANGLETOFINESHIFT; dist = P_AproxDistance(actor->target->x - shot->x, actor->target->y - shot->y); @@ -2886,7 +2886,7 @@ void A_Boss1Laser(mobj_t *actor) S_StartSound(actor, mobjinfo[locvar1].seesound); point = P_SpawnMobj(x + P_ReturnThrustX(actor, actor->angle, actor->radius), y + P_ReturnThrustY(actor, actor->angle, actor->radius), actor->z - actor->height / 2, MT_EGGMOBILE_TARGET); - point->angle = actor->angle; + P_InitAngle(point, actor->angle); point->fuse = dur+1; P_SetTarget(&point->target, actor->target); P_SetTarget(&actor->target, point); @@ -2896,7 +2896,7 @@ void A_Boss1Laser(mobj_t *actor) point = P_SpawnMobj(x, y, z, locvar1); P_SetTarget(&point->target, actor); - point->angle = actor->angle; + P_InitAngle(point, actor->angle); speed = point->radius; point->momz = FixedMul(FINECOSINE(angle>>ANGLETOFINESHIFT), speed); point->momx = FixedMul(FINESINE(angle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(point->angle>>ANGLETOFINESHIFT), speed)); @@ -2905,7 +2905,7 @@ void A_Boss1Laser(mobj_t *actor) for (i = 0; i < 256; i++) { mobj_t *mo = P_SpawnMobj(point->x, point->y, point->z, point->type); - mo->angle = point->angle; + P_InitAngle(mo, point->angle); mo->color = LASERCOLORS[((UINT8)(i + 3*dur) >> 2) % sizeof(LASERCOLORS)]; // codeing P_UnsetThingPosition(mo); mo->flags = MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY; @@ -2937,7 +2937,7 @@ void A_Boss1Laser(mobj_t *actor) if (z - floorz < mobjinfo[MT_EGGMOBILE_FIRE].height>>1 && dur & 1) { point = P_SpawnMobj(x, y, floorz, MT_EGGMOBILE_FIRE); - point->angle = actor->angle; + P_InitAngle(point, actor->angle); point->destscale = actor->scale; P_SetScale(point, point->destscale); P_SetTarget(&point->target, actor); @@ -3521,7 +3521,7 @@ bossjustdie: P_ReturnThrustX(mo, mo->angle - ANGLE_90, 32<angle - ANGLE_90, 32<angle = mo->angle; + P_InitAngle(mo2, mo->angle); P_InstaThrust(mo2, mo2->angle - ANGLE_90, 4*mo2->scale); P_SetObjectMomZ(mo2, 4*FRACUNIT, false); P_SetMobjState(mo2, S_BOSSEGLZ1); @@ -3530,7 +3530,7 @@ bossjustdie: P_ReturnThrustX(mo, mo->angle + ANGLE_90, 32<angle + ANGLE_90, 32<angle = mo->angle; + P_InitAngle(mo2, mo->angle); P_InstaThrust(mo2, mo2->angle + ANGLE_90, 4*mo2->scale); P_SetObjectMomZ(mo2, 4*FRACUNIT, false); P_SetMobjState(mo2, S_BOSSEGLZ2); @@ -3542,7 +3542,7 @@ bossjustdie: P_ReturnThrustX(mo, mo->angle - ANGLE_90, 32<angle - ANGLE_90, 32<angle = mo->angle; + P_InitAngle(mo2, mo->angle); P_InstaThrust(mo2, mo2->angle - ANGLE_90, 4*mo2->scale); P_SetObjectMomZ(mo2, 4*FRACUNIT, false); P_SetMobjState(mo2, S_BOSSTANK1); @@ -3551,7 +3551,7 @@ bossjustdie: P_ReturnThrustX(mo, mo->angle + ANGLE_90, 32<angle + ANGLE_90, 32<angle = mo->angle; + P_InitAngle(mo2, mo->angle); P_InstaThrust(mo2, mo2->angle + ANGLE_90, 4*mo2->scale); P_SetObjectMomZ(mo2, 4*FRACUNIT, false); P_SetMobjState(mo2, S_BOSSTANK2); @@ -3559,7 +3559,7 @@ bossjustdie: mo2 = P_SpawnMobjFromMobj(mo, 0, 0, mobjinfo[MT_EGGMOBILE2].height + (32<angle = mo->angle; + P_InitAngle(mo2, mo->angle); P_SetObjectMomZ(mo2, 4*FRACUNIT, false); mo2->momz += mo->momz; P_SetMobjState(mo2, S_BOSSSPIGOT); @@ -3568,7 +3568,7 @@ bossjustdie: case MT_EGGMOBILE3: { mo2 = P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_BOSSJUNK); - mo2->angle = mo->angle; + P_InitAngle(mo2, mo->angle); P_SetMobjState(mo2, S_BOSSSEBH1); } break; @@ -3642,7 +3642,8 @@ bossjustdie: pole->tracer->flags |= MF_NOCLIPTHING; P_SetScale(pole, (pole->destscale = 2*FRACUNIT)); P_SetScale(pole->tracer, (pole->tracer->destscale = 2*FRACUNIT)); - pole->angle = pole->tracer->angle = mo->tracer->angle; + P_InitAngle(pole, mo->tracer->angle); + P_InitAngle(pole->tracer, mo->tracer->angle); pole->tracer->tracer->angle = pole->angle - ANGLE_90; pole->momx = P_ReturnThrustX(pole, pole->angle, speed); pole->momy = P_ReturnThrustY(pole, pole->angle, speed); @@ -4011,7 +4012,7 @@ void A_AttractChase(mobj_t *actor) sparkle = P_SpawnMobj(actor->target->x, actor->target->y, actor->target->z, MT_RINGSPARKS); P_SetTarget(&sparkle->target, actor->target); - sparkle->angle = (actor->target->angle + (offset>>1)) + (offset * actor->target->player->sparkleanim); + P_InitAngle(sparkle, (actor->target->angle + (offset>>1)) + (offset * actor->target->player->sparkleanim)); actor->target->player->sparkleanim = (actor->target->player->sparkleanim+1) % 20; P_KillMobj(actor, actor->target, actor->target, DMG_NORMAL); @@ -5265,7 +5266,7 @@ void A_RockSpawn(mobj_t *actor) mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FALLINGROCK); P_SetMobjState(mo, mobjinfo[type].spawnstate); - mo->angle = R_PointToAngle2(line->v2->x, line->v2->y, line->v1->x, line->v1->y); + P_InitAngle(mo, R_PointToAngle2(line->v2->x, line->v2->y, line->v1->x, line->v1->y)); P_InstaThrust(mo, mo->angle, dist + randomoomph); mo->momz = dist + randomoomph; @@ -7131,7 +7132,7 @@ void A_Boss3ShockThink(mobj_t *actor) snew->momx = (actor->momx + snext->momx) >> 1; snew->momy = (actor->momy + snext->momy) >> 1; snew->momz = (actor->momz + snext->momz) >> 1; // is this really needed? - snew->angle = (actor->angle + snext->angle) >> 1; + P_InitAngle(snew, (actor->angle + snext->angle) >> 1); P_SetTarget(&snew->target, actor->target); snew->fuse = actor->fuse; @@ -7283,7 +7284,7 @@ void A_SpawnObjectAbsolute(mobj_t *actor) mo = P_SpawnMobj(x<angle = actor->angle; + P_InitAngle(mo, actor->angle); if (actor->eflags & MFE_VERTICALFLIP) mo->flags2 |= MF2_OBJECTFLIP; @@ -7325,7 +7326,7 @@ void A_SpawnObjectRelative(mobj_t *actor) (actor->eflags & MFE_VERTICALFLIP) ? ((actor->z + actor->height - mobjinfo[type].height) - FixedMul(z<scale)) : (actor->z + FixedMul(z<scale)), type); // Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn - mo->angle = actor->angle; + P_InitAngle(mo, actor->angle); if (actor->eflags & MFE_VERTICALFLIP) mo->flags2 |= MF2_OBJECTFLIP; @@ -8035,7 +8036,7 @@ void A_BossJetFume(mobj_t *actor) P_SetScale(filler, filler->destscale); if (actor->eflags & MFE_VERTICALFLIP) filler->flags2 |= MF2_OBJECTFLIP; - filler->angle = actor->angle - ANGLE_180; + P_InitAngle(filler, actor->angle - ANGLE_180); P_SetTarget(&actor->tracer, filler); }*/ @@ -9745,7 +9746,7 @@ void A_TrapShot(mobj_t *actor) S_StartSound(missile, missile->info->seesound); P_SetTarget(&missile->target, actor); - missile->angle = actor->angle; + P_InitAngle(missile, actor->angle); speed = FixedMul(missile->info->speed, missile->scale); @@ -10324,7 +10325,7 @@ void A_BrakLobShot(mobj_t *actor) S_StartSound(shot, shot->info->seesound); P_SetTarget(&shot->target, actor); // where it came from - shot->angle = actor->angle; + P_InitAngle(shot, actor->angle); // Horizontal axes first. First parameter is initial horizontal impulse, second is to correct its angle. shot->momx = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINECOSINE(shot->angle >> ANGLETOFINESHIFT)); @@ -10391,7 +10392,7 @@ void A_NapalmScatter(mobj_t *actor) mo = P_SpawnMobj(actor->x, actor->y, actor->z, typeOfShot); P_SetTarget(&mo->target, actor->target); // Transfer target so Brak doesn't hit himself like an idiot - mo->angle = fa << ANGLETOFINESHIFT; + P_InitAngle(mo, fa << ANGLETOFINESHIFT); mo->momx = FixedMul(FINECOSINE(fa),vx); mo->momy = FixedMul(FINESINE(fa),vx); mo->momz = vy; @@ -10415,7 +10416,7 @@ void A_SpawnFreshCopy(mobj_t *actor) newObject = P_SpawnMobjFromMobj(actor, 0, 0, 0, actor->type); newObject->flags2 = actor->flags2 & MF2_AMBUSH; - newObject->angle = actor->angle; + P_InitAngle(newObject, actor->angle); newObject->color = actor->color; P_SetTarget(&newObject->target, actor->target); P_SetTarget(&newObject->tracer, actor->tracer); @@ -10451,7 +10452,7 @@ mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz } flicky = P_SpawnMobjFromMobj(actor, offsx, offsy, 0, flickytype); - flicky->angle = actor->angle; + P_InitAngle(flicky, actor->angle); if (flickytype == MT_SEED) flicky->z += P_MobjFlip(actor)*(actor->height - flicky->height)/2; @@ -10601,7 +10602,7 @@ void A_FlickyCenter(mobj_t *actor) else if (actor->flags & MF_SLIDEME) // aimless { actor->tracer->fuse = 0; // less than 2*TICRATE means move aimlessly. - actor->tracer->angle = P_RandomKey(180)*ANG2; + P_InitAngle(actor->tracer, P_RandomKey(180)*ANG2); } else //orbit actor->tracer->fuse = FRACUNIT; @@ -11302,7 +11303,7 @@ void A_ConnectToGround(mobj_t *actor) { work = P_SpawnMobjFromMobj(actor, 0, 0, workz, locvar1); if (work) - work->angle = ang; + P_InitAngle(work, ang); ang += ANGLE_90; workz += workh; } @@ -11348,7 +11349,7 @@ void A_SpawnParticleRelative(mobj_t *actor) (actor->eflags & MFE_VERTICALFLIP) ? ((actor->z + actor->height - mobjinfo[MT_PARTICLE].height) - FixedMul(z<scale)) : (actor->z + FixedMul(z<scale)), MT_PARTICLE); // Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn - mo->angle = actor->angle; + P_InitAngle(mo, actor->angle); if (actor->eflags & MFE_VERTICALFLIP) mo->flags2 |= MF2_OBJECTFLIP; @@ -12095,7 +12096,7 @@ void A_Boss5MakeJunk(mobj_t *actor) broked->fuse = TICRATE; else broked->fuse = (((locvar2 & 1) ? 4 : 2)*TICRATE)/3; - broked->angle = ang; + P_InitAngle(broked, ang); P_InstaThrust(broked, ang, ((locvar2 & 2) ? 8 : 5)*actor->scale); P_SetObjectMomZ(broked, (((locvar2) ? 4 : 0) + P_RandomRange(2, 5))< 0) @@ -12174,7 +12175,7 @@ static void P_DustRing(mobjtype_t mobjtype, UINT32 div, fixed_t x, fixed_t y, fi mobjtype ); - dust->angle = ang*i + ANGLE_90; + P_InitAngle(dust, ang*i + ANGLE_90); P_SetScale(dust, FixedMul(initscale, scale)); dust->destscale = FixedMul(4*FRACUNIT + P_RandomFixed(), scale); dust->scalespeed = scale/24; @@ -12379,7 +12380,7 @@ static mobj_t *P_TrainSeg(mobj_t *src, fixed_t x, fixed_t y, fixed_t z, angle_t s->fuse = 16*TICRATE; s->sprite = spr; s->frame = frame|FF_PAPERSPRITE; - s->angle = ang; + P_InitAngle(s, ang); P_Thrust(s, src->angle, 7*FRACUNIT); return s; } @@ -12751,7 +12752,7 @@ void A_SaloonDoorSpawn(mobj_t *actor) // One door... if (!(door = P_SpawnMobjFromMobj(actor, c, s, 0, locvar1))) return; - door->angle = ang + ANGLE_180; + P_InitAngle(door, ang + ANGLE_180); door->extravalue1 = AngleFixed(door->angle); // Origin angle door->extravalue2 = 0; // Angular speed P_SetTarget(&door->tracer, actor); // Origin door @@ -12759,7 +12760,7 @@ void A_SaloonDoorSpawn(mobj_t *actor) // ...two door! if (!(door = P_SpawnMobjFromMobj(actor, -c, -s, 0, locvar1))) return; - door->angle = ang; + P_InitAngle(door, ang); door->extravalue1 = AngleFixed(door->angle); // Origin angle door->extravalue2 = 0; // Angular speed P_SetTarget(&door->tracer, actor); // Origin door @@ -12955,7 +12956,7 @@ void A_SpawnPterabytes(mobj_t *actor) c = FINECOSINE(fa); s = FINESINE(fa); waypoint = P_SpawnMobjFromMobj(actor, FixedMul(c, rad), FixedMul(s, rad), 0, MT_PTERABYTEWAYPOINT); - waypoint->angle = ang + ANGLE_90; + P_InitAngle(waypoint, ang + ANGLE_90); P_SetTarget(&waypoint->tracer, actor); ptera = P_SpawnMobjFromMobj(waypoint, 0, 0, 0, MT_PTERABYTE); ptera->angle = waypoint->angle; @@ -13129,7 +13130,7 @@ void A_DragonbomberSpawn(mobj_t *actor) segment = P_SpawnMobjFromMobj(mo, x, y, 0, MT_DRAGONTAIL); P_SetTarget(&segment->target, mo); P_SetTarget(&mo->tracer, segment); - segment->angle = mo->angle; + P_InitAngle(segment, mo->angle); mo = segment; } for (i = 0; i < 2; i++) // spawn wings @@ -13531,7 +13532,7 @@ static void SpawnSPBDust(mobj_t *mo) P_SetScale(dust, mo->scale*2); dust->colorized = true; dust->color = SKINCOLOR_RED; - dust->angle = mo->angle - FixedAngle(FRACUNIT*90 - FRACUNIT*180*i); // The first one will spawn to the right of the spb, the second one to the left. + P_InitAngle(dust, mo->angle - FixedAngle(FRACUNIT*90 - FRACUNIT*180*i)); // The first one will spawn to the right of the spb, the second one to the left. P_Thrust(dust, dust->angle, 6*dust->scale); K_MatchGenericExtraFlags(dust, mo); @@ -13568,7 +13569,7 @@ static void SpawnSPBAIZDust(mobj_t *mo, INT32 dir) spark->flags = MF_NOGRAVITY|MF_PAIN; P_SetTarget(&spark->target, mo); - spark->angle = travelangle+(dir*ANGLE_90); + P_InitAngle(spark, travelangle+(dir*ANGLE_90)); P_SetScale(spark, (spark->destscale = mo->scale*3/2)); spark->momx = (6*mo->momx)/5; @@ -13587,7 +13588,7 @@ static void SpawnSPBSpeedLines(mobj_t *actor) MT_FASTLINE); P_SetTarget(&fast->target, actor); - fast->angle = K_MomentumAngle(actor); + P_InitAngle(fast, K_MomentumAngle(actor)); fast->color = SKINCOLOR_RED; fast->colorized = true; K_MatchGenericExtraFlags(fast, actor); @@ -14289,7 +14290,7 @@ void A_RandomShadowFrame(mobj_t *actor) P_SetScale(fake, FRACUNIT*3/2); fake->scale = FRACUNIT*3/2; fake->destscale = FRACUNIT*3/2; - fake->angle = actor->angle; + P_InitAngle(fake, actor->angle); fake->tics = -1; actor->renderflags |= RF_DONTDRAW; actor->extravalue1 = 1; @@ -14677,6 +14678,8 @@ void A_FlameShieldPaper(mobj_t *actor) paper->frame |= framea; } + P_InitAngle(paper, paper->angle); + paper->extravalue1 = i; } } diff --git a/src/p_inter.c b/src/p_inter.c index 4dafa91c2..596bef294 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1184,7 +1184,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget mo->angle = FixedAngle((P_RandomKey(36)*10)<angle = mo->angle; + P_InitAngle(mo2, mo->angle); P_SetMobjState(mo2, S_BOSSSEBH2); if (++i == 2) // we've already removed 2 of these, let's stop now @@ -1251,6 +1251,12 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget kart->eflags |= MFE_DAMAGEHITLAG; P_SetObjectMomZ(kart, 6*FRACUNIT, false); kart->extravalue1 = target->player->kartweight; + + // Copy interp data + kart->old_angle = target->old_angle; + kart->old_x = target->old_x; + kart->old_y = target->old_y; + kart->old_z = target->old_z; } if (source && !P_MobjWasRemoved(source)) @@ -1322,7 +1328,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget for (i = 0; i < 2; i++) { mobj_t *blast = P_SpawnMobjFromMobj(target, 0, 0, target->info->height >> 1, MT_BATTLEBUMPER_BLAST); - blast->angle = angle + i*ANGLE_90; + P_InitAngle(blast, angle + i*ANGLE_90); P_SetScale(blast, 2*blast->scale/3); blast->destscale = 2*blast->scale; } @@ -1547,7 +1553,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_SPIKE);\ P_SetMobjState(chunk, target->info->xdeathstate);\ chunk->health = 0;\ - chunk->angle = angtweak;\ + P_InitAngle(chunk, angtweak);\ P_UnsetThingPosition(chunk);\ chunk->flags = MF_NOCLIP;\ chunk->x += xmov;\ @@ -1569,7 +1575,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_SPIKE); P_SetMobjState(chunk, target->info->deathstate); chunk->health = 0; - chunk->angle = ang + ANGLE_180; + P_InitAngle(chunk, ang + ANGLE_180); P_UnsetThingPosition(chunk); chunk->flags = MF_NOCLIP; chunk->x -= xoffs; @@ -1615,7 +1621,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_WALLSPIKE);\ P_SetMobjState(chunk, target->info->xdeathstate);\ chunk->health = 0;\ - chunk->angle = target->angle;\ + P_InitAngle(chunk, target->angle);\ P_UnsetThingPosition(chunk);\ chunk->flags = MF_NOCLIP;\ chunk->x += xmov - forwardxoffs;\ @@ -1641,7 +1647,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget P_SetMobjState(chunk, target->info->deathstate); chunk->health = 0; - chunk->angle = target->angle; + P_InitAngle(chunk, target->angle); P_UnsetThingPosition(chunk); chunk->flags = MF_NOCLIP; chunk->x += forwardxoffs - xoffs; @@ -1766,7 +1772,7 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, boom = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_FZEROBOOM); boom->scale = player->mo->scale; - boom->angle = player->mo->angle; + P_InitAngle(boom, player->mo->angle); P_SetTarget(&boom->target, player->mo); } diff --git a/src/p_local.h b/src/p_local.h index bfe8d9e08..0b71554da 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -410,6 +410,9 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff); boolean P_Move(mobj_t *actor, fixed_t speed); boolean P_SetOrigin(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z); boolean P_MoveOrigin(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z); +void P_InitAngle(mobj_t *thing, angle_t newValue); +void P_InitPitch(mobj_t *thing, angle_t newValue); +void P_InitRoll(mobj_t *thing, angle_t newValue); void P_SlideMove(mobj_t *mo); void P_BouncePlayerMove(mobj_t *mo); void P_BounceMove(mobj_t *mo); @@ -524,8 +527,8 @@ boolean P_CheckMissileSpawn(mobj_t *th); void P_Thrust(mobj_t *mo, angle_t angle, fixed_t move); void P_ExplodeMissile(mobj_t *mo); void P_CheckGravity(mobj_t *mo, boolean affect); -void P_SetPitchRollFromSlope(mobj_t *mo, pslope_t *slope); -void P_SetPitchRoll(mobj_t *mo, angle_t pitch, angle_t yaw); +void P_InitPitchRollFromSlope(mobj_t *mo, pslope_t *slope); +void P_InitPitchRoll(mobj_t *mo, angle_t pitch, angle_t yaw); fixed_t P_ScaleFromMap(fixed_t n, fixed_t scale); fixed_t P_GetMobjHead(const mobj_t *); fixed_t P_GetMobjFeet(const mobj_t *); diff --git a/src/p_map.c b/src/p_map.c index 28487e2e3..883eac52a 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -155,6 +155,30 @@ boolean P_MoveOrigin(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z) return P_TeleportMove(thing, x, y, z); } +// +// P_InitAngle - Change an object's angle, including interp values. +// +void P_InitAngle(mobj_t *thing, angle_t newValue) +{ + thing->angle = thing->old_angle = newValue; +} + +// +// P_InitPitch - Change an object's pitch, including interp values. +// +void P_InitPitch(mobj_t *thing, angle_t newValue) +{ + thing->pitch = thing->old_pitch = newValue; +} + +// +// P_InitRoll - Change an object's roll, including interp values. +// +void P_InitRoll(mobj_t *thing, angle_t newValue) +{ + thing->roll = thing->old_roll = newValue; +} + // ========================================================================= // MOVEMENT ITERATOR FUNCTIONS // ========================================================================= @@ -408,7 +432,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) mobj_t *grease; grease = P_SpawnMobj(object->x, object->y, object->z, MT_TIREGREASE); P_SetTarget(&grease->target, object); - grease->angle = K_MomentumAngle(object); + P_InitAngle(grease, K_MomentumAngle(object)); grease->extravalue1 = i; } @@ -2662,7 +2686,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) if (thing->momz <= 0) { thing->standingslope = tmfloorslope; - P_SetPitchRollFromSlope(thing, thing->standingslope); + P_InitPitchRollFromSlope(thing, thing->standingslope); if (thing->momz == 0 && thing->player && !startingonground) P_PlayerHitFloor(thing->player, true); @@ -2675,7 +2699,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) if (thing->momz >= 0) { thing->standingslope = tmceilingslope; - P_SetPitchRollFromSlope(thing, thing->standingslope); + P_InitPitchRollFromSlope(thing, thing->standingslope); if (thing->momz == 0 && thing->player && !startingonground) P_PlayerHitFloor(thing->player, true); diff --git a/src/p_mobj.c b/src/p_mobj.c index 2981f6110..7bba60fa3 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1231,9 +1231,9 @@ void P_CheckGravity(mobj_t *mo, boolean affect) } // -// P_SetPitchRollFromSlope +// P_InitPitchRollFromSlope // -void P_SetPitchRollFromSlope(mobj_t *mo, pslope_t *slope) +void P_InitPitchRollFromSlope(mobj_t *mo, pslope_t *slope) { if (slope) { @@ -1251,9 +1251,9 @@ void P_SetPitchRollFromSlope(mobj_t *mo, pslope_t *slope) } // -// P_SetPitchRoll +// P_InitPitchRoll // -void P_SetPitchRoll(mobj_t *mo, angle_t pitch, angle_t yaw) +void P_InitPitchRoll(mobj_t *mo, angle_t pitch, angle_t yaw) { pitch = InvAngle(pitch); yaw >>= ANGLETOFINESHIFT; @@ -1671,7 +1671,7 @@ void P_XYMovement(mobj_t *mo) { mo->momz = transfermomz; mo->standingslope = NULL; - P_SetPitchRoll(mo, ANGLE_90, + P_InitPitchRoll(mo, ANGLE_90, transferslope->xydirection + (transferslope->zangle & ANGLE_180)); @@ -1753,7 +1753,7 @@ void P_XYMovement(mobj_t *mo) // Now compare the Zs of the different quantizations if (oldangle-newangle > ANG30 && oldangle-newangle < ANGLE_180) { // Allow for a bit of sticking - this value can be adjusted later mo->standingslope = oldslope; - P_SetPitchRollFromSlope(mo, mo->standingslope); + P_InitPitchRollFromSlope(mo, mo->standingslope); P_SlopeLaunch(mo); //CONS_Printf("launched off of slope - "); @@ -2277,7 +2277,7 @@ boolean P_ZMovement(mobj_t *mo) if (((mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope) && (mo->type != MT_STEAM)) { mo->standingslope = (mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope; - P_SetPitchRollFromSlope(mo, mo->standingslope); + P_InitPitchRollFromSlope(mo, mo->standingslope); P_ReverseQuantizeMomentumToSlope(&mom, mo->standingslope); } @@ -2383,7 +2383,7 @@ boolean P_ZMovement(mobj_t *mo) MT_KART_TIRE ); - tire->angle = mo->angle; + P_InitAngle(tire, mo->angle); tire->fuse = 3*TICRATE; P_InstaThrust(tire, tireAngle, 4 * mo->scale); P_SetObjectMomZ(tire, 4*FRACUNIT, false); @@ -2403,7 +2403,7 @@ boolean P_ZMovement(mobj_t *mo) MT_KART_TIRE ); - tire->angle = mo->angle; + P_InitAngle(tire, mo->angle); tire->fuse = 3*TICRATE; P_InstaThrust(tire, tireAngle, 4 * mo->scale); P_SetObjectMomZ(tire, 4*FRACUNIT, false); @@ -4050,7 +4050,7 @@ static void P_SpawnItemCapsuleParts(mobj_t *mobj) part = part->hnext; P_SetTarget(&part->target, mobj); P_SetMobjState(part, buttState); - part->angle = i * ANG_CAPSULE; + P_InitAngle(part, i * ANG_CAPSULE); part->movedir = spin; // rotation speed part->movefactor = 0; // z offset part->extravalue1 = buttScale; // relative scale @@ -4061,7 +4061,7 @@ static void P_SpawnItemCapsuleParts(mobj_t *mobj) part = part->hnext; P_SetTarget(&part->target, mobj); P_SetMobjState(part, S_ITEMCAPSULE_TOP_SIDE); - part->angle = i * ANG_CAPSULE; + P_InitAngle(part, i * ANG_CAPSULE); part->movedir = spin; // rotation speed part->movefactor = mobj->info->height - part->info->height; // z offset } @@ -4386,7 +4386,7 @@ void P_SpawnParaloop(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 numb mobj->z -= mobj->height>>1; // change angle - mobj->angle = R_PointToAngle2(mobj->x, mobj->y, x, y); + P_InitAngle(mobj, R_PointToAngle2(mobj->x, mobj->y, x, y)); // change slope dist = P_AproxDistance(P_AproxDistance(x - mobj->x, y - mobj->y), z - mobj->z); @@ -5069,7 +5069,7 @@ static void P_FlameJetSceneryThink(mobj_t *mobj) flame = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_FLAMEJETFLAME); P_SetMobjState(flame, S_FLAMEJETFLAME4); - flame->angle = mobj->angle; + P_InitAngle(flame, mobj->angle); if (mobj->flags2 & MF2_AMBUSH) // Wave up and down instead of side-to-side flame->momz = mobj->fuse << (FRACBITS - 2); @@ -5530,7 +5530,7 @@ static void P_MobjSceneryThink(mobj_t *mobj) { mobj_t *blast = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_BATTLEBUMPER_BLAST); - blast->angle = R_PointToAngle2(0, 0, mobj->momx, mobj->momy) + ANGLE_45; + P_InitAngle(blast, R_PointToAngle2(0, 0, mobj->momx, mobj->momy) + ANGLE_45); blast->destscale *= 4; if (i & 1) @@ -7399,7 +7399,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) mobj->z + (mobj->height/2) + (P_RandomRange(-20,20) * mobj->scale), MT_FASTLINE); - fast->angle = mobj->angle; + P_InitAngle(fast, mobj->angle); fast->momx = 3*mobj->target->momx/4; fast->momy = 3*mobj->target->momy/4; fast->momz = 3*P_GetMobjZMovement(mobj->target)/4; @@ -7461,7 +7461,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) if (underlayst != S_NULL) { mobj_t *underlay = P_SpawnMobj(mobj->target->x, mobj->target->y, mobj->target->z, MT_FLAMESHIELDUNDERLAY); - underlay->angle = mobj->angle; + P_InitAngle(underlay, mobj->angle); P_SetMobjState(underlay, underlayst); } break; @@ -8839,7 +8839,7 @@ static boolean P_FuseThink(mobj_t *mobj) for (i = 0; i < 5; i++) { mobj_t *debris = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_SMK_ICEBLOCK_DEBRIS); - debris->angle = FixedAngle(P_RandomRange(0,360)<angle, P_RandomRange(3,18)*(FRACUNIT/4)); debris->momz = P_RandomRange(4,8)<angle = mobj->angle + ((mobj->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270);; + P_InitAngle(bigmeatyclaw, mobj->angle + ((mobj->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270)); P_SetTarget(&mobj->tracer, bigmeatyclaw); P_SetTarget(&bigmeatyclaw->tracer, mobj); mobj->reactiontime >>= 1; @@ -9624,7 +9624,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) case MT_BANPYURA: { mobj_t *bigmeatyclaw = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_BANPSPRING); - bigmeatyclaw->angle = mobj->angle + ((mobj->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270);; + P_InitAngle(bigmeatyclaw, mobj->angle + ((mobj->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270)); P_SetTarget(&mobj->tracer, bigmeatyclaw); P_SetTarget(&bigmeatyclaw->tracer, mobj); mobj->reactiontime >>= 1; @@ -9757,7 +9757,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) break; case MT_MINECARTEND: P_SetTarget(&mobj->tracer, P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_MINECARTENDSOLID)); - mobj->tracer->angle = mobj->angle + ANGLE_90; + P_InitAngle(mobj->tracer, mobj->angle + ANGLE_90); break; case MT_TORCHFLOWER: { @@ -9879,7 +9879,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) angle_t ang = i * diff; mobj_t *side = P_SpawnMobj(mobj->x + FINECOSINE((ang>>ANGLETOFINESHIFT) & FINEMASK), mobj->y + FINESINE((ang>>ANGLETOFINESHIFT) & FINEMASK), mobj->z, MT_DAYTONAPINETREE_SIDE); - side->angle = ang; + P_InitAngle(side, ang); side->target = mobj; side->threshold = i; } @@ -9896,7 +9896,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) cur = P_SpawnMobj(mobj->x + FINECOSINE(((mobj->angle*8)>>ANGLETOFINESHIFT) & FINEMASK), mobj->y + FINESINE(((mobj->angle*8)>>ANGLETOFINESHIFT) & FINEMASK), mobj->z, MT_EZZPROPELLER_BLADE); - cur->angle = mobj->angle; + P_InitAngle(cur, mobj->angle); P_SetTarget(&cur->hprev, prev); P_SetTarget(&prev->hnext, cur); @@ -9956,7 +9956,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) cur->threshold = i; P_MoveOrigin(cur, cur->x + ((cur->radius>>FRACBITS) * FINECOSINE((FixedAngle((90*cur->threshold)<>ANGLETOFINESHIFT) & FINEMASK)), cur->y + ((cur->radius>>FRACBITS) * FINESINE((FixedAngle((90*cur->threshold)<>ANGLETOFINESHIFT) & FINEMASK)), cur->z); - cur->angle = ANGLE_90*(cur->threshold+1); + P_InitAngle(cur, ANGLE_90*(cur->threshold+1)); P_SetTarget(&cur->hprev, prev); P_SetTarget(&prev->hnext, cur); @@ -10717,7 +10717,7 @@ void P_SpawnPlayer(INT32 playernum) mobj = P_SpawnMobj(0, 0, 0, MT_PLAYER); (mobj->player = p)->mo = mobj; - mobj->angle = 0; + mobj->angle = mobj->old_angle = 0; // set color translations for player sprites mobj->color = p->skincolor; @@ -10804,6 +10804,12 @@ void P_AfterPlayerSpawn(INT32 playernum) mobj_t *mobj = p->mo; UINT8 i; + // Update interpolation + mobj->old_x = mobj->x; + mobj->old_y = mobj->y; + mobj->old_z = mobj->z; + mobj->old_angle = mobj->angle; + P_SetPlayerAngle(p, mobj->angle); p->viewheight = P_GetPlayerViewHeight(p); @@ -11398,7 +11404,7 @@ static boolean P_SetupMace(mapthing_t *mthing, mobj_t *mobj, boolean *doangle) spawnee->friction = mroll;\ spawnee->movefactor = mwidthset;\ spawnee->movecount = dist;\ - spawnee->angle = myaw;\ + P_InitAngle(spawnee, myaw);\ spawnee->flags |= (MF_NOGRAVITY|mflagsapply);\ spawnee->flags2 |= (mflags2apply|moreflags2);\ spawnee->eflags |= meflagsapply;\ @@ -11597,29 +11603,29 @@ static boolean P_SetupBooster(mapthing_t* mthing, mobj_t* mobj, boolean strong) statenum_t rollerstate = strong ? S_REDBOOSTERROLLER : S_YELLOWBOOSTERROLLER; mobj_t *seg = P_SpawnMobjFromMobj(mobj, 26*x1, 26*y1, 0, MT_BOOSTERSEG); - seg->angle = angle - ANGLE_90; + P_InitAngle(seg, angle - ANGLE_90); P_SetMobjState(seg, facestate); seg = P_SpawnMobjFromMobj(mobj, -26*x1, -26*y1, 0, MT_BOOSTERSEG); - seg->angle = angle + ANGLE_90; + P_InitAngle(seg, angle + ANGLE_90); P_SetMobjState(seg, facestate); seg = P_SpawnMobjFromMobj(mobj, 21*x2, 21*y2, 0, MT_BOOSTERSEG); - seg->angle = angle; + P_InitAngle(seg, angle); P_SetMobjState(seg, leftstate); seg = P_SpawnMobjFromMobj(mobj, -21*x2, -21*y2, 0, MT_BOOSTERSEG); - seg->angle = angle; + P_InitAngle(seg, angle); P_SetMobjState(seg, rightstate); seg = P_SpawnMobjFromMobj(mobj, 13*(x1 + x2), 13*(y1 + y2), 0, MT_BOOSTERROLLER); - seg->angle = angle; + P_InitAngle(seg, angle); P_SetMobjState(seg, rollerstate); seg = P_SpawnMobjFromMobj(mobj, 13*(x1 - x2), 13*(y1 - y2), 0, MT_BOOSTERROLLER); - seg->angle = angle; + P_InitAngle(seg, angle); P_SetMobjState(seg, rollerstate); seg = P_SpawnMobjFromMobj(mobj, -13*(x1 + x2), -13*(y1 + y2), 0, MT_BOOSTERROLLER); - seg->angle = angle; + P_InitAngle(seg, angle); P_SetMobjState(seg, rollerstate); seg = P_SpawnMobjFromMobj(mobj, -13*(x1 - x2), -13*(y1 - y2), 0, MT_BOOSTERROLLER); - seg->angle = angle; + P_InitAngle(seg, angle); P_SetMobjState(seg, rollerstate); return true; @@ -11796,19 +11802,19 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean case MT_THZTREE: { // Spawn the branches angle_t mobjangle = FixedAngle((mthing->angle % 113) << FRACBITS); - P_SpawnMobjFromMobj(mobj, FRACUNIT, 0, 0, MT_THZTREEBRANCH)->angle = mobjangle + ANGLE_22h; - P_SpawnMobjFromMobj(mobj, 0, FRACUNIT, 0, MT_THZTREEBRANCH)->angle = mobjangle + ANGLE_157h; - P_SpawnMobjFromMobj(mobj, -FRACUNIT, 0, 0, MT_THZTREEBRANCH)->angle = mobjangle + ANGLE_270; + P_InitAngle(P_SpawnMobjFromMobj(mobj, FRACUNIT, 0, 0, MT_THZTREEBRANCH), mobjangle + ANGLE_22h); + P_InitAngle(P_SpawnMobjFromMobj(mobj, 0, FRACUNIT, 0, MT_THZTREEBRANCH), mobjangle + ANGLE_157h); + P_InitAngle(P_SpawnMobjFromMobj(mobj, -FRACUNIT, 0, 0, MT_THZTREEBRANCH), mobjangle + ANGLE_270); } break; case MT_CEZPOLE1: case MT_CEZPOLE2: { // Spawn the banner angle_t mobjangle = FixedAngle(mthing->angle << FRACBITS); - P_SpawnMobjFromMobj(mobj, + P_InitAngle(P_SpawnMobjFromMobj(mobj, P_ReturnThrustX(mobj, mobjangle, 4 << FRACBITS), P_ReturnThrustY(mobj, mobjangle, 4 << FRACBITS), - 0, ((mobj->type == MT_CEZPOLE1) ? MT_CEZBANNER1 : MT_CEZBANNER2))->angle = mobjangle + ANGLE_90; + 0, ((mobj->type == MT_CEZPOLE1) ? MT_CEZBANNER1 : MT_CEZBANNER2)), mobjangle + ANGLE_90); } break; case MT_HHZTREE_TOP: @@ -11817,7 +11823,7 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean mobj_t* leaf; #define doleaf(x, y) \ leaf = P_SpawnMobjFromMobj(mobj, x, y, 0, MT_HHZTREE_PART);\ - leaf->angle = mobjangle;\ + P_InitAngle(leaf, mobjangle);\ P_SetMobjState(leaf, leaf->info->seestate);\ mobjangle += ANGLE_90 doleaf(FRACUNIT, 0); @@ -11841,7 +11847,7 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean fixed_t xoffs = FINECOSINE(fa); fixed_t yoffs = FINESINE(fa); mobj_t* leaf = P_SpawnMobjFromMobj(mobj, xoffs, yoffs, 0, MT_BIGFERNLEAF); - leaf->angle = angle; + P_InitAngle(leaf, angle); angle += ANGLE_45; } break; @@ -11951,7 +11957,7 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean mobj->x - P_ReturnThrustX(mobj, mobjangle, baseradius), mobj->y - P_ReturnThrustY(mobj, mobjangle, baseradius), mobj->z, MT_WALLSPIKEBASE); - base->angle = mobjangle + ANGLE_90; + P_InitAngle(base, mobjangle + ANGLE_90); base->destscale = mobj->destscale; P_SetScale(base, mobj->scale); P_SetTarget(&base->target, mobj); @@ -12137,7 +12143,7 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean leaf = P_SpawnMobj(mobj->x + FINECOSINE((mobj->angle>>ANGLETOFINESHIFT) & FINEMASK), mobj->y + FINESINE((mobj->angle>>ANGLETOFINESHIFT) & FINEMASK), top, MT_AAZTREE_LEAF); - leaf->angle = mobj->angle; + P_InitAngle(leaf, mobj->angle); // Small coconut for each leaf P_SpawnMobj(mobj->x + (32 * FINECOSINE((mobj->angle>>ANGLETOFINESHIFT) & FINEMASK)), @@ -12354,7 +12360,9 @@ static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y, return mobj; if (doangle) - mobj->angle = FixedAngle(mthing->angle << FRACBITS); + { + P_InitAngle(mobj, FixedAngle(mthing->angle << FRACBITS)); + } if ((mobj->flags & MF_SPRING) && mobj->info->damage != 0 @@ -12366,8 +12374,8 @@ static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y, mobj->spryoff = FixedMul(mobj->radius, FINESINE(a >> ANGLETOFINESHIFT)); } - mobj->pitch = FixedAngle(mthing->pitch << FRACBITS); - mobj->roll = FixedAngle(mthing->roll << FRACBITS); + P_InitPitch(mobj, FixedAngle(mthing->pitch << FRACBITS)); + P_InitRoll(mobj, FixedAngle(mthing->roll << FRACBITS)); mthing->mobj = mobj; @@ -12774,7 +12782,7 @@ mobj_t *P_SpawnXYZMissile(mobj_t *source, mobj_t *dest, mobjtype_t type, P_SetTarget(&th->target, source); // where it came from an = R_PointToAngle2(x, y, dest->x, dest->y); - th->angle = an; + P_InitAngle(th, an); an >>= ANGLETOFINESHIFT; th->momx = FixedMul(speed, FINECOSINE(an)); th->momy = FixedMul(speed, FINESINE(an)); @@ -12836,7 +12844,7 @@ mobj_t *P_SpawnAlteredDirectionMissile(mobj_t *source, mobjtype_t type, fixed_t P_SetTarget(&th->target, source->target); // where it came from an = R_PointToAngle2(0, 0, source->momx, source->momy) + (ANG1*shiftingAngle); - th->angle = an; + P_InitAngle(th, an); an >>= ANGLETOFINESHIFT; th->momx = FixedMul(speed, FINECOSINE(an)); th->momy = FixedMul(speed, FINESINE(an)); @@ -12901,7 +12909,7 @@ mobj_t *P_SpawnPointMissile(mobj_t *source, fixed_t xa, fixed_t ya, fixed_t za, P_SetTarget(&th->target, source); // where it came from an = R_PointToAngle2(x, y, xa, ya); - th->angle = an; + P_InitAngle(th, an); an >>= ANGLETOFINESHIFT; th->momx = FixedMul(speed, FINECOSINE(an)); th->momy = FixedMul(speed, FINESINE(an)); @@ -12980,7 +12988,7 @@ mobj_t *P_SpawnMissile(mobj_t *source, mobj_t *dest, mobjtype_t type) else an = R_PointToAngle2(source->x, source->y, dest->x, dest->y); - th->angle = an; + P_InitAngle(th, an); an >>= ANGLETOFINESHIFT; th->momx = FixedMul(speed, FINECOSINE(an)); th->momy = FixedMul(speed, FINESINE(an)); @@ -13068,7 +13076,7 @@ mobj_t *P_SPMAngle(mobj_t *source, mobjtype_t type, angle_t angle, UINT8 allowai speed = th->info->speed; - th->angle = an; + P_InitAngle(th, an); th->momx = FixedMul(speed, FINECOSINE(an>>ANGLETOFINESHIFT)); th->momy = FixedMul(speed, FINESINE(an>>ANGLETOFINESHIFT)); diff --git a/src/p_saveg.c b/src/p_saveg.c index 39a4fec28..357dbb0aa 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -2885,19 +2885,19 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker) mobj->info = &mobjinfo[mobj->type]; if (diff & MD_POS) { - mobj->x = READFIXED(save_p); - mobj->y = READFIXED(save_p); - mobj->angle = READANGLE(save_p); - mobj->pitch = READANGLE(save_p); - mobj->roll = READANGLE(save_p); + mobj->x = mobj->old_x = READFIXED(save_p); + mobj->y = mobj->old_y = READFIXED(save_p); + mobj->angle = mobj->old_angle = READANGLE(save_p); + mobj->pitch = mobj->old_pitch = READANGLE(save_p); + mobj->roll = mobj->old_roll = READANGLE(save_p); } else { - mobj->x = mobj->spawnpoint->x << FRACBITS; - mobj->y = mobj->spawnpoint->y << FRACBITS; - mobj->angle = FixedAngle(mobj->spawnpoint->angle*FRACUNIT); - mobj->pitch = FixedAngle(mobj->spawnpoint->pitch*FRACUNIT); - mobj->roll = FixedAngle(mobj->spawnpoint->roll*FRACUNIT); + mobj->x = mobj->old_x = mobj->spawnpoint->x << FRACBITS; + mobj->y = mobj->old_y = mobj->spawnpoint->y << FRACBITS; + mobj->angle = mobj->old_angle = FixedAngle(mobj->spawnpoint->angle*FRACUNIT); + mobj->pitch = mobj->old_pitch = FixedAngle(mobj->spawnpoint->pitch*FRACUNIT); + mobj->roll = mobj->old_roll = FixedAngle(mobj->spawnpoint->roll*FRACUNIT); } if (diff & MD_MOM) { diff --git a/src/p_slopes.c b/src/p_slopes.c index 1bde8f4ee..b60e5e348 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -898,7 +898,7 @@ void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope) if (P_MobjFlip(thing)*(thing->momz) < 0) // falling, land on slope { thing->standingslope = slope; - P_SetPitchRollFromSlope(thing, slope); + P_InitPitchRollFromSlope(thing, slope); thing->momz = -P_MobjFlip(thing); } return; @@ -914,7 +914,7 @@ void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope) thing->momx = mom.x; thing->momy = mom.y; thing->standingslope = slope; - P_SetPitchRollFromSlope(thing, slope); + P_InitPitchRollFromSlope(thing, slope); thing->momz = -P_MobjFlip(thing); } } diff --git a/src/p_spec.c b/src/p_spec.c index caf4704d2..bf7784cd3 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -3633,7 +3633,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) if (mobj) { if (line->flags & ML_EFFECT1) - mobj->angle = R_PointToAngle2(line->v1->x, line->v1->y, line->v2->x, line->v2->y); + P_InitAngle(mobj, R_PointToAngle2(line->v1->x, line->v1->y, line->v2->x, line->v2->y)); CONS_Debug(DBG_GAMELOGIC, "Linedef Type %d - Spawn Object: %d spawned at (%d, %d, %d)\n", line->special, mobj->type, mobj->x>>FRACBITS, mobj->y>>FRACBITS, mobj->z>>FRACBITS); //TODO: Convert mobj->type to a string somehow. } else @@ -3928,7 +3928,7 @@ void P_SetupSignExit(player_t *player) if (player->mo && !P_MobjWasRemoved(player->mo)) { thing = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->floorz, MT_SIGN); - thing->angle = player->mo->angle; + P_InitAngle(thing, player->mo->angle); P_SetupSignObject(thing, player->mo, true); // Use :youfuckedup: sign face } } diff --git a/src/p_telept.c b/src/p_telept.c index 3e2917092..434994ce6 100644 --- a/src/p_telept.c +++ b/src/p_telept.c @@ -97,7 +97,7 @@ void P_MixUp(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, P_FlashPal(thing->player, PAL_MIXUP, 10); } - thing->angle = angle; + P_InitAngle(thing, angle); thing->momx = thing->momy = thing->momz = 0; @@ -171,7 +171,7 @@ boolean P_Teleport(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle P_FlashPal(thing->player, PAL_MIXUP, 10); } - thing->angle = angle; + P_InitAngle(thing, angle); return true; } diff --git a/src/p_user.c b/src/p_user.c index a696ed591..1a78e8b62 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -379,7 +379,7 @@ void P_GiveFinishFlags(player_t *player) fixed_t xoffs = FINECOSINE(fa); fixed_t yoffs = FINESINE(fa); mobj_t* flag = P_SpawnMobjFromMobj(player->mo, xoffs, yoffs, 0, MT_FINISHFLAG); - flag->angle = angle; + P_InitAngle(flag, angle); angle += FixedAngle(120*FRACUNIT); P_SetTarget(&flag->target, player->mo); @@ -2176,7 +2176,7 @@ void P_MovePlayer(player_t *player) if (trailScale > 0) { const angle_t forwardangle = K_MomentumAngle(player->mo); - const fixed_t playerVisualRadius = player->mo->radius + 8*FRACUNIT; + const fixed_t playerVisualRadius = player->mo->radius + (8 * player->mo->scale); const size_t numFrames = S_WATERTRAIL8 - S_WATERTRAIL1; const statenum_t curOverlayFrame = S_WATERTRAIL1 + (leveltime % numFrames); const statenum_t curUnderlayFrame = S_WATERTRAILUNDERLAY1 + (leveltime % numFrames); @@ -2197,7 +2197,7 @@ void P_MovePlayer(player_t *player) // underlay water = P_SpawnMobj(x1, y1, ((player->mo->eflags & MFE_VERTICALFLIP) ? player->mo->waterbottom - FixedMul(mobjinfo[MT_WATERTRAILUNDERLAY].height, player->mo->scale) : player->mo->watertop), MT_WATERTRAILUNDERLAY); - water->angle = forwardangle - ANGLE_180 - ANGLE_22h; + P_InitAngle(water, forwardangle - ANGLE_180 - ANGLE_22h); water->destscale = trailScale; water->momx = player->mo->momx; water->momy = player->mo->momy; @@ -2208,7 +2208,7 @@ void P_MovePlayer(player_t *player) // overlay water = P_SpawnMobj(x1, y1, ((player->mo->eflags & MFE_VERTICALFLIP) ? player->mo->waterbottom - FixedMul(mobjinfo[MT_WATERTRAIL].height, player->mo->scale) : player->mo->watertop), MT_WATERTRAIL); - water->angle = forwardangle - ANGLE_180 - ANGLE_22h; + P_InitAngle(water, forwardangle - ANGLE_180 - ANGLE_22h); water->destscale = trailScale; water->momx = player->mo->momx; water->momy = player->mo->momy; @@ -2220,7 +2220,7 @@ void P_MovePlayer(player_t *player) // Underlay water = P_SpawnMobj(x2, y2, ((player->mo->eflags & MFE_VERTICALFLIP) ? player->mo->waterbottom - FixedMul(mobjinfo[MT_WATERTRAILUNDERLAY].height, player->mo->scale) : player->mo->watertop), MT_WATERTRAILUNDERLAY); - water->angle = forwardangle - ANGLE_180 + ANGLE_22h; + P_InitAngle(water, forwardangle - ANGLE_180 + ANGLE_22h); water->destscale = trailScale; water->momx = player->mo->momx; water->momy = player->mo->momy; @@ -2231,7 +2231,7 @@ void P_MovePlayer(player_t *player) // Overlay water = P_SpawnMobj(x2, y2, ((player->mo->eflags & MFE_VERTICALFLIP) ? player->mo->waterbottom - FixedMul(mobjinfo[MT_WATERTRAIL].height, player->mo->scale) : player->mo->watertop), MT_WATERTRAIL); - water->angle = forwardangle - ANGLE_180 + ANGLE_22h; + P_InitAngle(water, forwardangle - ANGLE_180 + ANGLE_22h); water->destscale = trailScale; water->momx = player->mo->momx; water->momy = player->mo->momy; @@ -3991,7 +3991,7 @@ static void P_HandleFollower(player_t *player) P_SetTarget(&player->follower, P_SpawnMobj(sx, sy, sz, MT_FOLLOWER)); P_SetFollowerState(player->follower, fl.idlestate); P_SetTarget(&player->follower->target, player->mo); // we need that to know when we need to disappear - player->follower->angle = player->mo->angle; + P_InitAngle(player->follower, player->mo->angle); // This is safe to only spawn it here, the follower is removed then respawned when switched. if (bubble) @@ -4054,10 +4054,8 @@ static void P_HandleFollower(player_t *player) if (player->pflags & PF_NOCONTEST) player->follower->renderflags |= RF_DONTDRAW; - if (player->speed && (player->follower->momx || player->follower->momy)) - player->follower->angle = K_MomentumAngle(player->follower); - // if we're moving let's make the angle the direction we're moving towards. This is to avoid drifting / reverse looking awkward. - // Make sure the follower itself is also moving however, otherwise we'll be facing angle 0 + // if we're moving let's make the angle the direction we're moving towards. This is to avoid drifting / reverse looking awkward. + player->follower->angle = K_MomentumAngle(player->follower); // Finally, if the follower has bubbles, move them, set their scale, etc.... // This is what I meant earlier by it being easier, now we can just use this weird lil loop to get the job done! From b9cc482c53c1fa3d29fda23d74f3a1c6b6fe8f94 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Dec 2021 13:00:12 -0500 Subject: [PATCH 081/379] Rename PitchRoll stuff back I did a find+replace because I realized the functions I added were named too similar to other ones & to make the purpose more obvious ... but it ended up changing them too anyway! Gah! --- src/k_kart.c | 2 +- src/p_local.h | 4 ++-- src/p_map.c | 4 ++-- src/p_mobj.c | 14 +++++++------- src/p_slopes.c | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 4bc39f590..2dacce0ef 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5877,7 +5877,7 @@ static void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z } //mobj->standingslope = slope; - P_InitPitchRollFromSlope(mobj, slope); + P_SetPitchRollFromSlope(mobj, slope); } // Move the hnext chain! diff --git a/src/p_local.h b/src/p_local.h index 0b71554da..51a43b639 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -527,8 +527,8 @@ boolean P_CheckMissileSpawn(mobj_t *th); void P_Thrust(mobj_t *mo, angle_t angle, fixed_t move); void P_ExplodeMissile(mobj_t *mo); void P_CheckGravity(mobj_t *mo, boolean affect); -void P_InitPitchRollFromSlope(mobj_t *mo, pslope_t *slope); -void P_InitPitchRoll(mobj_t *mo, angle_t pitch, angle_t yaw); +void P_SetPitchRollFromSlope(mobj_t *mo, pslope_t *slope); +void P_SetPitchRoll(mobj_t *mo, angle_t pitch, angle_t yaw); fixed_t P_ScaleFromMap(fixed_t n, fixed_t scale); fixed_t P_GetMobjHead(const mobj_t *); fixed_t P_GetMobjFeet(const mobj_t *); diff --git a/src/p_map.c b/src/p_map.c index 883eac52a..7899a0964 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -2686,7 +2686,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) if (thing->momz <= 0) { thing->standingslope = tmfloorslope; - P_InitPitchRollFromSlope(thing, thing->standingslope); + P_SetPitchRollFromSlope(thing, thing->standingslope); if (thing->momz == 0 && thing->player && !startingonground) P_PlayerHitFloor(thing->player, true); @@ -2699,7 +2699,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) if (thing->momz >= 0) { thing->standingslope = tmceilingslope; - P_InitPitchRollFromSlope(thing, thing->standingslope); + P_SetPitchRollFromSlope(thing, thing->standingslope); if (thing->momz == 0 && thing->player && !startingonground) P_PlayerHitFloor(thing->player, true); diff --git a/src/p_mobj.c b/src/p_mobj.c index 7bba60fa3..c8b1ca4d0 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1231,9 +1231,9 @@ void P_CheckGravity(mobj_t *mo, boolean affect) } // -// P_InitPitchRollFromSlope +// P_SetPitchRollFromSlope // -void P_InitPitchRollFromSlope(mobj_t *mo, pslope_t *slope) +void P_SetPitchRollFromSlope(mobj_t *mo, pslope_t *slope) { if (slope) { @@ -1251,9 +1251,9 @@ void P_InitPitchRollFromSlope(mobj_t *mo, pslope_t *slope) } // -// P_InitPitchRoll +// P_SetPitchRoll // -void P_InitPitchRoll(mobj_t *mo, angle_t pitch, angle_t yaw) +void P_SetPitchRoll(mobj_t *mo, angle_t pitch, angle_t yaw) { pitch = InvAngle(pitch); yaw >>= ANGLETOFINESHIFT; @@ -1671,7 +1671,7 @@ void P_XYMovement(mobj_t *mo) { mo->momz = transfermomz; mo->standingslope = NULL; - P_InitPitchRoll(mo, ANGLE_90, + P_SetPitchRoll(mo, ANGLE_90, transferslope->xydirection + (transferslope->zangle & ANGLE_180)); @@ -1753,7 +1753,7 @@ void P_XYMovement(mobj_t *mo) // Now compare the Zs of the different quantizations if (oldangle-newangle > ANG30 && oldangle-newangle < ANGLE_180) { // Allow for a bit of sticking - this value can be adjusted later mo->standingslope = oldslope; - P_InitPitchRollFromSlope(mo, mo->standingslope); + P_SetPitchRollFromSlope(mo, mo->standingslope); P_SlopeLaunch(mo); //CONS_Printf("launched off of slope - "); @@ -2277,7 +2277,7 @@ boolean P_ZMovement(mobj_t *mo) if (((mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope) && (mo->type != MT_STEAM)) { mo->standingslope = (mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope; - P_InitPitchRollFromSlope(mo, mo->standingslope); + P_SetPitchRollFromSlope(mo, mo->standingslope); P_ReverseQuantizeMomentumToSlope(&mom, mo->standingslope); } diff --git a/src/p_slopes.c b/src/p_slopes.c index b60e5e348..1bde8f4ee 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -898,7 +898,7 @@ void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope) if (P_MobjFlip(thing)*(thing->momz) < 0) // falling, land on slope { thing->standingslope = slope; - P_InitPitchRollFromSlope(thing, slope); + P_SetPitchRollFromSlope(thing, slope); thing->momz = -P_MobjFlip(thing); } return; @@ -914,7 +914,7 @@ void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope) thing->momx = mom.x; thing->momy = mom.y; thing->standingslope = slope; - P_InitPitchRollFromSlope(thing, slope); + P_SetPitchRollFromSlope(thing, slope); thing->momz = -P_MobjFlip(thing); } } From 74bac4028b8e0e692f6af17ad3f66b0fe8e398b1 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Dec 2021 13:17:57 -0500 Subject: [PATCH 082/379] Hack to fix drift spark explosion while keeping its interpolation --- src/p_mobj.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/p_mobj.c b/src/p_mobj.c index c8b1ca4d0..269d99652 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6815,12 +6815,15 @@ static boolean P_MobjRegularThink(mobj_t *mobj) if (( mobj->fuse & 1 )) { nudge = 4*mobj->target->radius; + /* unrotate interp angle */ + mobj->old_angle -= ANGLE_90; } else { nudge = 2*mobj->target->radius; /* rotate the papersprite frames to see the flat angle */ mobj->angle += ANGLE_90; + mobj->old_angle += ANGLE_90; } P_MoveOrigin(mobj, From 843f89cb76cd5ec27794bf304a0ccb923defef3a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Dec 2021 22:52:38 -0500 Subject: [PATCH 083/379] Fix jawz reticule in uncapped --- src/k_kart.c | 3 +++ src/p_enemy.c | 3 +++ src/p_mobj.c | 3 +++ 3 files changed, 9 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index 2dacce0ef..28d351ba6 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7161,6 +7161,9 @@ void K_KartPlayerAfterThink(player_t *player) } ret = P_SpawnMobj(targ->mo->x, targ->mo->y, targ->mo->z, MT_PLAYERRETICULE); + ret->old_x = targ->mo->old_x; + ret->old_y = targ->mo->old_y; + ret->old_z = targ->mo->old_z; P_SetTarget(&ret->target, targ->mo); ret->frame |= ((leveltime % 10) / 2); ret->tics = 1; diff --git a/src/p_enemy.c b/src/p_enemy.c index 0eccc5f55..d9b1fd840 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -13400,6 +13400,9 @@ void A_JawzChase(mobj_t *actor) } ret = P_SpawnMobj(actor->tracer->x, actor->tracer->y, actor->tracer->z, MT_PLAYERRETICULE); + ret->old_x = actor->tracer->old_x; + ret->old_y = actor->tracer->old_y; + ret->old_z = actor->tracer->old_z; P_SetTarget(&ret->target, actor->tracer); ret->frame |= ((leveltime % 10) / 2) + 5; ret->color = actor->cvmem; diff --git a/src/p_mobj.c b/src/p_mobj.c index 269d99652..f09fb5b60 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -7018,6 +7018,9 @@ static boolean P_MobjRegularThink(mobj_t *mobj) return false; } P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z); + mobj->old_x = mobj->target->old_x; + mobj->old_y = mobj->target->old_y; + mobj->old_z = mobj->target->old_z; break; case MT_INSTASHIELDB: mobj->renderflags ^= RF_DONTDRAW; From f04520f368cccce50d00228fd74e1d26878f1d03 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Dec 2021 23:07:12 -0500 Subject: [PATCH 084/379] Apply the same fix for instashield overlay --- src/k_kart.c | 6 ++++++ src/p_mobj.c | 3 +++ 2 files changed, 9 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index 28d351ba6..d13ee39c7 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3136,9 +3136,15 @@ void K_DoInstashield(player_t *player) S_StartSound(player->mo, sfx_cdpcm9); layera = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INSTASHIELDA); + layera->old_x = player->mo->old_x; + layera->old_y = player->mo->old_y; + layera->old_z = player->mo->old_z; P_SetTarget(&layera->target, player->mo); layerb = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INSTASHIELDB); + layerb->old_x = player->mo->old_x; + layerb->old_y = player->mo->old_y; + layerb->old_z = player->mo->old_z; P_SetTarget(&layerb->target, player->mo); } diff --git a/src/p_mobj.c b/src/p_mobj.c index f09fb5b60..886c0eaa1 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -7033,6 +7033,9 @@ static boolean P_MobjRegularThink(mobj_t *mobj) return false; } P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z); + mobj->old_x = mobj->target->old_x; + mobj->old_y = mobj->target->old_y; + mobj->old_z = mobj->target->old_z; K_MatchGenericExtraFlags(mobj, mobj->target); break; case MT_BATTLEPOINT: From 6b84557b781b13c02c13db227eea532cd4b9a982 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Dec 2021 23:17:28 -0500 Subject: [PATCH 085/379] Make drop shadows slightly better in uncapped They still jitter uphill --- src/hardware/hw_main.c | 4 ++-- src/r_things.c | 45 +++++++++++++++++++++++++++++------------- src/r_things.h | 2 +- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 63e46e9a2..aa55544a5 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -3668,7 +3668,7 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) interpy += thing->spryoff; interpz += thing->sprzoff; - groundz = R_GetShadowZ(thing, &groundslope); + groundz = R_GetShadowZ(thing, &groundslope, interpx, interpy, interpz); gpatch = (patch_t *)W_CachePatchName("DSHADOW", PU_SPRITE); if (!(gpatch && ((GLPatch_t *)gpatch->hardware)->mipmap->format)) return; @@ -5295,7 +5295,7 @@ static void HWR_ProjectSprite(mobj_t *thing) if (caster && !P_MobjWasRemoved(caster)) { - fixed_t groundz = R_GetShadowZ(thing, NULL); + fixed_t groundz = R_GetShadowZ(thing, NULL, interpx, interpy, interpz); fixed_t floordiff = abs(((thing->eflags & MFE_VERTICALFLIP) ? caster->height : 0) + caster->z - groundz); shadowheight = FIXED_TO_FLOAT(floordiff); diff --git a/src/r_things.c b/src/r_things.c index 5fec86a7f..6130815e7 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1148,7 +1148,9 @@ static void R_SplitSprite(vissprite_t *sprite) // Get the first visible floor below the object for shadows // shadowslope is filled with the floor's slope, if provided // -fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope) +fixed_t R_GetShadowZ( + mobj_t *thing, pslope_t **shadowslope, + fixed_t interpx, fixed_t interpy, fixed_t interpz) { boolean isflipped = thing->eflags & MFE_VERTICALFLIP; fixed_t z, groundz = isflipped ? INT32_MAX : INT32_MIN; @@ -1156,7 +1158,8 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope) msecnode_t *node; sector_t *sector; ffloor_t *rover; -#define CHECKZ (isflipped ? z > thing->z+thing->height/2 && z < groundz : z < thing->z+thing->height/2 && z > groundz) + +#define CHECKZ (isflipped ? z > interpz+thing->height/2 && z < groundz : z < interpz+thing->height/2 && z > groundz) for (node = thing->touching_sectorlist; node; node = node->m_sectorlist_next) { @@ -1167,7 +1170,7 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope) if (sector->heightsec != -1) z = isflipped ? sectors[sector->heightsec].ceilingheight : sectors[sector->heightsec].floorheight; else - z = isflipped ? P_GetSectorCeilingZAt(sector, thing->x, thing->y) : P_GetSectorFloorZAt(sector, thing->x, thing->y); + z = isflipped ? P_GetSectorCeilingZAt(sector, interpx, interpy) : P_GetSectorFloorZAt(sector, interpx, interpy); if CHECKZ { @@ -1181,7 +1184,7 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope) if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERPLANES) || (rover->alpha < 90 && !(rover->flags & FF_SWIMMABLE))) continue; - z = isflipped ? P_GetFFloorBottomZAt(rover, thing->x, thing->y) : P_GetFFloorTopZAt(rover, thing->x, thing->y); + z = isflipped ? P_GetFFloorBottomZAt(rover, interpx, interpy) : P_GetFFloorTopZAt(rover, interpx, interpy); if CHECKZ { @@ -1268,11 +1271,12 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope) static void R_SkewShadowSprite( mobj_t *thing, pslope_t *groundslope, fixed_t groundz, INT32 spriteheight, fixed_t scalemul, - fixed_t *shadowyscale, fixed_t *shadowskew) + fixed_t *shadowyscale, fixed_t *shadowskew, + fixed_t interpx, fixed_t interpy) { // haha let's try some dumb stuff fixed_t xslope, zslope; - angle_t sloperelang = (R_PointToAngle(thing->x, thing->y) - groundslope->xydirection) >> ANGLETOFINESHIFT; + angle_t sloperelang = (R_PointToAngle(interpx, interpy) - groundslope->xydirection) >> ANGLETOFINESHIFT; xslope = FixedMul(FINESINE(sloperelang), groundslope->zdelta); zslope = FixedMul(FINECOSINE(sloperelang), groundslope->zdelta); @@ -1288,7 +1292,10 @@ static void R_SkewShadowSprite( *shadowskew = xslope; } -static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, fixed_t tx, fixed_t tz) +static void R_ProjectDropShadow( + mobj_t *thing, vissprite_t *vis, + fixed_t scale, fixed_t tx, fixed_t tz, + fixed_t interpx, fixed_t interpy, fixed_t interpz) { vissprite_t *shadow; patch_t *patch; @@ -1297,7 +1304,7 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, fixed_t groundz; pslope_t *groundslope; - groundz = R_GetShadowZ(thing, &groundslope); + groundz = R_GetShadowZ(thing, &groundslope, interpx, interpy, interpz); if (abs(groundz-viewz)/tz > 4) return; // Prevent stretchy shadows and possible crashes @@ -1311,7 +1318,14 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, shadowskew = 0; if (groundslope) - R_SkewShadowSprite(thing, groundslope, groundz, patch->height, FRACUNIT, &shadowyscale, &shadowskew); + { + R_SkewShadowSprite( + thing, + groundslope, groundz, + patch->height, FRACUNIT, + &shadowyscale, &shadowskew, + interpx, interpy); + } tx -= patch->width * shadowxscale/2; x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS; @@ -1330,8 +1344,8 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, shadow->mobjflags = 0; shadow->sortscale = vis->sortscale; shadow->dispoffset = vis->dispoffset - 5; - shadow->gx = thing->x; - shadow->gy = thing->y; + shadow->gx = interpx; + shadow->gy = interpy; shadow->gzt = groundz + patch->height * shadowyscale / 2; shadow->gz = shadow->gzt - patch->height * shadowyscale; shadow->texturemid = FixedMul(thing->scale, FixedDiv(shadow->gzt - viewz, shadowyscale)); @@ -1901,7 +1915,7 @@ static void R_ProjectSprite(mobj_t *thing) if (shadowdraw || shadoweffects) { - fixed_t groundz = R_GetShadowZ(thing, NULL); + fixed_t groundz = R_GetShadowZ(thing, NULL, interpx, interpy, interpz); boolean isflipped = (thing->eflags & MFE_VERTICALFLIP); if (shadoweffects) @@ -1925,7 +1939,7 @@ static void R_ProjectSprite(mobj_t *thing) if (shadowskew) { - R_SkewShadowSprite(thing, thing->standingslope, groundz, patch->height, shadowscale, &spriteyscale, &sheartan); + R_SkewShadowSprite(thing, thing->standingslope, groundz, patch->height, shadowscale, &spriteyscale, &sheartan, interpx, interpy); gzt = (isflipped ? (thing->z + thing->height) : thing->z) + patch->height * spriteyscale / 2; gz = gzt - patch->height * spriteyscale; @@ -2124,7 +2138,10 @@ static void R_ProjectSprite(mobj_t *thing) R_SplitSprite(vis); if (oldthing->shadowscale && cv_shadow.value) - R_ProjectDropShadow(oldthing, vis, oldthing->shadowscale, basetx, basetz); + { + R_ProjectDropShadow(oldthing, vis, oldthing->shadowscale, basetx, basetz, + interpx, interpy, interpz); + } // Debug ++objectsdrawn; diff --git a/src/r_things.h b/src/r_things.h index 7ff1bd4c7..4e0c8194a 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -59,7 +59,7 @@ void R_DrawFlippedMaskedColumn(column_t *column, column_t *brightmap); extern INT16 negonearray[MAXVIDWIDTH]; extern INT16 screenheightarray[MAXVIDWIDTH]; -fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope); +fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope, fixed_t interpx, fixed_t interpy, fixed_t interpz); //SoM: 6/5/2000: Light sprites correctly! void R_AddSprites(sector_t *sec, INT32 lightlevel); From 0adea2279b01d908c481b37c91e507f7a1635b97 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 25 Dec 2021 02:07:56 -0500 Subject: [PATCH 086/379] Fully fix drop shadows It used the thing's floorz / ceilingz directly -- that wouldn't account for interpolated coordinates. --- src/r_things.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/r_things.c b/src/r_things.c index 6130815e7..c31d295a8 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1152,14 +1152,17 @@ fixed_t R_GetShadowZ( mobj_t *thing, pslope_t **shadowslope, fixed_t interpx, fixed_t interpy, fixed_t interpz) { + fixed_t halfHeight = interpz + (thing->height >> 1); boolean isflipped = thing->eflags & MFE_VERTICALFLIP; + fixed_t floorz = P_GetFloorZ(thing, thing->subsector->sector, interpx, interpy, NULL); + fixed_t ceilingz = P_GetCeilingZ(thing, thing->subsector->sector, interpx, interpy, NULL); fixed_t z, groundz = isflipped ? INT32_MAX : INT32_MIN; pslope_t *slope, *groundslope = NULL; msecnode_t *node; sector_t *sector; ffloor_t *rover; -#define CHECKZ (isflipped ? z > interpz+thing->height/2 && z < groundz : z < interpz+thing->height/2 && z > groundz) +#define CHECKZ (isflipped ? z > halfHeight && z < groundz : z < halfHeight && z > groundz) for (node = thing->touching_sectorlist; node; node = node->m_sectorlist_next) { @@ -1194,10 +1197,10 @@ fixed_t R_GetShadowZ( } } - if (isflipped ? (thing->ceilingz < groundz - (!groundslope ? 0 : FixedMul(abs(groundslope->zdelta), thing->radius*3/2))) - : (thing->floorz > groundz + (!groundslope ? 0 : FixedMul(abs(groundslope->zdelta), thing->radius*3/2)))) + if (isflipped ? (ceilingz < groundz - (!groundslope ? 0 : FixedMul(abs(groundslope->zdelta), thing->radius*3/2))) + : (floorz > groundz + (!groundslope ? 0 : FixedMul(abs(groundslope->zdelta), thing->radius*3/2)))) { - groundz = isflipped ? thing->ceilingz : thing->floorz; + groundz = isflipped ? ceilingz : floorz; groundslope = NULL; } @@ -1208,10 +1211,10 @@ fixed_t R_GetShadowZ( { INT32 xl, xh, yl, yh, bx, by; - xl = (unsigned)(thing->x - thing->radius - bmaporgx)>>MAPBLOCKSHIFT; - xh = (unsigned)(thing->x + thing->radius - bmaporgx)>>MAPBLOCKSHIFT; - yl = (unsigned)(thing->y - thing->radius - bmaporgy)>>MAPBLOCKSHIFT; - yh = (unsigned)(thing->y + thing->radius - bmaporgy)>>MAPBLOCKSHIFT; + xl = (unsigned)(interpx - thing->radius - bmaporgx)>>MAPBLOCKSHIFT; + xh = (unsigned)(interpx + thing->radius - bmaporgx)>>MAPBLOCKSHIFT; + yl = (unsigned)(interpy - thing->radius - bmaporgy)>>MAPBLOCKSHIFT; + yh = (unsigned)(interpy + thing->radius - bmaporgy)>>MAPBLOCKSHIFT; BMBOUNDFIX(xl, xh, yl, yh); @@ -1248,7 +1251,7 @@ fixed_t R_GetShadowZ( // We're inside it! Yess... z = po->lines[0]->backsector->ceilingheight; - if (z < thing->z+thing->height/2 && z > groundz) + if (z < halfHeight && z > groundz) { groundz = z; groundslope = NULL; From d959ce02be87977a1f518ec90067e5646ec54a90 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 25 Dec 2021 03:53:55 -0500 Subject: [PATCH 087/379] Purple spark electricity UNCAPPED --- src/k_kart.c | 3 +++ src/p_mobj.c | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index d13ee39c7..ca32c6f6d 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3975,6 +3975,9 @@ static void K_SpawnDriftElectricity(player_t *player) + P_ReturnThrustY(mo, horizonatalangle, horizontalradius); spark = P_SpawnMobjFromMobj(mo, x, y, 0, MT_DRIFTELECTRICITY); P_InitAngle(spark, sparkangle); + spark->momx = mo->momx; + spark->momy = mo->momy; + spark->momz = mo->momz; spark->color = color; K_GenericExtraFlagsNoZAdjust(spark, mo); diff --git a/src/p_mobj.c b/src/p_mobj.c index 886c0eaa1..57034ebe0 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -13155,6 +13155,17 @@ mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zo newmobj->z = mobj->z + mobj->height - zofs - newmobj->height; } + // EXPERIMENT: Let all objects set their interp values relative to their owner's old values. + // This will hopefully create a lot less mobj-specific spawn cases, + // but if there's any weird scenarios feel free to remove again. + newmobj->old_x = mobj->old_x + xofs; + newmobj->old_y = mobj->old_y + yofs; + newmobj->old_z = mobj->old_z + zofs; + /* + newmobj->angle = mobj->angle; + newmobj->old_angle = mobj->old_angle; + */ + return newmobj; } @@ -13171,6 +13182,14 @@ fixed_t P_GetMobjHead(const mobj_t *mobj) fixed_t P_GetMobjFeet(const mobj_t *mobj) { + /* + | | + | | + /--\------/ | + | | + ----------------- + */ + return P_IsObjectFlipped(mobj) ? mobj->z + mobj->height : mobj->z; } From 0e55de6dccfb5955892d996df1769382b773dfe9 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 25 Dec 2021 04:57:35 -0500 Subject: [PATCH 088/379] Interpolate music credits --- src/hu_stuff.c | 173 +++++++++++++++++++++++++++++++++---------------- src/s_sound.c | 2 +- src/s_sound.h | 3 +- 3 files changed, 120 insertions(+), 58 deletions(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index e5b148ccb..0ae57ffaa 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -56,6 +56,7 @@ #include "k_kart.h" #include "k_color.h" #include "k_hud.h" +#include "r_fps.h" // coords are scaled #define HU_INPUTX 0 @@ -165,6 +166,8 @@ static tic_t cechotimer = 0; static tic_t cechoduration = 5*TICRATE; static INT32 cechoflags = 0; +static tic_t resynch_ticker = 0; + //====================================================================== // HEADS UP INIT //====================================================================== @@ -908,6 +911,64 @@ static inline boolean HU_keyInChatString(char *s, char ch) // // +static void HU_TickSongCredits(void) +{ + char *str; + INT32 len; + fixed_t destx; + + cursongcredit.old_x = cursongcredit.x; + + if (!cursongcredit.def) // No def + { + cursongcredit.x = cursongcredit.old_x = 0; + cursongcredit.anim = 0; + cursongcredit.trans = NUMTRANSMAPS; + return; + } + + str = va("\x1F"" %s", cursongcredit.def->source); + len = V_ThinStringWidth(str, V_ALLOWLOWERCASE|V_6WIDTHSPACE); + destx = (len+7) * FRACUNIT; + + if (cursongcredit.anim > 0) + { + if (cursongcredit.trans > 0) + { + cursongcredit.trans--; + } + + if (cursongcredit.x < destx) + { + cursongcredit.x += (destx - cursongcredit.x) / 2; + } + + if (cursongcredit.x > destx) + { + cursongcredit.x = destx; + } + + cursongcredit.anim--; + } + else + { + if (cursongcredit.trans < NUMTRANSMAPS) + { + cursongcredit.trans++; + } + + if (cursongcredit.x > 0) + { + cursongcredit.x /= 2; + } + + if (cursongcredit.x < 0) + { + cursongcredit.x = 0; + } + } +} + void HU_Ticker(void) { if (dedicated) @@ -924,6 +985,49 @@ void HU_Ticker(void) */ hu_keystrokes = false; + + if (chat_on) + { + // count down the scroll timer. + if (chat_scrolltime > 0) + chat_scrolltime--; + } + else + { + chat_scrolltime = 0; + } + + if (netgame) // would handle that in hu_drawminichat, but it's actually kinda awkward when you're typing a lot of messages. (only handle that in netgames duh) + { + size_t i = 0; + + // handle spam while we're at it: + for(; (i 0) + stop_spamming[i]--; + } + + // handle chat timers + for (i=0; (i 0) + chat_timers[i]--; + else + HU_removeChatText_Mini(); + } + } + + cechotimer--; + + if (gamestate != GS_LEVEL) + { + return; + } + + resynch_ticker++; + + HU_TickSongCredits(); } #ifndef NONET @@ -1915,8 +2019,6 @@ static void HU_DrawCEcho(void) echoptr = line; echoptr++; } - - --cechotimer; } // @@ -1966,42 +2068,28 @@ static void HU_DrawDemoInfo(void) void HU_DrawSongCredits(void) { char *str; - INT32 len, destx; - INT32 y = (r_splitscreen ? (BASEVIDHEIGHT/2)-4 : 32); + fixed_t x; + fixed_t y = (r_splitscreen ? (BASEVIDHEIGHT/2)-4 : 32) * FRACUNIT; INT32 bgt; if (!cursongcredit.def) // No def + { return; + } str = va("\x1F"" %s", cursongcredit.def->source); - len = V_ThinStringWidth(str, V_ALLOWLOWERCASE|V_6WIDTHSPACE); - destx = (len+7); + bgt = (NUMTRANSMAPS/2) + (cursongcredit.trans / 2); + x = R_InterpolateFixed(cursongcredit.old_x, cursongcredit.x); - if (cursongcredit.anim) - { - if (cursongcredit.trans > 0) - cursongcredit.trans--; - if (cursongcredit.x < destx) - cursongcredit.x += (destx - cursongcredit.x) / 2; - if (cursongcredit.x > destx) - cursongcredit.x = destx; - cursongcredit.anim--; - } - else - { - if (cursongcredit.trans < NUMTRANSMAPS) - cursongcredit.trans++; - if (cursongcredit.x > 0) - cursongcredit.x /= 2; - if (cursongcredit.x < 0) - cursongcredit.x = 0; - } - - bgt = (NUMTRANSMAPS/2)+(cursongcredit.trans/2); if (bgt < NUMTRANSMAPS) - V_DrawScaledPatch(cursongcredit.x, y-2, V_SNAPTOLEFT|(bgt< 0) - chat_scrolltime--; if (!OLDCHAT) HU_DrawChat(); else @@ -2027,31 +2112,9 @@ void HU_Drawer(void) else { typelines = 1; - chat_scrolltime = 0; if (!OLDCHAT && cv_consolechat.value < 2 && netgame) // Don't display minimized chat if you set the mode to Window (Hidden) HU_drawMiniChat(); // draw messages in a cool fashion. } - - if (netgame) // would handle that in hu_drawminichat, but it's actually kinda awkward when you're typing a lot of messages. (only handle that in netgames duh) - { - size_t i = 0; - - // handle spam while we're at it: - for(; (i 0) - stop_spamming[i]--; - } - - // handle chat timers - for (i=0; (i 0) - chat_timers[i]--; - else - HU_removeChatText_Mini(); - } - } #endif if (cechotimer) @@ -2090,12 +2153,10 @@ void HU_Drawer(void) // draw desynch text if (hu_redownloadinggamestate) { - static UINT32 resynch_ticker = 0; char resynch_text[14]; UINT32 i; // Animate the dots - resynch_ticker++; strcpy(resynch_text, "Resynching"); for (i = 0; i < (resynch_ticker / 16) % 4; i++) strcat(resynch_text, "."); diff --git a/src/s_sound.c b/src/s_sound.c index 7e44bb207..ac66a5777 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -1580,7 +1580,7 @@ void S_ShowMusicCredit(void) { cursongcredit.def = def; cursongcredit.anim = 5*TICRATE; - cursongcredit.x = 0; + cursongcredit.x = cursongcredit.old_x =0; cursongcredit.trans = NUMTRANSMAPS; return; } diff --git a/src/s_sound.h b/src/s_sound.h index 816a90fb5..3e6decf74 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -179,8 +179,9 @@ extern struct cursongcredit { musicdef_t *def; UINT16 anim; - INT32 x; UINT8 trans; + fixed_t x; + fixed_t old_x; } cursongcredit; extern musicdef_t *musicdefstart; From 817e6f568a97d9617b0122dd41358e46586ec064 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 25 Dec 2021 05:18:33 -0500 Subject: [PATCH 089/379] Only calculate string when we need it --- src/hu_stuff.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 0ae57ffaa..45b5c079c 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -913,13 +913,7 @@ static inline boolean HU_keyInChatString(char *s, char ch) // static void HU_TickSongCredits(void) { - char *str; - INT32 len; - fixed_t destx; - - cursongcredit.old_x = cursongcredit.x; - - if (!cursongcredit.def) // No def + if (cursongcredit.def == NULL) // No def { cursongcredit.x = cursongcredit.old_x = 0; cursongcredit.anim = 0; @@ -927,12 +921,14 @@ static void HU_TickSongCredits(void) return; } - str = va("\x1F"" %s", cursongcredit.def->source); - len = V_ThinStringWidth(str, V_ALLOWLOWERCASE|V_6WIDTHSPACE); - destx = (len+7) * FRACUNIT; + cursongcredit.old_x = cursongcredit.x; if (cursongcredit.anim > 0) { + char *str = va("\x1F"" %s", cursongcredit.def->source); + INT32 len = V_ThinStringWidth(str, V_ALLOWLOWERCASE|V_6WIDTHSPACE); + fixed_t destx = (len+7) * FRACUNIT; + if (cursongcredit.trans > 0) { cursongcredit.trans--; From 14ee70624f1834d9017fdec681e43cacb2775095 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 25 Dec 2021 05:51:09 -0500 Subject: [PATCH 090/379] Fix lap animation interp being reversed --- src/k_hud.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_hud.c b/src/k_hud.c index 2490139c2..ae6124a75 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -3944,7 +3944,7 @@ static void K_drawLapStartAnim(void) const UINT8 t = stplyr->karthud[khud_lapanimation]; const UINT8 progress = 80 - t; - const UINT8 tOld = t - 1; + const UINT8 tOld = t + 1; const UINT8 progressOld = 80 - tOld; const tic_t leveltimeOld = leveltime - 1; From fc8a7a6ca550209240381948afad3a21936eeda9 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 3 Jan 2022 20:02:22 -0500 Subject: [PATCH 091/379] Scale gravity with map scale instead of object scale --- src/p_map.c | 8 +++----- src/p_mobj.c | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/p_map.c b/src/p_map.c index 7899a0964..33c1821d9 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -291,9 +291,7 @@ static boolean P_SpecialIsLinedefCrossType(line_t *ld) // boolean P_DoSpring(mobj_t *spring, mobj_t *object) { - //INT32 pflags; - const fixed_t hscale = mapobjectscale + (mapobjectscale - object->scale); - const fixed_t vscale = mapobjectscale + (object->scale - mapobjectscale); + const fixed_t scaleVal = FixedSqrt(FixedMul(mapobjectscale, spring->scale)); fixed_t vertispeed = spring->info->mass; fixed_t horizspeed = spring->info->damage; UINT16 starcolor = (spring->info->painchance % numskincolors); @@ -370,13 +368,13 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) if (vertispeed) { - object->momz = FixedMul(vertispeed, FixedSqrt(FixedMul(vscale, spring->scale))); + object->momz = FixedMul(vertispeed, scaleVal); } if (horizspeed) { angle_t finalAngle = spring->angle; - fixed_t finalSpeed = FixedMul(horizspeed, FixedSqrt(FixedMul(hscale, spring->scale))); + fixed_t finalSpeed = FixedMul(horizspeed, scaleVal); fixed_t objectSpeed; if (object->player) diff --git a/src/p_mobj.c b/src/p_mobj.c index 57034ebe0..37385c735 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1201,7 +1201,7 @@ fixed_t P_GetMobjGravity(mobj_t *mo) gravityadd = -((gravityadd/5) + (gravityadd/8)); } - gravityadd = FixedMul(gravityadd, mo->scale); + gravityadd = FixedMul(gravityadd, mapobjectscale); return gravityadd; } From 5192c56d13ac7d35ee8b08baabb8e05904957e47 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 29 Dec 2021 22:25:04 -0800 Subject: [PATCH 092/379] Check to sting player before removing one ring --- src/k_collide.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/k_collide.c b/src/k_collide.c index 96fe30182..9c722e548 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -537,24 +537,24 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) if (t1Condition == true) { - P_PlayerRingBurst(t2->player, 1); - if (t2->player->rings <= 0) { P_DamageMobj(t2, t1, t1, 1, DMG_STING); stung = true; } + + P_PlayerRingBurst(t2->player, 1); } if (t2Condition == true) { - P_PlayerRingBurst(t1->player, 1); - if (t1->player->rings <= 0) { P_DamageMobj(t1, t2, t2, 1, DMG_STING); stung = true; } + + P_PlayerRingBurst(t1->player, 1); } return stung; From f0e33d9a36effdec4a7bb07338fe3c177f3dda28 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 29 Dec 2021 22:58:21 -0800 Subject: [PATCH 093/379] Let objects receive damage while in hitlag --- src/p_inter.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/p_inter.c b/src/p_inter.c index 596bef294..6a352ecec 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1844,8 +1844,10 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (!(target->flags & MF_SHOOTABLE)) return false; // shouldn't happen... +#if 0 if (!(damagetype & DMG_DEATHMASK) && target->hitlag > 0) return false; +#endif } if (target->flags2 & MF2_SKULLFLY) @@ -1928,7 +1930,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (combo == false) { - if (player->flashing > 0) + if (player->mo->hitlag == 0 && player->flashing > 0) { // Post-hit invincibility K_DoInstashield(player); From 97a503d2c21dc8e6cac6f96df5331eea487cf38c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 4 Jan 2022 00:23:12 -0500 Subject: [PATCH 094/379] Don't do hitlag combo with items with threshold Prevents instakill ballhog --- src/k_collide.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/k_collide.c b/src/k_collide.c index 9c722e548..5d072c0eb 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -17,6 +17,9 @@ boolean K_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2) boolean damageitem = false; boolean sprung = false; + if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0)) + return true; + if (((t1->target == t2) || (t1->target == t2->target)) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0))) return true; @@ -108,6 +111,9 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2) { boolean damageitem = false; + if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0)) + return true; + if (((t1->target == t2) || (t1->target == t2->target)) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0))) return true; @@ -186,6 +192,9 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2) boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2) { + if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0)) + return true; + // Push fakes out of other item boxes if (t2->type == MT_RANDOMITEM || t2->type == MT_EGGMANITEM) { @@ -258,6 +267,9 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2) boolean K_MineCollide(mobj_t *t1, mobj_t *t2) { + if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0)) + return true; + if (((t1->target == t2) || (t1->target == t2->target)) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0))) return true; @@ -331,6 +343,9 @@ boolean K_MineExplosionCollide(mobj_t *t1, mobj_t *t2) boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2) { + if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0)) + return true; + if (((t1->target == t2) || (t1->target == t2->target)) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0))) return true; @@ -398,6 +413,9 @@ boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2) boolean K_KitchenSinkCollide(mobj_t *t1, mobj_t *t2) { + if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0)) + return true; + if (((t1->target == t2) || (t1->target == t2->target)) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0))) return true; From 4ff50fe4384cdb7c1263034ca34c27248bcd2bd7 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 4 Jan 2022 00:39:01 -0500 Subject: [PATCH 095/379] Don't use damage hitlag effects on a person when they "win" a ring sting exchange Only applies to ring sting collision (no other types of damage touching), only applies to the person not getting stung. The person who is getting stung & sting trades are unaffected. --- src/k_collide.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/k_collide.c b/src/k_collide.c index 5d072c0eb..9b0463bfc 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -483,7 +483,8 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) { boolean t1Condition = false; boolean t2Condition = false; - boolean stung = false; + boolean stungT1 = false; + boolean stungT2 = false; // Grow damage t1Condition = (t1->scale > t2->scale + (mapobjectscale/8)); @@ -558,7 +559,7 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) if (t2->player->rings <= 0) { P_DamageMobj(t2, t1, t1, 1, DMG_STING); - stung = true; + stungT2 = true; } P_PlayerRingBurst(t2->player, 1); @@ -569,11 +570,21 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) if (t1->player->rings <= 0) { P_DamageMobj(t1, t2, t2, 1, DMG_STING); - stung = true; + stungT1 = true; } P_PlayerRingBurst(t1->player, 1); } - return stung; + // No damage hitlag for stinging. + if (stungT1 == true && stungT2 == false) + { + t2->eflags &= ~MFE_DAMAGEHITLAG; + } + else if (stungT2 == true && stungT1 == false) + { + t1->eflags &= ~MFE_DAMAGEHITLAG; + } + + return (stungT1 || stungT2); } From 12acd6977b27dd06a4c3f74e9c8547603673bedd Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 7 Dec 2021 17:23:42 -0500 Subject: [PATCH 096/379] TERRAIN lump mockup --- src/k_terrain.c | 366 ++++++++++++++++++++++++++++++++++++++++++++++++ src/k_terrain.h | 70 +++++++++ 2 files changed, 436 insertions(+) create mode 100644 src/k_terrain.c create mode 100644 src/k_terrain.h diff --git a/src/k_terrain.c b/src/k_terrain.c new file mode 100644 index 000000000..ea78c9d2d --- /dev/null +++ b/src/k_terrain.c @@ -0,0 +1,366 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2021 by ZDoom + GZDoom teams, and contributors +// Copyright (C) 2021 by Sally "TehRealSalt" Cochenour +// Copyright (C) 2021 by Kart Krew +// +// 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 k_terrain.c +/// \brief Implementation of TERRAIN lump from GZDoom codebase for DRRR. + +#include "k_terrain.h" + +#include "z_zone.h" + +t_splash_t *splashDefs = NULL; +UINT16 numSplashDefs = 0; + +t_footstep_t *footstepDefs = NULL; +UINT16 numFootstepDefs = 0; + +terrain_t *terrainDefs = NULL; +UINT16 numTerrainDefs = 0; + +UINT16 defaultTerrain = UINT16_MAX; + +terrain_t *K_GetTerrainByIndex(UINT16 checkIndex) +{ + if (checkIndex >= numTerrainDefs) + { + return NULL; + } + + return terrainDefs[checkIndex]; +} + +terrain_t *K_GetTerrainByName(const char *checkName) +{ + INT32 i; + + if (numTerrainDefs == 0) + { + return NULL; + } + + // Search backwards through all terrain definitions. + // The latest one will have priority over the older one. + for (i = numTerrainDefs-1; i >= 0; i--) + { + terrain_t t = terrainDefs[i]; + + if (stricmp(checkName, t->name) == 0) + { + // Name matches. + return t; + } + } + + return NULL; +} + +terrain_t *K_GetDefaultTerrain(void) +{ + return K_GetTerrainByIndex(defaultTerrain); +} + +terrain_t *K_GetTerrainForTextureNum(INT32 textureNum) +{ + INT32 i, j; + + if (textureNum == -1) + { + return NULL; + } + + if (numTerrainDefs == 0) + { + return NULL; + } + + // Search backwards through all terrain definitions. + // The latest one will have priority over the older one. + + for (i = numTerrainDefs-1; i >= 0; i--) + { + terrain_t t = terrainDefs[i]; + + if (t->numTextureIDs == 0) + { + // No textures are applied to this terrain type. + continue; + } + + for (j = 0; j < numTextureIDs; j++) + { + if (textureNum == t->textureIDs[j]) + { + // Texture matches. + return t; + } + } + } + + // This texture doesn't have a terrain directly applied to it, + // so we fallback to the default terrain. + return K_GetDefaultTerrain(); +} + +terrain_t *K_GetTerrainForTextureName(const char *checkName) +{ + return K_GetTerrainForTextureNum( R_CheckTextureNumForName(checkName) ); +} + +static void K_GrowSplashDefs(void) +{ + numSplashDefs++; + splashDefs = (t_splash_t *)Z_Realloc(splashDefs, sizeof(t_splash_t) * (numSplashDefs + 1), PU_STATIC, NULL); +} + +static void K_GrowFootstepDefs(void) +{ + numFootstepDefs++; + footstepDefs = (t_footstep_t *)Z_Realloc(footstepDefs, sizeof(t_footstep_t) * (numFootstepDefs + 1), PU_STATIC, NULL); +} + +static void K_ParseNextLine(char *p, char *token) +{ + // parse next line + while (*p != '\0' && *p != '\n') + { + ++p; + } + + if (*p == '\n') + { + ++p; + } + + token = M_GetToken(p); +} + +static void K_GrowTerrainDefs(void) +{ + numTerrainDefs++; + terrainDefs = (terrain_t *)Z_Realloc(terrainDefs, sizeof(terrain_t) * (numTerrainDefs + 1), PU_STATIC, NULL); +} + +static void K_ParseTerrainDefintion(void) +{ + char *token; + size_t tokenLength; + char *endPos; + size_t i; + + // Startname + token = M_GetToken(NULL); + + if (token == NULL) + { + I_Error("Error parsing TERRAIN lump: Expected terrain definition, got end of file"); + } + + if (stricmp(token, "{") != 0) + { + I_Error("Error parsing TERRAIN lump: No starting bracket"); + } + + while (token != NULL) + { + + } + tokenLength = strlen(token); + + if (stricmp(animdefsToken, "OPTIONAL") == 0) + { + // This is meaningful to ZDoom - it tells the program NOT to bomb out + // if the textures can't be found - but it's useless in SRB2, so we'll + // just smile, nod, and carry on + Z_Free(animdefsToken); + animdefsToken = M_GetToken(NULL); + + if (animdefsToken == NULL) + { + I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where start texture/flat name should be"); + } + else if (stricmp(animdefsToken, "RANGE") == 0) + { + // Oh. Um. Apparently "OPTIONAL" is a texture name. Naughty. + // I should probably handle this more gracefully, but right now + // I can't be bothered; especially since ZDoom doesn't handle this + // condition at all. + I_Error("Error parsing ANIMDEFS lump: \"OPTIONAL\" is a keyword; you cannot use it as the startname of an animation"); + } + } + animdefsTokenLength = strlen(animdefsToken); + if (animdefsTokenLength>8) + { + I_Error("Error parsing ANIMDEFS lump: lump name \"%s\" exceeds 8 characters", animdefsToken); + } + + // Search for existing animdef + for (i = 0; i < maxanims; i++) + if (animdefs[i].istexture == istexture // Check if it's the same type! + && stricmp(animdefsToken, animdefs[i].startname) == 0) + { + //CONS_Alert(CONS_NOTICE, "Duplicate animation: %s\n", animdefsToken); + + // If we weren't parsing in reverse order, we would `break` here and parse the new data into the existing slot we found. + // Instead, we're just going to skip parsing the rest of this line entirely. + Z_Free(animdefsToken); + return; + } + + // Not found + if (i == maxanims) + { + // Increase the size to make room for the new animation definition + GrowAnimDefs(); + strncpy(animdefs[i].startname, animdefsToken, 9); + } + + // animdefs[i].startname is now set to animdefsToken either way. + Z_Free(animdefsToken); + + // set texture type + animdefs[i].istexture = istexture; + + // "RANGE" + animdefsToken = M_GetToken(NULL); + if (animdefsToken == NULL) + { + I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where \"RANGE\" after \"%s\"'s startname should be", animdefs[i].startname); + } + if (stricmp(animdefsToken, "ALLOWDECALS") == 0) + { + // Another ZDoom keyword, ho-hum. Skip it, move on to the next token. + Z_Free(animdefsToken); + animdefsToken = M_GetToken(NULL); + } + if (stricmp(animdefsToken, "PIC") == 0) + { + // This is technically legitimate ANIMDEFS syntax, but SRB2 doesn't support it. + I_Error("Error parsing ANIMDEFS lump: Animation definitions utilizing \"PIC\" (specific frames instead of a consecutive range) are not supported by SRB2"); + } + if (stricmp(animdefsToken, "RANGE") != 0) + { + I_Error("Error parsing ANIMDEFS lump: Expected \"RANGE\" after \"%s\"'s startname, got \"%s\"", animdefs[i].startname, animdefsToken); + } + Z_Free(animdefsToken); + + // Endname + animdefsToken = M_GetToken(NULL); + if (animdefsToken == NULL) + { + I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where \"%s\"'s end texture/flat name should be", animdefs[i].startname); + } + animdefsTokenLength = strlen(animdefsToken); + if (animdefsTokenLength>8) + { + I_Error("Error parsing ANIMDEFS lump: lump name \"%s\" exceeds 8 characters", animdefsToken); + } + strncpy(animdefs[i].endname, animdefsToken, 9); + Z_Free(animdefsToken); + + // "TICS" + animdefsToken = M_GetToken(NULL); + if (animdefsToken == NULL) + { + I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where \"%s\"'s \"TICS\" should be", animdefs[i].startname); + } + if (stricmp(animdefsToken, "RAND") == 0) + { + // This is technically legitimate ANIMDEFS syntax, but SRB2 doesn't support it. + I_Error("Error parsing ANIMDEFS lump: Animation definitions utilizing \"RAND\" (random duration per frame) are not supported by SRB2"); + } + if (stricmp(animdefsToken, "TICS") != 0) + { + I_Error("Error parsing ANIMDEFS lump: Expected \"TICS\" in animation definition for \"%s\", got \"%s\"", animdefs[i].startname, animdefsToken); + } + Z_Free(animdefsToken); + + // Speed + animdefsToken = M_GetToken(NULL); + if (animdefsToken == NULL) + { + I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where \"%s\"'s animation speed should be", animdefs[i].startname); + } + endPos = NULL; +#ifndef AVOID_ERRNO + errno = 0; +#endif + + animSpeed = strtol(animdefsToken,&endPos,10); + + if (endPos == animdefsToken // Empty string + || *endPos != '\0' // Not end of string +#ifndef AVOID_ERRNO + || errno == ERANGE // Number out-of-range +#endif + || animSpeed < 0) // Number is not positive + { + I_Error("Error parsing ANIMDEFS lump: Expected a positive integer for \"%s\"'s animation speed, got \"%s\"", animdefs[i].startname, animdefsToken); + } + + animdefs[i].speed = animSpeed; + + Z_Free(animdefsToken); +} + +void K_ParseTERRAINLump(INT32 wadNum, UINT16 lumpnum) +{ + char *lump; + size_t lumpLength; + char *fullText; + char *token; + char *p; + + // Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll + // need to make a space of memory where I can ensure that it will terminate + // correctly. Start by loading the relevant data from the WAD. + lump = (char *)W_CacheLumpNumPwad(wadNum, lumpnum, PU_STATIC); + + // If that didn't exist, we have nothing to do here. + if (lump == NULL) + { + return; + } + + // If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly. + lumpLength = W_LumpLengthPwad(wadNum, lumpnum); + fullText = (char *)Z_Malloc((lumpLength + 1) * sizeof(char), PU_STATIC, NULL); + + // Now move the contents of the lump into this new location. + memmove(fullText, lump, lumpLength); + + // Make damn well sure the last character in our new memory location is \0. + fullText[lumpLength] = '\0'; + + // Finally, free up the memory from the first data load, because we really + // don't need it. + Z_Free(lump); + + // Now, let's start parsing this thing + p = fullText; + token = M_GetToken(p); + + while (token != NULL) + { + if (stricmp(token, "TERRAIN") == 0) + { + Z_Free(token); + K_ParseTerrainDefintion(&p, &token); + } + else + { + I_Error("Error parsing TERRAIN lump: Expected \"SPLASH\", \"FOOTSTEP\", \"TERRAIN\", \"FLOOR\"; got \"%s\"", token); + } + + K_ParseNextLine(&p, &token); + } + + Z_Free(token); + Z_Free((void *)fullText); +} diff --git a/src/k_terrain.h b/src/k_terrain.h new file mode 100644 index 000000000..ac504cd48 --- /dev/null +++ b/src/k_terrain.h @@ -0,0 +1,70 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2021 by ZDoom + GZDoom teams, and contributors +// Copyright (C) 2021 by Sally "TehRealSalt" Cochenour +// Copyright (C) 2021 by Kart Krew +// +// 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 k_terrain.h +/// \brief Implementation of a TERRAIN-style lump for DRRR, ala GZDoom's codebase. + +#ifndef __K_TERRAIN_H__ +#define __K_TERRAIN_H__ + +typedef struct t_splash_s +{ + // Splash definition. + // These are particles spawned when hitting the floor. + + const char *name; // Lookup name. + + UINT16 objType; // Thing type. MT_NULL to not spawn anything. + UINT16 sound; // Sound to play. +} t_splash_t; + +typedef struct t_footstep_s +{ + // Footstep definition. + // These are particles spawned when moving fast enough on a floor. + + const char *name; // Lookup name. + + UINT16 objType; // Thing type. MT_NULL to not spawn anything. + UINT16 sound; // Sound to play. +} t_footstep_t; + +typedef struct terrain_s +{ + // Terrain definition. + // These are all of the properties that the floor gets. + + const char *name; // Lookup name. + + INT32 *textureIDs; // Texture nums this terrain applies to. (Doesn't support flats, stop using them already.) + UINT32 numTextureIDs; // Length of the above table. + + UINT16 splashID; // Splash defintion ID. + UINT16 footstepID; // Footstep defintion ID. + + fixed_t friction; // The default friction of this texture. + UINT8 offroad; // The default offroad level of this texture. + INT16 damageType; // The default damage type of this texture. (Negative means no damage). +} terrain_t; + +// Arrays for all terrain definitions. +extern t_splash_t *splashDefs; +extern UINT16 numSplashDefs; + +extern t_footstep_t *footstepDefs; +extern UINT16 numFootstepDefs; + +extern terrain_t *terrainDefs; +extern UINT16 numTerrainDefs; + +// Default terrain definition ID. +extern UINT16 defaultTerrain; + +#endif // __K_TERRAIN_H__ From 9d65e53d735854bcda638ce254c7d8084d64d7ba Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 8 Dec 2021 19:16:12 -0500 Subject: [PATCH 097/379] TERRAIN lump reading Does not impact gameplay but hey pretty cool --- src/Sourcefile | 1 + src/k_terrain.c | 409 ++++++++++++++++++++++-------------------------- src/k_terrain.h | 19 ++- src/w_wad.c | 4 + 4 files changed, 204 insertions(+), 229 deletions(-) diff --git a/src/Sourcefile b/src/Sourcefile index b423fecb2..67844d410 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -114,3 +114,4 @@ k_menudef.c k_menufunc.c k_menudraw.c k_brightmap.c +k_terrain.c diff --git a/src/k_terrain.c b/src/k_terrain.c index ea78c9d2d..77537f0f3 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -13,6 +13,12 @@ #include "k_terrain.h" +#include "dehacked.h" // get_number +#include "doomtype.h" +#include "fastcmp.h" +#include "m_fixed.h" +#include "r_textures.h" +#include "w_wad.h" #include "z_zone.h" t_splash_t *splashDefs = NULL; @@ -33,7 +39,7 @@ terrain_t *K_GetTerrainByIndex(UINT16 checkIndex) return NULL; } - return terrainDefs[checkIndex]; + return &terrainDefs[checkIndex]; } terrain_t *K_GetTerrainByName(const char *checkName) @@ -49,7 +55,7 @@ terrain_t *K_GetTerrainByName(const char *checkName) // The latest one will have priority over the older one. for (i = numTerrainDefs-1; i >= 0; i--) { - terrain_t t = terrainDefs[i]; + terrain_t *t = &terrainDefs[i]; if (stricmp(checkName, t->name) == 0) { @@ -68,7 +74,7 @@ terrain_t *K_GetDefaultTerrain(void) terrain_t *K_GetTerrainForTextureNum(INT32 textureNum) { - INT32 i, j; + INT32 i; if (textureNum == -1) { @@ -85,7 +91,8 @@ terrain_t *K_GetTerrainForTextureNum(INT32 textureNum) for (i = numTerrainDefs-1; i >= 0; i--) { - terrain_t t = terrainDefs[i]; + terrain_t *t = &terrainDefs[i]; + size_t j; if (t->numTextureIDs == 0) { @@ -93,7 +100,7 @@ terrain_t *K_GetTerrainForTextureNum(INT32 textureNum) continue; } - for (j = 0; j < numTextureIDs; j++) + for (j = 0; j < t->numTextureIDs; j++) { if (textureNum == t->textureIDs[j]) { @@ -113,254 +120,204 @@ terrain_t *K_GetTerrainForTextureName(const char *checkName) return K_GetTerrainForTextureNum( R_CheckTextureNumForName(checkName) ); } -static void K_GrowSplashDefs(void) +// +// Parser code starts here. +// + +static void K_TerrainDefaults(terrain_t *terrain) { - numSplashDefs++; - splashDefs = (t_splash_t *)Z_Realloc(splashDefs, sizeof(t_splash_t) * (numSplashDefs + 1), PU_STATIC, NULL); + terrain->splashID = UINT16_MAX; + terrain->footstepID = UINT16_MAX; + + terrain->friction = FRACUNIT; + terrain->offroad = 0; + terrain->damageType = -1; } -static void K_GrowFootstepDefs(void) -{ - numFootstepDefs++; - footstepDefs = (t_footstep_t *)Z_Realloc(footstepDefs, sizeof(t_footstep_t) * (numFootstepDefs + 1), PU_STATIC, NULL); -} - -static void K_ParseNextLine(char *p, char *token) -{ - // parse next line - while (*p != '\0' && *p != '\n') - { - ++p; - } - - if (*p == '\n') - { - ++p; - } - - token = M_GetToken(p); -} - -static void K_GrowTerrainDefs(void) +static void K_NewTerrainDefs(void) { numTerrainDefs++; terrainDefs = (terrain_t *)Z_Realloc(terrainDefs, sizeof(terrain_t) * (numTerrainDefs + 1), PU_STATIC, NULL); + K_TerrainDefaults( &terrainDefs[numTerrainDefs - 1] ); } -static void K_ParseTerrainDefintion(void) +static void K_ParseTerrainParameter(UINT32 i, char *param, char *val) { - char *token; - size_t tokenLength; - char *endPos; + terrain_t *terrain = &terrainDefs[i]; + + if (stricmp(param, "splash") == 0) + { + //terrain->splashID = 0; + } + else if (stricmp(param, "footstep") == 0) + { + //terrain->footstepID = 0; + } + else if (stricmp(param, "friction") == 0) + { + terrain->friction = FLOAT_TO_FIXED(atof(val)); + } + else if (stricmp(param, "offroad") == 0) + { + terrain->offroad = (UINT8)get_number(val); + } + else if (stricmp(param, "damageType") == 0) + { + terrain->damageType = (INT16)get_number(val); + } +} + +static boolean K_DoTERRAINLumpParse(size_t num, void (*parser)(UINT32, char *, char *)) +{ + char *param, *val; + + param = M_GetToken(NULL); + + if (!fastcmp(param, "{")) + { + Z_Free(param); + CONS_Alert(CONS_WARNING, "Invalid TERRAIN data capsule!\n"); + return false; + } + + Z_Free(param); + + while (true) + { + param = M_GetToken(NULL); + + if (fastcmp(param, "}")) + { + Z_Free(param); + break; + } + + val = M_GetToken(NULL); + parser(num, param, val); + + Z_Free(param); + Z_Free(val); + } + + return true; +} + +static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) +{ + char *tkn = M_GetToken((char *)data); + size_t pos = 0; size_t i; - // Startname - token = M_GetToken(NULL); - - if (token == NULL) + while (tkn && (pos = M_GetTokenPos()) < size) { - I_Error("Error parsing TERRAIN lump: Expected terrain definition, got end of file"); - } + boolean result = true; - if (stricmp(token, "{") != 0) - { - I_Error("Error parsing TERRAIN lump: No starting bracket"); - } - - while (token != NULL) - { - - } - tokenLength = strlen(token); - - if (stricmp(animdefsToken, "OPTIONAL") == 0) - { - // This is meaningful to ZDoom - it tells the program NOT to bomb out - // if the textures can't be found - but it's useless in SRB2, so we'll - // just smile, nod, and carry on - Z_Free(animdefsToken); - animdefsToken = M_GetToken(NULL); - - if (animdefsToken == NULL) + // Avoid anything inside bracketed stuff, only look for external keywords. + if (fastcmp(tkn, "{") || fastcmp(tkn, "}")) { - I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where start texture/flat name should be"); + CONS_Alert(CONS_ERROR, "Rogue bracket detected in TERRAIN lump.\n"); + Z_Free(tkn); + return false; } - else if (stricmp(animdefsToken, "RANGE") == 0) + // Check for valid fields. + else if (stricmp(tkn, "terrain") == 0) { - // Oh. Um. Apparently "OPTIONAL" is a texture name. Naughty. - // I should probably handle this more gracefully, but right now - // I can't be bothered; especially since ZDoom doesn't handle this - // condition at all. - I_Error("Error parsing ANIMDEFS lump: \"OPTIONAL\" is a keyword; you cannot use it as the startname of an animation"); - } - } - animdefsTokenLength = strlen(animdefsToken); - if (animdefsTokenLength>8) - { - I_Error("Error parsing ANIMDEFS lump: lump name \"%s\" exceeds 8 characters", animdefsToken); - } + Z_Free(tkn); + tkn = M_GetToken(NULL); + pos = M_GetTokenPos(); - // Search for existing animdef - for (i = 0; i < maxanims; i++) - if (animdefs[i].istexture == istexture // Check if it's the same type! - && stricmp(animdefsToken, animdefs[i].startname) == 0) - { - //CONS_Alert(CONS_NOTICE, "Duplicate animation: %s\n", animdefsToken); + if (tkn && pos < size) + { + terrain_t *t = NULL; - // If we weren't parsing in reverse order, we would `break` here and parse the new data into the existing slot we found. - // Instead, we're just going to skip parsing the rest of this line entirely. - Z_Free(animdefsToken); - return; - } + for (i = 0; i < numTerrainDefs; i++) + { + t = &terrainDefs[i]; - // Not found - if (i == maxanims) - { - // Increase the size to make room for the new animation definition - GrowAnimDefs(); - strncpy(animdefs[i].startname, animdefsToken, 9); - } + if (stricmp(tkn, t->name) == 0) + { + break; + } + } - // animdefs[i].startname is now set to animdefsToken either way. - Z_Free(animdefsToken); + if (i == numTerrainDefs) + { + K_NewTerrainDefs(); + t = &terrainDefs[i]; - // set texture type - animdefs[i].istexture = istexture; + strncpy(t->name, tkn, TERRAIN_NAME_LEN); + CONS_Printf("Created new Terrain type '%s'\n", t->name); + } - // "RANGE" - animdefsToken = M_GetToken(NULL); - if (animdefsToken == NULL) - { - I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where \"RANGE\" after \"%s\"'s startname should be", animdefs[i].startname); - } - if (stricmp(animdefsToken, "ALLOWDECALS") == 0) - { - // Another ZDoom keyword, ho-hum. Skip it, move on to the next token. - Z_Free(animdefsToken); - animdefsToken = M_GetToken(NULL); - } - if (stricmp(animdefsToken, "PIC") == 0) - { - // This is technically legitimate ANIMDEFS syntax, but SRB2 doesn't support it. - I_Error("Error parsing ANIMDEFS lump: Animation definitions utilizing \"PIC\" (specific frames instead of a consecutive range) are not supported by SRB2"); - } - if (stricmp(animdefsToken, "RANGE") != 0) - { - I_Error("Error parsing ANIMDEFS lump: Expected \"RANGE\" after \"%s\"'s startname, got \"%s\"", animdefs[i].startname, animdefsToken); - } - Z_Free(animdefsToken); - - // Endname - animdefsToken = M_GetToken(NULL); - if (animdefsToken == NULL) - { - I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where \"%s\"'s end texture/flat name should be", animdefs[i].startname); - } - animdefsTokenLength = strlen(animdefsToken); - if (animdefsTokenLength>8) - { - I_Error("Error parsing ANIMDEFS lump: lump name \"%s\" exceeds 8 characters", animdefsToken); - } - strncpy(animdefs[i].endname, animdefsToken, 9); - Z_Free(animdefsToken); - - // "TICS" - animdefsToken = M_GetToken(NULL); - if (animdefsToken == NULL) - { - I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where \"%s\"'s \"TICS\" should be", animdefs[i].startname); - } - if (stricmp(animdefsToken, "RAND") == 0) - { - // This is technically legitimate ANIMDEFS syntax, but SRB2 doesn't support it. - I_Error("Error parsing ANIMDEFS lump: Animation definitions utilizing \"RAND\" (random duration per frame) are not supported by SRB2"); - } - if (stricmp(animdefsToken, "TICS") != 0) - { - I_Error("Error parsing ANIMDEFS lump: Expected \"TICS\" in animation definition for \"%s\", got \"%s\"", animdefs[i].startname, animdefsToken); - } - Z_Free(animdefsToken); - - // Speed - animdefsToken = M_GetToken(NULL); - if (animdefsToken == NULL) - { - I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where \"%s\"'s animation speed should be", animdefs[i].startname); - } - endPos = NULL; -#ifndef AVOID_ERRNO - errno = 0; -#endif - - animSpeed = strtol(animdefsToken,&endPos,10); - - if (endPos == animdefsToken // Empty string - || *endPos != '\0' // Not end of string -#ifndef AVOID_ERRNO - || errno == ERANGE // Number out-of-range -#endif - || animSpeed < 0) // Number is not positive - { - I_Error("Error parsing ANIMDEFS lump: Expected a positive integer for \"%s\"'s animation speed, got \"%s\"", animdefs[i].startname, animdefsToken); - } - - animdefs[i].speed = animSpeed; - - Z_Free(animdefsToken); -} - -void K_ParseTERRAINLump(INT32 wadNum, UINT16 lumpnum) -{ - char *lump; - size_t lumpLength; - char *fullText; - char *token; - char *p; - - // Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll - // need to make a space of memory where I can ensure that it will terminate - // correctly. Start by loading the relevant data from the WAD. - lump = (char *)W_CacheLumpNumPwad(wadNum, lumpnum, PU_STATIC); - - // If that didn't exist, we have nothing to do here. - if (lump == NULL) - { - return; - } - - // If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly. - lumpLength = W_LumpLengthPwad(wadNum, lumpnum); - fullText = (char *)Z_Malloc((lumpLength + 1) * sizeof(char), PU_STATIC, NULL); - - // Now move the contents of the lump into this new location. - memmove(fullText, lump, lumpLength); - - // Make damn well sure the last character in our new memory location is \0. - fullText[lumpLength] = '\0'; - - // Finally, free up the memory from the first data load, because we really - // don't need it. - Z_Free(lump); - - // Now, let's start parsing this thing - p = fullText; - token = M_GetToken(p); - - while (token != NULL) - { - if (stricmp(token, "TERRAIN") == 0) - { - Z_Free(token); - K_ParseTerrainDefintion(&p, &token); + result = K_DoTERRAINLumpParse(i, K_ParseTerrainParameter); + } + // TODO: the other block types! + else + { + CONS_Alert(CONS_NOTICE, "No terrain type name.\n"); + } } else { - I_Error("Error parsing TERRAIN lump: Expected \"SPLASH\", \"FOOTSTEP\", \"TERRAIN\", \"FLOOR\"; got \"%s\"", token); + CONS_Alert(CONS_NOTICE, "Unknown field '%s' found in TERRAIN lump.\n", tkn); + Z_Free(tkn); + return false; } - K_ParseNextLine(&p, &token); + if (result == false) + { + Z_Free(tkn); + return false; + } + + Z_Free(tkn); + tkn = M_GetToken(NULL); } - Z_Free(token); - Z_Free((void *)fullText); + Z_Free(tkn); + return true; +} + +void K_InitTerrain(UINT16 wadNum) +{ + UINT16 lumpNum; + lumpinfo_t *lump_p = wadfiles[wadNum]->lumpinfo; + + // Iterate through all lumps and compare the name individually. + // In PK3 files, you can potentially have multiple TERRAIN differentiated by + // their file extension. + for (lumpNum = 0; lumpNum < wadfiles[wadNum]->numlumps; lumpNum++, lump_p++) + { + UINT8 *data; + + if (memcmp(lump_p->name, "TERRAIN", 8) != 0) + { + continue; + } + + data = (UINT8 *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC); + + // If that didn't exist, we have nothing to do here. + if (data == NULL) + { + continue; + } + else + { + size_t size = W_LumpLengthPwad(wadNum, lumpNum); + + size_t nameLength = strlen(wadfiles[wadNum]->filename) + 1 + strlen(lump_p->fullname); // length of file name, '|', and lump name + char *name = malloc(nameLength + 1); + + sprintf(name, "%s|%s", wadfiles[wadNum]->filename, lump_p->fullname); + name[nameLength] = '\0'; + + size = W_LumpLengthPwad(wadNum, lumpNum); + + CONS_Printf(M_GetText("Loading TERRAIN from %s\n"), name); + K_TERRAINLumpParser(data, size); + + free(name); + } + } } diff --git a/src/k_terrain.h b/src/k_terrain.h index ac504cd48..d774fa130 100644 --- a/src/k_terrain.h +++ b/src/k_terrain.h @@ -14,12 +14,17 @@ #ifndef __K_TERRAIN_H__ #define __K_TERRAIN_H__ +#include "doomtype.h" +#include "m_fixed.h" + +#define TERRAIN_NAME_LEN 32 + typedef struct t_splash_s { // Splash definition. // These are particles spawned when hitting the floor. - const char *name; // Lookup name. + char name[TERRAIN_NAME_LEN]; // Lookup name. UINT16 objType; // Thing type. MT_NULL to not spawn anything. UINT16 sound; // Sound to play. @@ -30,7 +35,7 @@ typedef struct t_footstep_s // Footstep definition. // These are particles spawned when moving fast enough on a floor. - const char *name; // Lookup name. + char name[TERRAIN_NAME_LEN]; // Lookup name. UINT16 objType; // Thing type. MT_NULL to not spawn anything. UINT16 sound; // Sound to play. @@ -41,7 +46,7 @@ typedef struct terrain_s // Terrain definition. // These are all of the properties that the floor gets. - const char *name; // Lookup name. + char name[TERRAIN_NAME_LEN]; // Lookup name. INT32 *textureIDs; // Texture nums this terrain applies to. (Doesn't support flats, stop using them already.) UINT32 numTextureIDs; // Length of the above table. @@ -67,4 +72,12 @@ extern UINT16 numTerrainDefs; // Default terrain definition ID. extern UINT16 defaultTerrain; +terrain_t *K_GetTerrainByIndex(UINT16 checkIndex); +terrain_t *K_GetTerrainByName(const char *checkName); +terrain_t *K_GetDefaultTerrain(void); +terrain_t *K_GetTerrainForTextureNum(INT32 textureNum); +terrain_t *K_GetTerrainForTextureName(const char *checkName); + +void K_InitTerrain(UINT16 wadNum); + #endif // __K_TERRAIN_H__ diff --git a/src/w_wad.c b/src/w_wad.c index 60339c426..c2c4ed231 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -69,6 +69,8 @@ #include "m_misc.h" // M_MapNumber #include "g_game.h" // G_SetGameModified +#include "k_terrain.h" + #ifdef HWRENDER #include "hardware/hw_main.h" #include "hardware/hw_glob.h" @@ -866,6 +868,8 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) break; } + K_InitTerrain(numwadfiles - 1); + if (refreshdirmenu & REFRESHDIR_GAMEDATA) G_LoadGameData(); DEH_UpdateMaxFreeslots(); From 57fb65077ac8f5ce81cbcd79cd885b10f6ed418f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 9 Dec 2021 00:56:05 -0500 Subject: [PATCH 098/379] Add terrain pointer to mobj_t --- src/k_kart.c | 1 + src/k_terrain.c | 88 ++++++++++++++++++++++++++++++++++++++++--------- src/k_terrain.h | 14 ++++++++ src/p_inter.c | 1 + src/p_local.h | 1 + src/p_map.c | 51 +++++++++++++++++++++++++--- src/p_maputl.c | 8 +++++ src/p_maputl.h | 1 + src/p_mobj.c | 79 ++++++++++++++++++++++++++++++-------------- src/p_mobj.h | 1 + src/p_slopes.c | 1 + 11 files changed, 203 insertions(+), 43 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index ca32c6f6d..4e967ac00 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5219,6 +5219,7 @@ void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound) return; mo->standingslope = NULL; + mo->terrain = NULL; mo->eflags |= MFE_SPRUNG; diff --git a/src/k_terrain.c b/src/k_terrain.c index 77537f0f3..fabc89882 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -14,9 +14,13 @@ #include "k_terrain.h" #include "dehacked.h" // get_number +#include "doomdata.h" +#include "doomdef.h" #include "doomtype.h" #include "fastcmp.h" #include "m_fixed.h" +#include "p_local.h" +#include "p_mobj.h" #include "r_textures.h" #include "w_wad.h" #include "z_zone.h" @@ -120,10 +124,51 @@ terrain_t *K_GetTerrainForTextureName(const char *checkName) return K_GetTerrainForTextureNum( R_CheckTextureNumForName(checkName) ); } +void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID) +{ + levelflat_t *levelFlat = NULL; + + if (mo == NULL || P_MobjWasRemoved(mo) == true) + { + // Invalid object. + return; + } + + if (flatID < 0 || flatID >= (signed)numlevelflats) + { + // Clearly invalid floor... + mo->terrain = NULL; + return; + } + + if (mo->flags & MF_NOCLIPHEIGHT) + { + // You can't collide with floors anyway! + mo->terrain = NULL; + return; + } + + // Update the object's terrain pointer. + levelFlat = &levelflats[flatID]; + mo->terrain = K_GetTerrainForTextureName(levelFlat->name); +} + // // Parser code starts here. // +static void K_FlagBoolean(UINT32 *inputFlags, UINT32 newFlag, char *val) +{ + if (stricmp(val, "true") == 0) + { + *inputFlags |= newFlag; + } + else if (stricmp(val, "false") == 0) + { + *inputFlags &= ~newFlag; + } +} + static void K_TerrainDefaults(terrain_t *terrain) { terrain->splashID = UINT16_MAX; @@ -132,6 +177,8 @@ static void K_TerrainDefaults(terrain_t *terrain) terrain->friction = FRACUNIT; terrain->offroad = 0; terrain->damageType = -1; + terrain->trickPanel = 0; + terrain->flags = 0; } static void K_NewTerrainDefs(void) @@ -159,12 +206,24 @@ static void K_ParseTerrainParameter(UINT32 i, char *param, char *val) } else if (stricmp(param, "offroad") == 0) { - terrain->offroad = (UINT8)get_number(val); + terrain->offroad = (UINT8)get_number(val); // offroad strength enum? } else if (stricmp(param, "damageType") == 0) { terrain->damageType = (INT16)get_number(val); } + else if (stricmp(param, "trickPanel") == 0) + { + terrain->trickPanel = (UINT8)get_number(val); // trick panel strength enum? + } + else if (stricmp(param, "liquid") == 0) + { + K_FlagBoolean(&terrain->flags, TRF_LIQUID, val); + } + else if (stricmp(param, "sneakerPanel") == 0) + { + K_FlagBoolean(&terrain->flags, TRF_SNEAKERPANEL, val); + } } static boolean K_DoTERRAINLumpParse(size_t num, void (*parser)(UINT32, char *, char *)) @@ -210,14 +269,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) while (tkn && (pos = M_GetTokenPos()) < size) { - boolean result = true; + boolean valid = true; // Avoid anything inside bracketed stuff, only look for external keywords. if (fastcmp(tkn, "{") || fastcmp(tkn, "}")) { CONS_Alert(CONS_ERROR, "Rogue bracket detected in TERRAIN lump.\n"); - Z_Free(tkn); - return false; + valid = false; } // Check for valid fields. else if (stricmp(tkn, "terrain") == 0) @@ -249,28 +307,28 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) CONS_Printf("Created new Terrain type '%s'\n", t->name); } - result = K_DoTERRAINLumpParse(i, K_ParseTerrainParameter); + valid = K_DoTERRAINLumpParse(i, K_ParseTerrainParameter); } // TODO: the other block types! else { - CONS_Alert(CONS_NOTICE, "No terrain type name.\n"); + CONS_Alert(CONS_ERROR, "No terrain type name.\n"); + valid = false; } } else { - CONS_Alert(CONS_NOTICE, "Unknown field '%s' found in TERRAIN lump.\n", tkn); - Z_Free(tkn); - return false; - } - - if (result == false) - { - Z_Free(tkn); - return false; + CONS_Alert(CONS_ERROR, "Unknown field '%s' found in TERRAIN lump.\n", tkn); + valid = false; } Z_Free(tkn); + + if (valid == false) + { + return false; + } + tkn = M_GetToken(NULL); } diff --git a/src/k_terrain.h b/src/k_terrain.h index d774fa130..94db49dfb 100644 --- a/src/k_terrain.h +++ b/src/k_terrain.h @@ -14,8 +14,11 @@ #ifndef __K_TERRAIN_H__ #define __K_TERRAIN_H__ +#include "doomdata.h" +#include "doomdef.h" #include "doomtype.h" #include "m_fixed.h" +#include "p_mobj.h" #define TERRAIN_NAME_LEN 32 @@ -41,6 +44,13 @@ typedef struct t_footstep_s UINT16 sound; // Sound to play. } t_footstep_t; +typedef enum +{ + // Terrain flag values. + TRF_LIQUID = 1, // Texture water properties (wavy, slippery, etc) + TRF_SNEAKERPANEL = 1<<1 // Texture is a booster +} terrain_flags_t; + typedef struct terrain_s { // Terrain definition. @@ -57,6 +67,8 @@ typedef struct terrain_s fixed_t friction; // The default friction of this texture. UINT8 offroad; // The default offroad level of this texture. INT16 damageType; // The default damage type of this texture. (Negative means no damage). + UINT8 trickPanel; // Trick panel strength + UINT32 flags; // Flag values (see: terrain_flags_t) } terrain_t; // Arrays for all terrain definitions. @@ -78,6 +90,8 @@ terrain_t *K_GetDefaultTerrain(void); terrain_t *K_GetTerrainForTextureNum(INT32 textureNum); terrain_t *K_GetTerrainForTextureName(const char *checkName); +void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID); + void K_InitTerrain(UINT16 wadNum); #endif // __K_TERRAIN_H__ diff --git a/src/p_inter.c b/src/p_inter.c index 6a352ecec..337d2b84f 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1054,6 +1054,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget target->flags |= MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; P_SetThingPosition(target); target->standingslope = NULL; + target->terrain = NULL; target->pmomz = 0; target->player->playerstate = PST_DEAD; diff --git a/src/p_local.h b/src/p_local.h index 51a43b639..3f25fbf79 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -385,6 +385,7 @@ extern camera_t *mapcampointer; extern fixed_t tmx; extern fixed_t tmy; extern pslope_t *tmfloorslope, *tmceilingslope; +extern INT32 tmfloorpic, tmceilingpic; /* cphipps 2004/08/30 */ extern void P_MapStart(void); diff --git a/src/p_map.c b/src/p_map.c index 33c1821d9..8fb58cd0a 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -24,12 +24,13 @@ #include "r_sky.h" #include "s_sound.h" #include "w_wad.h" + #include "k_kart.h" // SRB2kart 011617 #include "k_collide.h" #include "k_respawn.h" - #include "hu_stuff.h" // SRB2kart #include "i_system.h" // SRB2kart +#include "k_terrain.h" #include "r_splats.h" @@ -60,6 +61,7 @@ mobj_t *tmfloorthing; // the thing corresponding to tmfloorz or NULL if tmfloorz mobj_t *tmhitthing; // the solid thing you bumped into (for collisions) ffloor_t *tmfloorrover, *tmceilingrover; pslope_t *tmfloorslope, *tmceilingslope; +INT32 tmfloorpic, tmceilingpic; static fixed_t tmfloorstep; static fixed_t tmceilingstep; @@ -325,6 +327,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) } object->standingslope = NULL; // Okay, now we know it's not going to be relevant - no launching off at silly angles for you. + object->terrain = NULL; object->eflags |= MFE_SPRUNG; // apply this flag asap! spring->flags &= ~(MF_SOLID|MF_SPECIAL); // De-solidify @@ -466,6 +469,7 @@ static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object) } object->standingslope = NULL; // No launching off at silly angles for you. + object->terrain = NULL; switch (spring->type) { @@ -1452,6 +1456,7 @@ static boolean PIT_CheckThing(mobj_t *thing) tmfloorz = thing->z + thing->height; tmfloorrover = NULL; tmfloorslope = NULL; + tmfloorpic = -1; } return true; } @@ -1471,6 +1476,7 @@ static boolean PIT_CheckThing(mobj_t *thing) tmfloorz = tmceilingz = topz; // block while in air tmceilingrover = NULL; tmceilingslope = NULL; + tmceilingpic = -1; tmfloorthing = thing; // needed for side collision } else if (topz < tmceilingz && tmthing->z <= thing->z+thing->height) @@ -1478,6 +1484,7 @@ static boolean PIT_CheckThing(mobj_t *thing) tmceilingz = topz; tmceilingrover = NULL; tmceilingslope = NULL; + tmceilingpic = -1; tmfloorthing = thing; // thing we may stand on } } @@ -1493,6 +1500,7 @@ static boolean PIT_CheckThing(mobj_t *thing) tmceilingz = thing->z; tmceilingrover = NULL; tmceilingslope = NULL; + tmceilingpic = -1; } return true; } @@ -1512,6 +1520,7 @@ static boolean PIT_CheckThing(mobj_t *thing) tmfloorz = tmceilingz = topz; // block while in air tmfloorrover = NULL; tmfloorslope = NULL; + tmfloorpic = -1; tmfloorthing = thing; // needed for side collision } else if (topz > tmfloorz && tmthing->z+tmthing->height >= thing->z) @@ -1519,6 +1528,7 @@ static boolean PIT_CheckThing(mobj_t *thing) tmfloorz = topz; tmfloorrover = NULL; tmfloorslope = NULL; + tmfloorpic = -1; tmfloorthing = thing; // thing we may stand on } } @@ -1698,6 +1708,7 @@ static boolean PIT_CheckLine(line_t *ld) ceilingline = ld; tmceilingrover = openceilingrover; tmceilingslope = opentopslope; + tmceilingpic = opentoppic; tmceilingstep = openceilingstep; if (thingtop == tmthing->ceilingz) { @@ -1710,6 +1721,7 @@ static boolean PIT_CheckLine(line_t *ld) tmfloorz = openbottom; tmfloorrover = openfloorrover; tmfloorslope = openbottomslope; + tmfloorpic = openbottompic; tmfloorstep = openfloorstep; if (tmthing->z == tmthing->floorz) { @@ -1806,6 +1818,8 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) tmceilingrover = NULL; tmfloorslope = newsubsec->sector->f_slope; tmceilingslope = newsubsec->sector->c_slope; + tmfloorpic = newsubsec->sector->floorpic; + tmceilingpic = newsubsec->sector->ceilingpic; tmfloorstep = 0; tmceilingstep = 0; @@ -1859,6 +1873,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) tmfloorz = topheight - sinklevel; tmfloorrover = rover; tmfloorslope = *rover->t_slope; + tmfloorpic = *rover->toppic; } } else if (thing->eflags & MFE_VERTICALFLIP && thingtop <= bottomheight + sinklevel && thing->momz >= 0) @@ -1867,6 +1882,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) tmceilingz = bottomheight + sinklevel; tmceilingrover = rover; tmceilingslope = *rover->b_slope; + tmceilingpic = *rover->bottompic; } } } @@ -1890,6 +1906,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) tmfloorz = thing->z; tmfloorrover = rover; tmfloorslope = NULL; + tmfloorpic = *rover->toppic; } } // Quicksand blocks never change heights otherwise. @@ -1907,6 +1924,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) tmfloorz = tmdropoffz = topheight; tmfloorrover = rover; tmfloorslope = *rover->t_slope; + tmfloorpic = *rover->toppic; } if (bottomheight < tmceilingz && abs(delta1) >= abs(delta2) && !(rover->flags & FF_PLATFORM) @@ -1915,6 +1933,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) tmceilingz = tmdrpoffceilz = bottomheight; tmceilingrover = rover; tmceilingslope = *rover->b_slope; + tmceilingpic = *rover->bottompic; } } } @@ -1989,12 +2008,14 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) tmfloorz = tmdropoffz = polytop; tmfloorslope = NULL; tmfloorrover = NULL; + tmfloorpic = polysec->ceilingpic; } if (polybottom < tmceilingz && abs(delta1) >= abs(delta2)) { tmceilingz = tmdrpoffceilz = polybottom; tmceilingslope = NULL; tmceilingrover = NULL; + tmceilingpic = polysec->floorpic; } } plink = (polymaplink_t *)(plink->link.next); @@ -2412,6 +2433,8 @@ boolean PIT_PushableMoved(mobj_t *thing) ffloor_t *oldceilrover = tmceilingrover; pslope_t *oldfslope = tmfloorslope; pslope_t *oldcslope = tmceilingslope; + INT32 oldfpic = tmfloorpic; + INT32 oldcpic = tmceilingpic; // Move the player P_TryMove(thing, thing->x+stand->momx, thing->y+stand->momy, true); @@ -2428,6 +2451,8 @@ boolean PIT_PushableMoved(mobj_t *thing) tmceilingrover = oldceilrover; tmfloorslope = oldfslope; tmceilingslope = oldcslope; + tmfloorpic = oldfpic; + tmceilingpic = oldcpic; thing->momz = stand->momz; } else @@ -2677,9 +2702,14 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) if (!(thing->flags & MF_NOCLIPHEIGHT)) { // Assign thing's standingslope if needed - if (thing->z <= tmfloorz && !(thing->eflags & MFE_VERTICALFLIP)) { + if (thing->z <= tmfloorz && !(thing->eflags & MFE_VERTICALFLIP)) + { + K_UpdateMobjTerrain(thing, tmfloorpic); + if (!startingonground && tmfloorslope) + { P_HandleSlopeLanding(thing, tmfloorslope); + } if (thing->momz <= 0) { @@ -2687,12 +2717,19 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) P_SetPitchRollFromSlope(thing, thing->standingslope); if (thing->momz == 0 && thing->player && !startingonground) + { P_PlayerHitFloor(thing->player, true); + } } } - else if (thing->z+thing->height >= tmceilingz && (thing->eflags & MFE_VERTICALFLIP)) { + else if (thing->z+thing->height >= tmceilingz && (thing->eflags & MFE_VERTICALFLIP)) + { + K_UpdateMobjTerrain(thing, tmceilingpic); + if (!startingonground && tmceilingslope) + { P_HandleSlopeLanding(thing, tmceilingslope); + } if (thing->momz >= 0) { @@ -2700,12 +2737,18 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) P_SetPitchRollFromSlope(thing, thing->standingslope); if (thing->momz == 0 && thing->player && !startingonground) + { P_PlayerHitFloor(thing->player, true); + } } } } - else // don't set standingslope if you're not going to clip against it + else + { + // don't set standingslope if you're not going to clip against it thing->standingslope = NULL; + thing->terrain = NULL; + } /* FIXME: slope step down (even up) has some false positives, so just ignore them entirely. */ diff --git a/src/p_maputl.c b/src/p_maputl.c index 5acddf09c..555928a3a 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -342,6 +342,7 @@ fixed_t openceilingstep; fixed_t openceilingdrop; fixed_t openfloorstep; fixed_t openfloordrop; +INT32 opentoppic, openbottompic; // P_CameraLineOpening // P_LineOpening, but for camera @@ -537,6 +538,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) highceiling = INT32_MIN; lowfloor = INT32_MAX; opentopslope = openbottomslope = NULL; + opentoppic = openbottompic = -1; openceilingstep = 0; openceilingdrop = 0; openfloorstep = 0; @@ -556,6 +558,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) opentop = height[lo]; highceiling = height[hi]; opentopslope = sector[lo]->c_slope; + opentoppic = sector[lo]->ceilingpic; if (mobj) { @@ -575,6 +578,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) openbottom = height[hi]; lowfloor = height[lo]; openbottomslope = sector[hi]->f_slope; + openbottompic = sector[hi]->floorpic; if (mobj) { @@ -747,6 +751,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) if (bottomheight < open[FRONT].top) { open[FRONT].top = bottomheight; opentopslope = *rover->b_slope; + opentoppic = *rover->bottompic; open[FRONT].ceilingrover = rover; } else if (bottomheight < highceiling) @@ -758,6 +763,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) if (topheight > open[FRONT].bottom) { open[FRONT].bottom = topheight; openbottomslope = *rover->t_slope; + openbottompic = *rover->toppic; open[FRONT].floorrover = rover; } else if (topheight > lowfloor) @@ -789,6 +795,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) if (bottomheight < open[BACK].top) { open[BACK].top = bottomheight; opentopslope = *rover->b_slope; + opentoppic = *rover->bottompic; open[BACK].ceilingrover = rover; } else if (bottomheight < highceiling) @@ -800,6 +807,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) if (topheight > open[BACK].bottom) { open[BACK].bottom = topheight; openbottomslope = *rover->t_slope; + openbottompic = *rover->toppic; open[BACK].floorrover = rover; } else if (topheight > lowfloor) diff --git a/src/p_maputl.h b/src/p_maputl.h index 6f0c1f8ad..43a7fb507 100644 --- a/src/p_maputl.h +++ b/src/p_maputl.h @@ -63,6 +63,7 @@ extern fixed_t openceilingstep; extern fixed_t openceilingdrop; extern fixed_t openfloorstep; extern fixed_t openfloordrop; +extern INT32 opentoppic, openbottompic; void P_LineOpening(line_t *plinedef, mobj_t *mobj); diff --git a/src/p_mobj.c b/src/p_mobj.c index 37385c735..5d32da269 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -40,6 +40,7 @@ #include "k_color.h" #include "k_respawn.h" #include "k_bot.h" +#include "k_terrain.h" static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}}; consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL); @@ -1505,7 +1506,10 @@ void P_XYMovement(mobj_t *mo) oldy = mo->y; if (mo->flags & MF_NOCLIPHEIGHT) + { mo->standingslope = NULL; + mo->terrain = NULL; + } // adjust various things based on slope if (mo->standingslope && abs(mo->standingslope->zdelta) > FRACUNIT>>8) { @@ -1671,6 +1675,7 @@ void P_XYMovement(mobj_t *mo) { mo->momz = transfermomz; mo->standingslope = NULL; + mo->terrain = NULL; P_SetPitchRoll(mo, ANGLE_90, transferslope->xydirection + (transferslope->zangle @@ -1782,6 +1787,7 @@ void P_XYMovement(mobj_t *mo) mo->momz = P_MobjFlip(mo)*FRACUNIT/2; mo->z = predictedz + P_MobjFlip(mo); mo->standingslope = NULL; + mo->terrain = NULL; //CONS_Printf("Launched off of flat surface running into downward slope\n"); } } @@ -2274,6 +2280,8 @@ boolean P_ZMovement(mobj_t *mo) } P_CheckPosition(mo, mo->x, mo->y); // Sets mo->standingslope correctly + K_UpdateMobjTerrain(mo, ((mo->eflags & MFE_VERTICALFLIP) ? tmceilingpic : tmfloorpic)); + if (((mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope) && (mo->type != MT_STEAM)) { mo->standingslope = (mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope; @@ -2503,12 +2511,17 @@ boolean P_ZMovement(mobj_t *mo) if (mo->type == MT_STEAM) return true; } - else if (!(mo->flags & MF_NOGRAVITY)) // Gravity here! + else { - /// \todo may not be needed (done in P_MobjThinker normally) - mo->eflags &= ~MFE_JUSTHITFLOOR; + mo->terrain = NULL; - P_CheckGravity(mo, true); + if (!(mo->flags & MF_NOGRAVITY)) // Gravity here! + { + /// \todo may not be needed (done in P_MobjThinker normally) + mo->eflags &= ~MFE_JUSTHITFLOOR; + + P_CheckGravity(mo, true); + } } if (((mo->z + mo->height > mo->ceilingz && !(mo->eflags & MFE_VERTICALFLIP)) @@ -2712,20 +2725,29 @@ void P_PlayerZMovement(mobj_t *mo) if (onground && !(mo->flags & MF_NOCLIPHEIGHT)) { if (mo->eflags & MFE_VERTICALFLIP) + { mo->z = mo->ceilingz - mo->height; + } else + { mo->z = mo->floorz; + } + + K_UpdateMobjTerrain(mo, (mo->eflags & MFE_VERTICALFLIP ? tmceilingpic : tmfloorpic)); // Get up if you fell. if (mo->player->panim == PA_HURT && mo->player->spinouttimer == 0 && mo->player->tumbleBounces == 0) + { P_SetPlayerMobjState(mo, S_KART_STILL); + } - if (!mo->standingslope && (mo->eflags & MFE_VERTICALFLIP ? tmceilingslope : tmfloorslope)) { + if (!mo->standingslope && (mo->eflags & MFE_VERTICALFLIP ? tmceilingslope : tmfloorslope)) + { // Handle landing on slope during Z movement P_HandleSlopeLanding(mo, (mo->eflags & MFE_VERTICALFLIP ? tmceilingslope : tmfloorslope)); } - if (P_MobjFlip(mo)*mo->momz < 0) // falling + if (P_MobjFlip(mo) * mo->momz < 0) // falling { boolean clipmomz = !(P_CheckDeathPitCollide(mo)); @@ -2736,29 +2758,38 @@ void P_PlayerZMovement(mobj_t *mo) P_PlayerPolyObjectZMovement(mo); if (clipmomz) + { mo->momz = (tmfloorthing ? tmfloorthing->momz : 0); + } } else if (tmfloorthing) - mo->momz = tmfloorthing->momz; - } - else if (!(mo->flags & MF_NOGRAVITY)) // Gravity here! - { - if (P_IsObjectInGoop(mo) && !(mo->flags & MF_NOCLIPHEIGHT)) { - if (mo->z < mo->floorz) - { - mo->z = mo->floorz; - mo->momz = 0; - } - else if (mo->z + mo->height > mo->ceilingz) - { - mo->z = mo->ceilingz - mo->height; - mo->momz = 0; - } + mo->momz = tmfloorthing->momz; + } + } + else + { + mo->terrain = NULL; + + if (!(mo->flags & MF_NOGRAVITY)) // Gravity here! + { + if (P_IsObjectInGoop(mo) && !(mo->flags & MF_NOCLIPHEIGHT)) + { + if (mo->z < mo->floorz) + { + mo->z = mo->floorz; + mo->momz = 0; + } + else if (mo->z + mo->height > mo->ceilingz) + { + mo->z = mo->ceilingz - mo->height; + mo->momz = 0; + } + } + /// \todo may not be needed (done in P_MobjThinker normally) + mo->eflags &= ~MFE_JUSTHITFLOOR; + P_CheckGravity(mo, true); } - /// \todo may not be needed (done in P_MobjThinker normally) - mo->eflags &= ~MFE_JUSTHITFLOOR; - P_CheckGravity(mo, true); } if (((mo->eflags & MFE_VERTICALFLIP && mo->z < mo->floorz) || (!(mo->eflags & MFE_VERTICALFLIP) && mo->z + mo->height > mo->ceilingz)) diff --git a/src/p_mobj.h b/src/p_mobj.h index e5bba25de..53614c5dd 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -397,6 +397,7 @@ typedef struct mobj_s fixed_t sprxoff, spryoff, sprzoff; // Sprite offsets in real space, does NOT affect position or collision + struct terrain_s *terrain; // Terrain definition of the floor this object last hit. NULL when in the air. INT32 hitlag; // Sal-style hit lag, straight from Captain Fetch's jowls // WARNING: New fields must be added separately to savegame and Lua. diff --git a/src/p_slopes.c b/src/p_slopes.c index 1bde8f4ee..6c660ad0f 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -850,6 +850,7 @@ void P_SlopeLaunch(mobj_t *mo) //CONS_Printf("Launched off of slope.\n"); mo->standingslope = NULL; + mo->terrain = NULL; if (mo->player) { From f6c42f70a797bde35cb88ca62946e3a98875cf24 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 9 Dec 2021 01:21:34 -0500 Subject: [PATCH 099/379] Friction value is used, implemented DefaultTerrain --- src/k_terrain.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ src/k_terrain.h | 1 + src/p_mobj.c | 2 +- 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index fabc89882..4928281b1 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -153,6 +153,58 @@ void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID) mo->terrain = K_GetTerrainForTextureName(levelFlat->name); } +void K_SetDefaultFriction(mobj_t *mo) +{ + mo->friction = ORIG_FRICTION; + + if (mo->player != NULL) + { + mo->movefactor = FRACUNIT; + } + + if (mo->terrain != NULL) + { + fixed_t strength = mo->terrain->friction; + + fixed_t newFriction = INT32_MAX; + fixed_t newMovefactor = INT32_MAX; + + if (strength > 0) // sludge + { + strength = strength*2; // otherwise, the maximum sludginess value is +967... + } + + // The following might seem odd. At the time of movement, + // the move distance is multiplied by 'friction/0x10000', so a + // higher friction value actually means 'less friction'. + newFriction = ORIG_FRICTION - FixedMul(0x1EB8, strength) / 0x80; // ORIG_FRICTION is 0xE800 + + if (newFriction > FRACUNIT) + { + newFriction = FRACUNIT; + } + + if (newFriction < 0) + { + newFriction = 0; + } + + newMovefactor = FixedDiv(ORIG_FRICTION, newFriction); + + if (newMovefactor < FRACUNIT) + { + newMovefactor = 19*newMovefactor - 18*FRACUNIT; + } + else + { + newMovefactor = FRACUNIT; + } + + mo->friction = newFriction; + mo->movefactor = newMovefactor; + } +} + // // Parser code starts here. // @@ -316,6 +368,43 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) valid = false; } } + else if (stricmp(tkn, "defaultTerrain") == 0) + { + Z_Free(tkn); + tkn = M_GetToken(NULL); + pos = M_GetTokenPos(); + + if (tkn && pos < size) + { + terrain_t *t = NULL; + + for (i = 0; i < numTerrainDefs; i++) + { + t = &terrainDefs[i]; + + if (stricmp(tkn, t->name) == 0) + { + break; + } + } + + if (i == numTerrainDefs) + { + CONS_Alert(CONS_ERROR, "Invalid DefaultTerrain type.\n"); + valid = false; + } + else + { + defaultTerrain = i; + } + } + // TODO: the other block types! + else + { + CONS_Alert(CONS_ERROR, "No DefaultTerrain type.\n"); + valid = false; + } + } else { CONS_Alert(CONS_ERROR, "Unknown field '%s' found in TERRAIN lump.\n", tkn); diff --git a/src/k_terrain.h b/src/k_terrain.h index 94db49dfb..5660c0d4b 100644 --- a/src/k_terrain.h +++ b/src/k_terrain.h @@ -91,6 +91,7 @@ terrain_t *K_GetTerrainForTextureNum(INT32 textureNum); terrain_t *K_GetTerrainForTextureName(const char *checkName); void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID); +void K_SetDefaultFriction(mobj_t *mo); void K_InitTerrain(UINT16 wadNum); diff --git a/src/p_mobj.c b/src/p_mobj.c index 5d32da269..ceaceae0e 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1341,7 +1341,7 @@ static void P_XYFriction(mobj_t *mo, fixed_t oldx, fixed_t oldy) mo->momy = FixedMul(mo->momy, mo->friction); } - mo->friction = ORIG_FRICTION; + K_SetDefaultFriction(mo); } } else From 595d568b53bcea971d01a8aabb2409d66528971b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 9 Dec 2021 01:55:29 -0500 Subject: [PATCH 100/379] You can now link terrain to textures --- src/k_terrain.c | 128 ++++++++++++++++++++++++++++++++++++++---------- src/k_terrain.h | 21 ++++++-- 2 files changed, 119 insertions(+), 30 deletions(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index 4928281b1..a2b09158d 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -34,6 +34,9 @@ UINT16 numFootstepDefs = 0; terrain_t *terrainDefs = NULL; UINT16 numTerrainDefs = 0; +t_floor_t *terrainFloorDefs = NULL; +UINT16 numTerrainFloorDefs = 0; + UINT16 defaultTerrain = UINT16_MAX; terrain_t *K_GetTerrainByIndex(UINT16 checkIndex) @@ -76,16 +79,11 @@ terrain_t *K_GetDefaultTerrain(void) return K_GetTerrainByIndex(defaultTerrain); } -terrain_t *K_GetTerrainForTextureNum(INT32 textureNum) +terrain_t *K_GetTerrainForTextureName(const char *checkName) { INT32 i; - if (textureNum == -1) - { - return NULL; - } - - if (numTerrainDefs == 0) + if (numTerrainFloorDefs == 0) { return NULL; } @@ -93,24 +91,13 @@ terrain_t *K_GetTerrainForTextureNum(INT32 textureNum) // Search backwards through all terrain definitions. // The latest one will have priority over the older one. - for (i = numTerrainDefs-1; i >= 0; i--) + for (i = 0; i < numTerrainFloorDefs; i++) { - terrain_t *t = &terrainDefs[i]; - size_t j; + t_floor_t *f = &terrainFloorDefs[i]; - if (t->numTextureIDs == 0) + if (stricmp(checkName, f->textureName) == 0) { - // No textures are applied to this terrain type. - continue; - } - - for (j = 0; j < t->numTextureIDs; j++) - { - if (textureNum == t->textureIDs[j]) - { - // Texture matches. - return t; - } + return K_GetTerrainByIndex(f->terrainID); } } @@ -119,9 +106,17 @@ terrain_t *K_GetTerrainForTextureNum(INT32 textureNum) return K_GetDefaultTerrain(); } -terrain_t *K_GetTerrainForTextureName(const char *checkName) +terrain_t *K_GetTerrainForTextureNum(INT32 textureNum) { - return K_GetTerrainForTextureNum( R_CheckTextureNumForName(checkName) ); + texture_t *tex = NULL; + + if (textureNum < 0 || textureNum >= numtextures) + { + return NULL; + } + + tex = textures[textureNum]; + return K_GetTerrainForTextureName(tex->name); } void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID) @@ -278,6 +273,12 @@ static void K_ParseTerrainParameter(UINT32 i, char *param, char *val) } } +static void K_NewTerrainFloorDefs(void) +{ + numTerrainFloorDefs++; + terrainFloorDefs = (t_floor_t *)Z_Realloc(terrainFloorDefs, sizeof(t_floor_t) * (numTerrainFloorDefs + 1), PU_STATIC, NULL); +} + static boolean K_DoTERRAINLumpParse(size_t num, void (*parser)(UINT32, char *, char *)) { char *param, *val; @@ -361,13 +362,88 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) valid = K_DoTERRAINLumpParse(i, K_ParseTerrainParameter); } - // TODO: the other block types! else { CONS_Alert(CONS_ERROR, "No terrain type name.\n"); valid = false; } } + else if (stricmp(tkn, "floor") == 0) + { + Z_Free(tkn); + tkn = M_GetToken(NULL); + pos = M_GetTokenPos(); + + if (tkn && pos < size) + { + if (stricmp(tkn, "optional") == 0) + { + // "optional" is ZDoom syntax + // We don't use it, but we can ignore it. + Z_Free(tkn); + tkn = M_GetToken(NULL); + pos = M_GetTokenPos(); + } + + if (tkn && pos < size) + { + t_floor_t *f = NULL; + + for (i = 0; i < numTerrainFloorDefs; i++) + { + f = &terrainFloorDefs[i]; + + if (stricmp(tkn, f->textureName) == 0) + { + break; + } + } + + if (i == numTerrainFloorDefs) + { + K_NewTerrainFloorDefs(); + f = &terrainFloorDefs[i]; + + strncpy(f->textureName, tkn, 9); + } + + Z_Free(tkn); + tkn = M_GetToken(NULL); + pos = M_GetTokenPos(); + + if (tkn && pos < size) + { + terrain_t *t = K_GetTerrainByName(tkn); + + if (t == NULL) + { + CONS_Alert(CONS_ERROR, "Invalid Terrain type '%s'.\n", tkn); + valid = false; + } + else + { + f->terrainID = (t - terrainDefs); + CONS_Printf("Texture '%s' set to Terrain '%s'\n", f->textureName, tkn); + } + } + else + { + CONS_Alert(CONS_ERROR, "No terrain for floor definition.\n"); + valid = false; + } + } + else + { + CONS_Alert(CONS_ERROR, "No texture for floor definition.\n"); + valid = false; + } + } + else + { + CONS_Alert(CONS_ERROR, "No texture for floor definition.\n"); + valid = false; + } + } else if (stricmp(tkn, "defaultTerrain") == 0) { Z_Free(tkn); @@ -398,13 +474,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) defaultTerrain = i; } } - // TODO: the other block types! else { CONS_Alert(CONS_ERROR, "No DefaultTerrain type.\n"); valid = false; } } + // TODO: splash & footstep blocks else { CONS_Alert(CONS_ERROR, "Unknown field '%s' found in TERRAIN lump.\n", tkn); diff --git a/src/k_terrain.h b/src/k_terrain.h index 5660c0d4b..e42a2ada2 100644 --- a/src/k_terrain.h +++ b/src/k_terrain.h @@ -58,9 +58,6 @@ typedef struct terrain_s char name[TERRAIN_NAME_LEN]; // Lookup name. - INT32 *textureIDs; // Texture nums this terrain applies to. (Doesn't support flats, stop using them already.) - UINT32 numTextureIDs; // Length of the above table. - UINT16 splashID; // Splash defintion ID. UINT16 footstepID; // Footstep defintion ID. @@ -71,6 +68,19 @@ typedef struct terrain_s UINT32 flags; // Flag values (see: terrain_flags_t) } terrain_t; +typedef struct t_floor_s +{ + // Terrain floor definition. + // Ties texture names to a . + + // (Could be optimized by using texture IDs instead of names, + // but was concerned because I recall sooomething about those not being netsafe? + // Someone confirm if I just hallucinated that. :V) + + char textureName[9]; // Floor texture name. + UINT16 terrainID; // Terrain definition ID. +} t_floor_t; + // Arrays for all terrain definitions. extern t_splash_t *splashDefs; extern UINT16 numSplashDefs; @@ -81,14 +91,17 @@ extern UINT16 numFootstepDefs; extern terrain_t *terrainDefs; extern UINT16 numTerrainDefs; +extern t_floor_t *terrainFloorDefs; +extern UINT16 numTerrainFloorDefs; + // Default terrain definition ID. extern UINT16 defaultTerrain; terrain_t *K_GetTerrainByIndex(UINT16 checkIndex); terrain_t *K_GetTerrainByName(const char *checkName); terrain_t *K_GetDefaultTerrain(void); -terrain_t *K_GetTerrainForTextureNum(INT32 textureNum); terrain_t *K_GetTerrainForTextureName(const char *checkName); +terrain_t *K_GetTerrainForTextureNum(INT32 textureNum); void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID); void K_SetDefaultFriction(mobj_t *mo); From 4d08194fc7669d4d2c5739f1973345646f13a328 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 9 Dec 2021 02:37:44 -0500 Subject: [PATCH 101/379] Liquid terrain functions --- src/p_mobj.c | 244 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 150 insertions(+), 94 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index ceaceae0e..f1e4ce0c0 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3079,19 +3079,37 @@ void P_MobjCheckWater(mobj_t *mobj) } } - if (mobj->watertop > top2) - mobj->watertop = top2; + if (mobj->terrain != NULL) + { + if (mobj->terrain->flags & TRF_LIQUID) + { + // This floor is water. + mobj->eflags |= MFE_TOUCHWATER; - if (mobj->waterbottom < bot2) - mobj->waterbottom = bot2; + if (mobj->eflags & MFE_VERTICALFLIP) + { + mobj->watertop = thingtop + height; + mobj->waterbottom = thingtop; + } + else + { + mobj->watertop = mobj->z; + mobj->waterbottom = mobj->z - height; + } + } + } // Spectators and dead players don't get to do any of the things after this. if (p && (p->spectator || p->playerstate != PST_LIVE)) + { return; + } // The rest of this code only executes on a water state change. - if (!!(mobj->eflags & MFE_UNDERWATER) == wasinwater) + if (waterwasnotset || !!(mobj->eflags & MFE_UNDERWATER) == wasinwater) + { return; + } if (p && !p->waterskip && p->curshield != KSHIELD_BUBBLE && wasinwater) @@ -3104,6 +3122,132 @@ void P_MobjCheckWater(mobj_t *mobj) || ((mobj->info->flags & MF_PUSHABLE) && mobj->fuse) // Previously pushable, might be moving still ) { + fixed_t waterZ = INT32_MAX; + fixed_t solidZ = INT32_MAX; + fixed_t diff = INT32_MAX; + + fixed_t thingZ = INT32_MAX; + boolean splashValid = false; + + if (mobj->eflags & MFE_VERTICALFLIP) + { + waterZ = mobj->waterbottom; + solidZ = mobj->ceilingz; + } + else + { + waterZ = mobj->watertop; + solidZ = mobj->floorz; + } + + diff = waterZ - solidZ; + if (mobj->eflags & MFE_VERTICALFLIP) + { + diff = -diff; + } + + // Check to make sure you didn't just cross into a sector to jump out of + // that has shallower water than the block you were originally in. + if (diff <= (height >> 1)) + { + return; + } + + if (mobj->eflags & MFE_GOOWATER || wasingoo) + { + // Decide what happens to your momentum when you enter/leave goopy water. + if (P_MobjFlip(mobj) * mobj->momz > 0) + { + mobj->momz -= (mobj->momz/8); // cut momentum a little bit to prevent multiple bobs + //CONS_Printf("leaving\n"); + } + else + { + if (!wasgroundpounding) + mobj->momz >>= 1; // kill momentum significantly, to make the goo feel thick. + //CONS_Printf("entering\n"); + } + } + else if (wasinwater && P_MobjFlip(mobj) * mobj->momz > 0) + { + // Give the mobj a little out-of-water boost. + mobj->momz = FixedMul(mobj->momz, FixedDiv(780*FRACUNIT, 457*FRACUNIT)); + } + + if (mobj->eflags & MFE_VERTICALFLIP) + { + thingZ = thingtop - (height >> 1); + splashValid = (thingZ - mobj->momz <= waterZ); + } + else + { + thingZ = mobj->z + (height >> 1); + splashValid = (thingZ - mobj->momz >= waterZ); + } + + if (P_MobjFlip(mobj) * mobj->momz <= 0) + { + if (splashValid == true) + { + // Spawn a splash + mobj_t *splish; + mobjtype_t splishtype = (mobj->eflags & MFE_TOUCHLAVA) ? MT_LAVASPLISH : MT_SPLISH; + + if (mobj->eflags & MFE_VERTICALFLIP) + { + splish = P_SpawnMobj(mobj->x, mobj->y, waterZ - FixedMul(mobjinfo[splishtype].height, mobj->scale), splishtype); + splish->flags2 |= MF2_OBJECTFLIP; + splish->eflags |= MFE_VERTICALFLIP; + } + else + { + splish = P_SpawnMobj(mobj->x, mobj->y, waterZ, splishtype); + } + + splish->destscale = mobj->scale; + P_SetScale(splish, mobj->scale); + } + + // skipping stone! + if (p && p->waterskip < 2 + && ((p->speed/3 > abs(mobj->momz)) // Going more forward than horizontal, so you can skip across the water. + || (p->speed > 20*mapobjectscale && p->waterskip)) // Already skipped once, so you can skip once more! + && (splashValid == true)) + { + const fixed_t hop = 5 * mobj->scale; + + mobj->momx = (4*mobj->momx)/5; + mobj->momy = (4*mobj->momy)/5; + mobj->momz = hop * P_MobjFlip(mobj); + + p->waterskip++; + } + + } + else if (P_MobjFlip(mobj) * mobj->momz > 0) + { + if (splashValid == true && !(mobj->eflags & MFE_UNDERWATER)) // underwater check to prevent splashes on opposite side + { + // Spawn a splash + mobj_t *splish; + mobjtype_t splishtype = (mobj->eflags & MFE_TOUCHLAVA) ? MT_LAVASPLISH : MT_SPLISH; + + if (mobj->eflags & MFE_VERTICALFLIP) + { + splish = P_SpawnMobj(mobj->x, mobj->y, waterZ - FixedMul(mobjinfo[splishtype].height, mobj->scale), splishtype); + splish->flags2 |= MF2_OBJECTFLIP; + splish->eflags |= MFE_VERTICALFLIP; + } + else + { + splish = P_SpawnMobj(mobj->x, mobj->y, waterZ, splishtype); + } + + splish->destscale = mobj->scale; + P_SetScale(splish, mobj->scale); + } + } + // Time to spawn the bubbles! { INT32 i; @@ -3119,7 +3263,7 @@ void P_MobjCheckWater(mobj_t *mobj) else S_StartSound(mobj, sfx_splish); // And make a sound! - bubblecount = FixedDiv(abs(mobj->momz), mobj->scale)>>(FRACBITS-1); + bubblecount = FixedDiv(abs(mobj->momz), mobj->scale) >> (FRACBITS-1); // Max bubble count if (bubblecount > 128) bubblecount = 128; @@ -3156,94 +3300,6 @@ void P_MobjCheckWater(mobj_t *mobj) } } } - - if (waterwasnotset) - return; - - // Check to make sure you didn't just cross into a sector to jump out of - // that has shallower water than the block you were originally in. - if ((!(mobj->eflags & MFE_VERTICALFLIP) && mobj->watertop-mobj->floorz <= height>>1) - || ((mobj->eflags & MFE_VERTICALFLIP) && mobj->ceilingz-mobj->waterbottom <= height>>1)) - return; - - if (mobj->eflags & MFE_GOOWATER || wasingoo) { // Decide what happens to your momentum when you enter/leave goopy water. - if (P_MobjFlip(mobj)*mobj->momz > 0) - { - mobj->momz -= (mobj->momz/8); // cut momentum a little bit to prevent multiple bobs - //CONS_Printf("leaving\n"); - } - else - { - if (!wasgroundpounding) - mobj->momz >>= 1; // kill momentum significantly, to make the goo feel thick. - //CONS_Printf("entering\n"); - } - } - else if (wasinwater && P_MobjFlip(mobj)*mobj->momz > 0) - mobj->momz = FixedMul(mobj->momz, FixedDiv(780*FRACUNIT, 457*FRACUNIT)); // Give the mobj a little out-of-water boost. - - if (P_MobjFlip(mobj)*mobj->momz < 0) - { - if ((mobj->eflags & MFE_VERTICALFLIP && thingtop-(height>>1)-mobj->momz <= mobj->waterbottom) - || (!(mobj->eflags & MFE_VERTICALFLIP) && mobj->z+(height>>1)-mobj->momz >= mobj->watertop)) - { - // Spawn a splash - mobj_t *splish; - mobjtype_t splishtype = (mobj->eflags & MFE_TOUCHLAVA) ? MT_LAVASPLISH : MT_SPLISH; - if (mobj->eflags & MFE_VERTICALFLIP) - { - splish = P_SpawnMobj(mobj->x, mobj->y, mobj->waterbottom-FixedMul(mobjinfo[splishtype].height, mobj->scale), splishtype); - splish->flags2 |= MF2_OBJECTFLIP; - splish->eflags |= MFE_VERTICALFLIP; - } - else - splish = P_SpawnMobj(mobj->x, mobj->y, mobj->watertop, splishtype); - splish->destscale = mobj->scale; - P_SetScale(splish, mobj->scale); - } - - // skipping stone! - if (p && p->waterskip < 2 - && ((p->speed/3 > abs(mobj->momz)) // Going more forward than horizontal, so you can skip across the water. - || (p->speed > 20*mapobjectscale && p->waterskip)) // Already skipped once, so you can skip once more! - && ((!(mobj->eflags & MFE_VERTICALFLIP) && thingtop - mobj->momz > mobj->watertop) - || ((mobj->eflags & MFE_VERTICALFLIP) && mobj->z - mobj->momz < mobj->waterbottom))) - { - const fixed_t hop = 5<momx = (4*mobj->momx)/5; - mobj->momy = (4*mobj->momy)/5; - - if (mobj->eflags & MFE_VERTICALFLIP) - mobj->momz = FixedMul(-hop, mobj->scale); - else - mobj->momz = FixedMul(hop, mobj->scale); - - p->waterskip++; - } - - } - else if (P_MobjFlip(mobj)*mobj->momz > 0) - { - if (((mobj->eflags & MFE_VERTICALFLIP && thingtop-(height>>1)-mobj->momz > mobj->waterbottom) - || (!(mobj->eflags & MFE_VERTICALFLIP) && mobj->z+(height>>1)-mobj->momz < mobj->watertop)) - && !(mobj->eflags & MFE_UNDERWATER)) // underwater check to prevent splashes on opposite side - { - // Spawn a splash - mobj_t *splish; - mobjtype_t splishtype = (mobj->eflags & MFE_TOUCHLAVA) ? MT_LAVASPLISH : MT_SPLISH; - if (mobj->eflags & MFE_VERTICALFLIP) - { - splish = P_SpawnMobj(mobj->x, mobj->y, mobj->waterbottom-FixedMul(mobjinfo[splishtype].height, mobj->scale), splishtype); - splish->flags2 |= MF2_OBJECTFLIP; - splish->eflags |= MFE_VERTICALFLIP; - } - else - splish = P_SpawnMobj(mobj->x, mobj->y, mobj->watertop, splishtype); - splish->destscale = mobj->scale; - P_SetScale(splish, mobj->scale); - } - } } } From e4c04169d743e74fe47792bb5fd3daa52652b1f5 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 9 Dec 2021 03:20:45 -0500 Subject: [PATCH 102/379] Implement the sector special-like effects Offroad, sneaker panels, trick panels, and damaging floors can now be set via terrain. --- src/k_kart.c | 14 ++++++- src/k_terrain.c | 99 ++++++++++++++++++++++++++++++++++++++++++++----- src/k_terrain.h | 2 + src/p_spec.c | 4 ++ src/r_bsp.c | 32 ++++++++++++++-- 5 files changed, 137 insertions(+), 14 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 4e967ac00..a2edc297f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -33,6 +33,7 @@ #include "k_waypoint.h" #include "k_bot.h" #include "k_hud.h" +#include "k_terrain.h" // SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H: // gamespeed is cc (0 for easy, 1 for normal, 2 for hard) @@ -1605,6 +1606,7 @@ static UINT8 K_CheckOffroadCollide(mobj_t *mo) } } + return 0; // couldn't find any offroad } @@ -1616,7 +1618,17 @@ static UINT8 K_CheckOffroadCollide(mobj_t *mo) */ static void K_UpdateOffroad(player_t *player) { - fixed_t offroadstrength = (K_CheckOffroadCollide(player->mo) << FRACBITS); + terrain_t *terrain = player->mo->terrain; + fixed_t offroadstrength = 0; + + if (terrain != NULL && terrain->offroad > 0) + { + offroadstrength = (terrain->offroad << FRACBITS); + } + else + { + offroadstrength = (K_CheckOffroadCollide(player->mo) << FRACBITS); + } // If you are in offroad, a timer starts. if (offroadstrength) diff --git a/src/k_terrain.c b/src/k_terrain.c index a2b09158d..b8edebf03 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -25,6 +25,8 @@ #include "w_wad.h" #include "z_zone.h" +#include "k_kart.h" // on the chopping block... + t_splash_t *splashDefs = NULL; UINT16 numSplashDefs = 0; @@ -119,20 +121,25 @@ terrain_t *K_GetTerrainForTextureNum(INT32 textureNum) return K_GetTerrainForTextureName(tex->name); } -void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID) +terrain_t *K_GetTerrainForFlatNum(INT32 flatID) { levelflat_t *levelFlat = NULL; - if (mo == NULL || P_MobjWasRemoved(mo) == true) - { - // Invalid object. - return; - } - if (flatID < 0 || flatID >= (signed)numlevelflats) { // Clearly invalid floor... - mo->terrain = NULL; + return NULL; + } + + levelFlat = &levelflats[flatID]; + return K_GetTerrainForTextureName(levelFlat->name); +} + +void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID) +{ + if (mo == NULL || P_MobjWasRemoved(mo) == true) + { + // Invalid object. return; } @@ -144,12 +151,84 @@ void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID) } // Update the object's terrain pointer. - levelFlat = &levelflats[flatID]; - mo->terrain = K_GetTerrainForTextureName(levelFlat->name); + mo->terrain = K_GetTerrainForFlatNum(flatID); +} + +void K_ProcessTerrainEffect(mobj_t *mo) +{ + player_t *player = NULL; + terrain_t *terrain = NULL; + + if (mo == NULL || P_MobjWasRemoved(mo) == true) + { + // Invalid object. + return; + } + + if (mo->terrain == NULL) + { + // No terrain type. + return; + } + + terrain = mo->terrain; + player = mo->player; + + if (player == NULL) + { + // maybe can support regualar mobjs later? :) + return; + } + + // Damage effects + if (terrain->damageType > 0) + { + UINT8 dmg = (terrain->damageType & 0xFF); + P_DamageMobj(mo, NULL, NULL, 1, dmg); + } + + // Sneaker panel + if (terrain->flags & TRF_SNEAKERPANEL) + { + if (!player->floorboost) + player->floorboost = 3; + else + player->floorboost = 2; + + K_DoSneaker(player, 0); + } + + // Trick panel + if (terrain->trickPanel > 0 && !(mo->eflags & MFE_SPRUNG)) + { + const fixed_t hscale = mapobjectscale + (mapobjectscale - mo->scale); + const fixed_t minspeed = 24*hscale; + fixed_t speed = FixedHypot(mo->momx, mo->momy); + fixed_t upwards = 16 * FRACUNIT * terrain->trickPanel; + + player->trickpanel = 1; + player->pflags |= PF_TRICKDELAY; + K_DoPogoSpring(mo, upwards, 1); + + if (speed < minspeed) + { + speed = minspeed; + } + + P_InstaThrust(mo, mo->angle, speed); + } + + // (Offroad is handled elsewhere!) } void K_SetDefaultFriction(mobj_t *mo) { + if (mo == NULL || P_MobjWasRemoved(mo) == true) + { + // Invalid object. + return; + } + mo->friction = ORIG_FRICTION; if (mo->player != NULL) diff --git a/src/k_terrain.h b/src/k_terrain.h index e42a2ada2..600656522 100644 --- a/src/k_terrain.h +++ b/src/k_terrain.h @@ -102,8 +102,10 @@ terrain_t *K_GetTerrainByName(const char *checkName); terrain_t *K_GetDefaultTerrain(void); terrain_t *K_GetTerrainForTextureName(const char *checkName); terrain_t *K_GetTerrainForTextureNum(INT32 textureNum); +terrain_t *K_GetTerrainForFlatNum(INT32 flatID); void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID); +void K_ProcessTerrainEffect(mobj_t *mo); void K_SetDefaultFriction(mobj_t *mo); void K_InitTerrain(UINT16 wadNum); diff --git a/src/p_spec.c b/src/p_spec.c index bf7784cd3..b58b8aa40 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -43,6 +43,7 @@ #include "k_kart.h" #include "console.h" // CON_LogMessage #include "k_respawn.h" +#include "k_terrain.h" #ifdef HW3SOUND #include "hardware/hw3sound.h" @@ -4333,7 +4334,9 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers // Conveyor stuff if (section3 == 2 || section3 == 4) + { player->onconveyor = section3; + } special = section1; @@ -5050,6 +5053,7 @@ void P_PlayerInSpecialSector(player_t *player) if (!player->mo) return; + K_ProcessTerrainEffect(player->mo); originalsector = player->mo->subsector->sector; P_PlayerOnSpecial3DFloor(player, originalsector); // Handle FOFs first. diff --git a/src/r_bsp.c b/src/r_bsp.c index e9d51c03a..6095f3739 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -23,6 +23,8 @@ #include "z_zone.h" // Check R_Prep3DFloors #include "taglist.h" +#include "k_terrain.h" + seg_t *curline; side_t *sidedef; line_t *linedef; @@ -67,11 +69,35 @@ boolean R_IsRipplePlane(sector_t *sector, ffloor_t *rover, int ceiling) static void R_PlaneLightOverride(sector_t *sector, boolean ceiling, INT32 *lightlevel) { - if (GETSECSPECIAL(sector->special, 4) == 6) // Fullbright sneaker panels + terrain_t *t = NULL; + + if (ceiling == true) { - if ((ceiling && (sector->flags & SF_FLIPSPECIAL_CEILING)) - || (!ceiling && (sector->flags & SF_FLIPSPECIAL_FLOOR))) + t = K_GetTerrainForFlatNum(sector->ceilingpic); + } + else + { + t = K_GetTerrainForFlatNum(sector->floorpic); + } + + if (t != NULL) + { + if (t->flags & TRF_SNEAKERPANEL) + { *lightlevel = 255; + } + } + else + { + // Sector effect sneaker panels (DEPRECATED) + if (GETSECSPECIAL(sector->special, 4) == 6) + { + if ((ceiling && (sector->flags & SF_FLIPSPECIAL_CEILING)) + || (!ceiling && (sector->flags & SF_FLIPSPECIAL_FLOOR))) + { + *lightlevel = 255; + } + } } } From 6cc30005b37a3e53ad25c4020f369ec7f73aee1c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 9 Dec 2021 13:20:35 -0500 Subject: [PATCH 103/379] Sync mobj->terrain This should be enough I think to sync terrain up. The data structures can't change after the files have been loaded, so it should be good. Needs proper online testing though --- src/k_terrain.c | 21 +++++++++++++++++++++ src/k_terrain.h | 1 + src/p_saveg.c | 16 ++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/src/k_terrain.c b/src/k_terrain.c index b8edebf03..8c55ac4f2 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -41,6 +41,27 @@ UINT16 numTerrainFloorDefs = 0; UINT16 defaultTerrain = UINT16_MAX; +/*-------------------------------------------------- + size_t K_GetTerrainHeapIndex(terrain_t *terrain) + + See header file for description. +--------------------------------------------------*/ +size_t K_GetTerrainHeapIndex(terrain_t *terrain) +{ + size_t i = SIZE_MAX; + + if (terrain == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL terrain in K_GetTerrainHeapIndex.\n"); + } + else + { + i = (terrain - terrainDefs); + } + + return i; +} + terrain_t *K_GetTerrainByIndex(UINT16 checkIndex) { if (checkIndex >= numTerrainDefs) diff --git a/src/k_terrain.h b/src/k_terrain.h index 600656522..c5bf51168 100644 --- a/src/k_terrain.h +++ b/src/k_terrain.h @@ -97,6 +97,7 @@ extern UINT16 numTerrainFloorDefs; // Default terrain definition ID. extern UINT16 defaultTerrain; +size_t K_GetTerrainHeapIndex(terrain_t *terrain); terrain_t *K_GetTerrainByIndex(UINT16 checkIndex); terrain_t *K_GetTerrainByName(const char *checkName); terrain_t *K_GetDefaultTerrain(void); diff --git a/src/p_saveg.c b/src/p_saveg.c index 357dbb0aa..7710ee0de 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -38,6 +38,7 @@ // SRB2Kart #include "k_battle.h" #include "k_pwrlv.h" +#include "k_terrain.h" savedata_t savedata; UINT8 *save_p; @@ -1540,6 +1541,7 @@ typedef enum MD2_KITEMCAP = 1<<26, MD2_ITNEXT = 1<<27, MD2_LASTMOMZ = 1<<28, + MD2_TERRAIN = 1<<29, } mobj_diff2_t; typedef enum @@ -1782,6 +1784,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) diff2 |= MD2_ITNEXT; if (mobj->lastmomz) diff2 |= MD2_LASTMOMZ; + if (mobj->terrain != NULL) + diff2 |= MD2_TERRAIN; if (diff2 != 0) diff |= MD_MORE; @@ -1979,6 +1983,10 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) { WRITEINT32(save_p, mobj->lastmomz); } + if (diff2 & MD2_TERRAIN) + { + WRITEUINT32(save_p, K_GetTerrainHeapIndex(mobj->terrain)); + } WRITEUINT32(save_p, mobj->mobjnum); } @@ -3077,6 +3085,14 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker) { mobj->lastmomz = READINT32(save_p); } + if (diff2 & MD2_TERRAIN) + { + mobj->terrain = (terrain_t *)(size_t)READUINT32(save_p); + } + else + { + mobj->terrain = NULL; + } if (diff & MD_REDFLAG) { From c35553767631882845a35900fc4617ea66a09990 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 9 Dec 2021 14:45:59 -0500 Subject: [PATCH 104/379] Implement splash & footstep blocks, commentate more of the code They do nothing atm, but they can be set properly now :) --- src/k_terrain.c | 578 +++++++++++++++++++++++++++++++++++++++++++----- src/k_terrain.h | 330 +++++++++++++++++++++++++-- src/p_user.c | 4 - 3 files changed, 839 insertions(+), 73 deletions(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index 8c55ac4f2..9ba13757d 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -14,6 +14,7 @@ #include "k_terrain.h" #include "dehacked.h" // get_number +#include "deh_soc.h" // get_mobjtype #include "doomdata.h" #include "doomdef.h" #include "doomtype.h" @@ -27,19 +28,155 @@ #include "k_kart.h" // on the chopping block... -t_splash_t *splashDefs = NULL; -UINT16 numSplashDefs = 0; +static t_splash_t *splashDefs = NULL; +static size_t numSplashDefs = 0; -t_footstep_t *footstepDefs = NULL; -UINT16 numFootstepDefs = 0; +static t_footstep_t *footstepDefs = NULL; +static size_t numFootstepDefs = 0; -terrain_t *terrainDefs = NULL; -UINT16 numTerrainDefs = 0; +static terrain_t *terrainDefs = NULL; +static size_t numTerrainDefs = 0; -t_floor_t *terrainFloorDefs = NULL; -UINT16 numTerrainFloorDefs = 0; +static t_floor_t *terrainFloorDefs = NULL; +static size_t numTerrainFloorDefs = 0; -UINT16 defaultTerrain = UINT16_MAX; +static size_t defaultTerrain = SIZE_MAX; + +/*-------------------------------------------------- + size_t K_GetSplashHeapIndex(t_splash_t *splash) + + See header file for description. +--------------------------------------------------*/ +size_t K_GetSplashHeapIndex(t_splash_t *splash) +{ + if (splash == NULL) + { + return SIZE_MAX; + } + + return (splash - splashDefs); +} + +/*-------------------------------------------------- + size_t K_GetNumSplashDefs(void) + + See header file for description. +--------------------------------------------------*/ +size_t K_GetNumSplashDefs(void) +{ + return numSplashDefs; +} + +/*-------------------------------------------------- + t_splash_t *K_GetSplashByIndex(size_t checkIndex) + + See header file for description. +--------------------------------------------------*/ +t_splash_t *K_GetSplashByIndex(size_t checkIndex) +{ + if (checkIndex >= numSplashDefs) + { + return NULL; + } + + return &splashDefs[checkIndex]; +} + +/*-------------------------------------------------- + t_splash_t *K_GetSplashByName(const char *checkName) + + See header file for description. +--------------------------------------------------*/ +t_splash_t *K_GetSplashByName(const char *checkName) +{ + size_t i; + + if (numSplashDefs == 0) + { + return NULL; + } + + for (i = 0; i < numSplashDefs; i++) + { + t_splash_t *s = &splashDefs[i]; + + if (stricmp(checkName, s->name) == 0) + { + // Name matches. + return s; + } + } + + return NULL; +} + +/*-------------------------------------------------- + size_t K_GetFootstepHeapIndex(t_footstep_t *footstep) + + See header file for description. +--------------------------------------------------*/ +size_t K_GetFootstepHeapIndex(t_footstep_t *footstep) +{ + if (footstep == NULL) + { + return SIZE_MAX; + } + + return (footstep - footstepDefs); +} + +/*-------------------------------------------------- + size_t K_GetNumFootstepDefs(void) + + See header file for description. +--------------------------------------------------*/ +size_t K_GetNumFootstepDefs(void) +{ + return numFootstepDefs; +} + +/*-------------------------------------------------- + t_footstep_t *K_GetFootstepByIndex(size_t checkIndex) + + See header file for description. +--------------------------------------------------*/ +t_footstep_t *K_GetFootstepByIndex(size_t checkIndex) +{ + if (checkIndex >= numFootstepDefs) + { + return NULL; + } + + return &footstepDefs[checkIndex]; +} + +/*-------------------------------------------------- + t_footstep_t *K_GetFootstepByName(const char *checkName) + + See header file for description. +--------------------------------------------------*/ +t_footstep_t *K_GetFootstepByName(const char *checkName) +{ + size_t i; + + if (numFootstepDefs == 0) + { + return NULL; + } + + for (i = 0; i < numFootstepDefs; i++) + { + t_footstep_t *fs = &footstepDefs[i]; + + if (stricmp(checkName, fs->name) == 0) + { + // Name matches. + return fs; + } + } + + return NULL; +} /*-------------------------------------------------- size_t K_GetTerrainHeapIndex(terrain_t *terrain) @@ -48,21 +185,30 @@ UINT16 defaultTerrain = UINT16_MAX; --------------------------------------------------*/ size_t K_GetTerrainHeapIndex(terrain_t *terrain) { - size_t i = SIZE_MAX; - if (terrain == NULL) { - CONS_Debug(DBG_GAMELOGIC, "NULL terrain in K_GetTerrainHeapIndex.\n"); - } - else - { - i = (terrain - terrainDefs); + return SIZE_MAX; } - return i; + return (terrain - terrainDefs); } -terrain_t *K_GetTerrainByIndex(UINT16 checkIndex) +/*-------------------------------------------------- + size_t K_GetNumTerrainDefs(void) + + See header file for description. +--------------------------------------------------*/ +size_t K_GetNumTerrainDefs(void) +{ + return numTerrainDefs; +} + +/*-------------------------------------------------- + terrain_t *K_GetTerrainByIndex(size_t checkIndex) + + See header file for description. +--------------------------------------------------*/ +terrain_t *K_GetTerrainByIndex(size_t checkIndex) { if (checkIndex >= numTerrainDefs) { @@ -72,18 +218,21 @@ terrain_t *K_GetTerrainByIndex(UINT16 checkIndex) return &terrainDefs[checkIndex]; } +/*-------------------------------------------------- + terrain_t *K_GetTerrainByName(const char *checkName) + + See header file for description. +--------------------------------------------------*/ terrain_t *K_GetTerrainByName(const char *checkName) { - INT32 i; + size_t i; if (numTerrainDefs == 0) { return NULL; } - // Search backwards through all terrain definitions. - // The latest one will have priority over the older one. - for (i = numTerrainDefs-1; i >= 0; i--) + for (i = 0; i < numTerrainDefs; i++) { terrain_t *t = &terrainDefs[i]; @@ -97,23 +246,30 @@ terrain_t *K_GetTerrainByName(const char *checkName) return NULL; } +/*-------------------------------------------------- + terrain_t *K_GetDefaultTerrain(void) + + See header file for description. +--------------------------------------------------*/ terrain_t *K_GetDefaultTerrain(void) { return K_GetTerrainByIndex(defaultTerrain); } +/*-------------------------------------------------- + terrain_t *K_GetTerrainForTextureName(const char *checkName) + + See header file for description. +--------------------------------------------------*/ terrain_t *K_GetTerrainForTextureName(const char *checkName) { - INT32 i; + size_t i; if (numTerrainFloorDefs == 0) { return NULL; } - // Search backwards through all terrain definitions. - // The latest one will have priority over the older one. - for (i = 0; i < numTerrainFloorDefs; i++) { t_floor_t *f = &terrainFloorDefs[i]; @@ -129,6 +285,11 @@ terrain_t *K_GetTerrainForTextureName(const char *checkName) return K_GetDefaultTerrain(); } +/*-------------------------------------------------- + terrain_t *K_GetTerrainForTextureNum(INT32 textureNum) + + See header file for description. +--------------------------------------------------*/ terrain_t *K_GetTerrainForTextureNum(INT32 textureNum) { texture_t *tex = NULL; @@ -142,6 +303,11 @@ terrain_t *K_GetTerrainForTextureNum(INT32 textureNum) return K_GetTerrainForTextureName(tex->name); } +/*-------------------------------------------------- + terrain_t *K_GetTerrainForFlatNum(INT32 flatID) + + See header file for description. +--------------------------------------------------*/ terrain_t *K_GetTerrainForFlatNum(INT32 flatID) { levelflat_t *levelFlat = NULL; @@ -156,6 +322,11 @@ terrain_t *K_GetTerrainForFlatNum(INT32 flatID) return K_GetTerrainForTextureName(levelFlat->name); } +/*-------------------------------------------------- + void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID) + + See header file for description. +--------------------------------------------------*/ void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID) { if (mo == NULL || P_MobjWasRemoved(mo) == true) @@ -175,6 +346,11 @@ void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID) mo->terrain = K_GetTerrainForFlatNum(flatID); } +/*-------------------------------------------------- + void K_ProcessTerrainEffect(mobj_t *mo) + + See header file for description. +--------------------------------------------------*/ void K_ProcessTerrainEffect(mobj_t *mo) { player_t *player = NULL; @@ -242,17 +418,26 @@ void K_ProcessTerrainEffect(mobj_t *mo) // (Offroad is handled elsewhere!) } +/*-------------------------------------------------- + void K_SetDefaultFriction(mobj_t *mo) + + See header file for description. +--------------------------------------------------*/ void K_SetDefaultFriction(mobj_t *mo) { + boolean isPlayer = false; + if (mo == NULL || P_MobjWasRemoved(mo) == true) { // Invalid object. return; } + isPlayer = (mo->player != NULL); + mo->friction = ORIG_FRICTION; - if (mo->player != NULL) + if (isPlayer == true) { mo->movefactor = FRACUNIT; } @@ -266,7 +451,7 @@ void K_SetDefaultFriction(mobj_t *mo) if (strength > 0) // sludge { - strength = strength*2; // otherwise, the maximum sludginess value is +967... + strength = strength * 2; // otherwise, the maximum sludginess value is +967... } // The following might seem odd. At the time of movement, @@ -284,26 +469,40 @@ void K_SetDefaultFriction(mobj_t *mo) newFriction = 0; } - newMovefactor = FixedDiv(ORIG_FRICTION, newFriction); - - if (newMovefactor < FRACUNIT) - { - newMovefactor = 19*newMovefactor - 18*FRACUNIT; - } - else - { - newMovefactor = FRACUNIT; - } - mo->friction = newFriction; - mo->movefactor = newMovefactor; + + if (isPlayer == true) + { + newMovefactor = FixedDiv(ORIG_FRICTION, newFriction); + + if (newMovefactor < FRACUNIT) + { + newMovefactor = 19*newMovefactor - 18*FRACUNIT; + } + else + { + newMovefactor = FRACUNIT; + } + + mo->movefactor = newMovefactor; + } } } -// -// Parser code starts here. -// +/*-------------------------------------------------- + static void K_FlagBoolean(UINT32 *inputFlags, UINT32 newFlag, char *val) + Sets a flag to true or false depending on + the string input. + + Input Arguments:- + inputFlags - Pointer to flags value to modify. + newFlag - The flag(s) to set / unset. + val - The string input from the file. + + Return:- + None +--------------------------------------------------*/ static void K_FlagBoolean(UINT32 *inputFlags, UINT32 newFlag, char *val) { if (stricmp(val, "true") == 0) @@ -316,10 +515,147 @@ static void K_FlagBoolean(UINT32 *inputFlags, UINT32 newFlag, char *val) } } +/*-------------------------------------------------- + static void K_SplashDefaults(t_splash_t *splash) + + Sets the defaults for a new Splash block. + + Input Arguments:- + splash - Terrain Splash structure to default. + + Return:- + None +--------------------------------------------------*/ +static void K_SplashDefaults(t_splash_t *splash) +{ + splash->mobjType = MT_NULL; + splash->sfx = sfx_None; +} + +/*-------------------------------------------------- + static void K_NewSplashDefs(void) + + Increases the size of splashDefs by 1, and + sets the new struct's values to their defaults. + + Input Arguments:- + None + + Return:- + None +--------------------------------------------------*/ +static void K_NewSplashDefs(void) +{ + numSplashDefs++; + splashDefs = (t_splash_t *)Z_Realloc(splashDefs, sizeof(t_splash_t) * (numSplashDefs + 1), PU_STATIC, NULL); + K_SplashDefaults( &splashDefs[numSplashDefs - 1] ); +} + +/*-------------------------------------------------- + static void K_ParseSplashParameter(size_t i, char *param, char *val) + + Parser function for Splash blocks. + + Input Arguments:- + i - Struct ID + param - Parameter string + val - Value string + + Return:- + None +--------------------------------------------------*/ +static void K_ParseSplashParameter(size_t i, char *param, char *val) +{ + t_splash_t *splash = &splashDefs[i]; + + if (stricmp(param, "mobjType") == 0) + { + splash->mobjType = get_mobjtype(val); + } + else if (stricmp(param, "sfx") == 0) + { + splash->sfx = get_sfx(val); + } +} + +/*-------------------------------------------------- + static void K_FootstepDefaults(t_footstep_t *footstep) + + Sets the defaults for a new Footstep block. + + Input Arguments:- + footstep - Terrain Footstep structure to default. + + Return:- + None +--------------------------------------------------*/ +static void K_FootstepDefaults(t_footstep_t *footstep) +{ + footstep->mobjType = MT_NULL; + footstep->sfx = sfx_None; +} + +/*-------------------------------------------------- + static void K_NewFootstepDefs(void) + + Increases the size of footstepDefs by 1, and + sets the new struct's values to their defaults. + + Input Arguments:- + None + + Return:- + None +--------------------------------------------------*/ +static void K_NewFootstepDefs(void) +{ + numFootstepDefs++; + footstepDefs = (t_footstep_t *)Z_Realloc(footstepDefs, sizeof(t_footstep_t) * (numFootstepDefs + 1), PU_STATIC, NULL); + K_FootstepDefaults( &footstepDefs[numFootstepDefs - 1] ); +} + +/*-------------------------------------------------- + static void K_ParseFootstepParameter(size_t i, char *param, char *val) + + Parser function for Footstep blocks. + + Input Arguments:- + i - Struct ID + param - Parameter string + val - Value string + + Return:- + None +--------------------------------------------------*/ +static void K_ParseFootstepParameter(size_t i, char *param, char *val) +{ + t_footstep_t *footstep = &footstepDefs[i]; + + if (stricmp(param, "mobjType") == 0) + { + footstep->mobjType = get_mobjtype(val); + } + else if (stricmp(param, "sfx") == 0) + { + footstep->sfx = get_sfx(val); + } +} + +/*-------------------------------------------------- + static void K_TerrainDefaults(terrain_t *terrain) + + Sets the defaults for a new Terrain block. + + Input Arguments:- + terrain - Terrain structure to default. + + Return:- + None +--------------------------------------------------*/ static void K_TerrainDefaults(terrain_t *terrain) { - terrain->splashID = UINT16_MAX; - terrain->footstepID = UINT16_MAX; + terrain->splashID = SIZE_MAX; + terrain->footstepID = SIZE_MAX; terrain->friction = FRACUNIT; terrain->offroad = 0; @@ -328,6 +664,18 @@ static void K_TerrainDefaults(terrain_t *terrain) terrain->flags = 0; } +/*-------------------------------------------------- + static void K_NewTerrainDefs(void) + + Increases the size of terrainDefs by 1, and + sets the new struct's values to their defaults. + + Input Arguments:- + None + + Return:- + None +--------------------------------------------------*/ static void K_NewTerrainDefs(void) { numTerrainDefs++; @@ -335,17 +683,32 @@ static void K_NewTerrainDefs(void) K_TerrainDefaults( &terrainDefs[numTerrainDefs - 1] ); } +/*-------------------------------------------------- + static void K_ParseTerrainParameter(UINT32 i, char *param, char *val) + + Parser function for Terrain blocks. + + Input Arguments:- + i - Struct ID + param - Parameter string + val - Value string + + Return:- + None +--------------------------------------------------*/ static void K_ParseTerrainParameter(UINT32 i, char *param, char *val) { terrain_t *terrain = &terrainDefs[i]; if (stricmp(param, "splash") == 0) { - //terrain->splashID = 0; + t_splash_t *splash = K_GetSplashByName(val); + terrain->splashID = K_GetSplashHeapIndex(splash); } else if (stricmp(param, "footstep") == 0) { - //terrain->footstepID = 0; + t_footstep_t *footstep = K_GetFootstepByName(val); + terrain->footstepID = K_GetFootstepHeapIndex(footstep); } else if (stricmp(param, "friction") == 0) { @@ -373,12 +736,37 @@ static void K_ParseTerrainParameter(UINT32 i, char *param, char *val) } } +/*-------------------------------------------------- + static void K_NewTerrainFloorDefs(void) + + Increases the size of numTerrainFloorDefs by 1. + + Input Arguments:- + None + + Return:- + None +--------------------------------------------------*/ static void K_NewTerrainFloorDefs(void) { numTerrainFloorDefs++; terrainFloorDefs = (t_floor_t *)Z_Realloc(terrainFloorDefs, sizeof(t_floor_t) * (numTerrainFloorDefs + 1), PU_STATIC, NULL); } +/*-------------------------------------------------- + static boolean K_DoTERRAINLumpParse(size_t num, void (*parser)(UINT32, char *, char *)) + + Runs another parser function for the TERRAIN + lump, handling the nitty-gritty parts of the + token handling. + + Input Arguments:- + num - Struct ID to modify. Which one it will modify depends on the parser function. + parser - The parser function. Takes three inputs: Struct ID, Parameter String, and Value String. + + Return:- + false if any errors occured, otherwise true. +--------------------------------------------------*/ static boolean K_DoTERRAINLumpParse(size_t num, void (*parser)(UINT32, char *, char *)) { char *param, *val; @@ -414,6 +802,18 @@ static boolean K_DoTERRAINLumpParse(size_t num, void (*parser)(UINT32, char *, c return true; } +/*-------------------------------------------------- + static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) + + Parses inputted lump data as a TERRAIN lump. + + Input Arguments:- + data - Pointer to lump data. + size - The length of the lump data. + + Return:- + false if any errors occured, otherwise true. +--------------------------------------------------*/ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) { char *tkn = M_GetToken((char *)data); @@ -431,6 +831,80 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) valid = false; } // Check for valid fields. + else if (stricmp(tkn, "splash") == 0) + { + Z_Free(tkn); + tkn = M_GetToken(NULL); + pos = M_GetTokenPos(); + + if (tkn && pos < size) + { + t_splash_t *s = NULL; + + for (i = 0; i < numSplashDefs; i++) + { + s = &splashDefs[i]; + + if (stricmp(tkn, s->name) == 0) + { + break; + } + } + + if (i == numSplashDefs) + { + K_NewSplashDefs(); + s = &splashDefs[i]; + + strncpy(s->name, tkn, TERRAIN_NAME_LEN); + CONS_Printf("Created new Splash type '%s'\n", s->name); + } + + valid = K_DoTERRAINLumpParse(i, K_ParseSplashParameter); + } + else + { + CONS_Alert(CONS_ERROR, "No Splash type name.\n"); + valid = false; + } + } + else if (stricmp(tkn, "footstep") == 0) + { + Z_Free(tkn); + tkn = M_GetToken(NULL); + pos = M_GetTokenPos(); + + if (tkn && pos < size) + { + t_footstep_t *fs = NULL; + + for (i = 0; i < numFootstepDefs; i++) + { + fs = &footstepDefs[i]; + + if (stricmp(tkn, fs->name) == 0) + { + break; + } + } + + if (i == numFootstepDefs) + { + K_NewFootstepDefs(); + fs = &footstepDefs[i]; + + strncpy(fs->name, tkn, TERRAIN_NAME_LEN); + CONS_Printf("Created new Footstep type '%s'\n", fs->name); + } + + valid = K_DoTERRAINLumpParse(i, K_ParseFootstepParameter); + } + else + { + CONS_Alert(CONS_ERROR, "No Footstep type name.\n"); + valid = false; + } + } else if (stricmp(tkn, "terrain") == 0) { Z_Free(tkn); @@ -464,7 +938,7 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) } else { - CONS_Alert(CONS_ERROR, "No terrain type name.\n"); + CONS_Alert(CONS_ERROR, "No Terrain type name.\n"); valid = false; } } @@ -522,7 +996,7 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) } else { - f->terrainID = (t - terrainDefs); + f->terrainID = K_GetTerrainHeapIndex(t); CONS_Printf("Texture '%s' set to Terrain '%s'\n", f->textureName, tkn); } } @@ -580,7 +1054,6 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) valid = false; } } - // TODO: splash & footstep blocks else { CONS_Alert(CONS_ERROR, "Unknown field '%s' found in TERRAIN lump.\n", tkn); @@ -601,6 +1074,11 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) return true; } +/*-------------------------------------------------- + void K_InitTerrain(UINT16 wadNum) + + See header file for description. +--------------------------------------------------*/ void K_InitTerrain(UINT16 wadNum) { UINT16 lumpNum; diff --git a/src/k_terrain.h b/src/k_terrain.h index c5bf51168..0e36388ea 100644 --- a/src/k_terrain.h +++ b/src/k_terrain.h @@ -29,8 +29,8 @@ typedef struct t_splash_s char name[TERRAIN_NAME_LEN]; // Lookup name. - UINT16 objType; // Thing type. MT_NULL to not spawn anything. - UINT16 sound; // Sound to play. + UINT16 mobjType; // Thing type. MT_NULL to not spawn anything. + UINT16 sfx; // Sound to play. } t_splash_t; typedef struct t_footstep_s @@ -40,8 +40,8 @@ typedef struct t_footstep_s char name[TERRAIN_NAME_LEN]; // Lookup name. - UINT16 objType; // Thing type. MT_NULL to not spawn anything. - UINT16 sound; // Sound to play. + UINT16 mobjType; // Thing type. MT_NULL to not spawn anything. + UINT16 sfx; // Sound to play. } t_footstep_t; typedef enum @@ -58,8 +58,8 @@ typedef struct terrain_s char name[TERRAIN_NAME_LEN]; // Lookup name. - UINT16 splashID; // Splash defintion ID. - UINT16 footstepID; // Footstep defintion ID. + size_t splashID; // Splash defintion ID. + size_t footstepID; // Footstep defintion ID. fixed_t friction; // The default friction of this texture. UINT8 offroad; // The default offroad level of this texture. @@ -78,37 +78,329 @@ typedef struct t_floor_s // Someone confirm if I just hallucinated that. :V) char textureName[9]; // Floor texture name. - UINT16 terrainID; // Terrain definition ID. + size_t terrainID; // Terrain definition ID. } t_floor_t; -// Arrays for all terrain definitions. -extern t_splash_t *splashDefs; -extern UINT16 numSplashDefs; +/*-------------------------------------------------- + size_t K_GetSplashHeapIndex(t_splash_t *splash); -extern t_footstep_t *footstepDefs; -extern UINT16 numFootstepDefs; + Returns a splash defintion's index in the + splash definition heap. -extern terrain_t *terrainDefs; -extern UINT16 numTerrainDefs; + Input Arguments:- + splash - The splash definition to return the index of. -extern t_floor_t *terrainFloorDefs; -extern UINT16 numTerrainFloorDefs; + Return:- + The splash heap index, SIZE_MAX if the splash was invalid. +--------------------------------------------------*/ -// Default terrain definition ID. -extern UINT16 defaultTerrain; +size_t K_GetSplashHeapIndex(t_splash_t *splash); + + +/*-------------------------------------------------- + size_t K_GetNumSplashDefs(void); + + Returns the number of splash definitions. + + Input Arguments:- + None + + Return:- + Length of splashDefs. +--------------------------------------------------*/ + +size_t K_GetNumSplashDefs(void); + + +/*-------------------------------------------------- + t_splash_t *K_GetSplashByIndex(size_t checkIndex); + + Retrieves a splash definition by its heap index. + + Input Arguments:- + checkIndex - The heap index to retrieve. + + Return:- + The splash definition, NULL if it didn't exist. +--------------------------------------------------*/ + +t_splash_t *K_GetSplashByIndex(size_t checkIndex); + + +/*-------------------------------------------------- + t_splash_t *K_GetSplashByName(const char *checkName); + + Retrieves a splash definition by its lookup name. + + Input Arguments:- + checkName - The lookup name to retrieve. + + Return:- + The splash definition, NULL if it didn't exist. +--------------------------------------------------*/ + +t_splash_t *K_GetSplashByName(const char *checkName); + + +/*-------------------------------------------------- + size_t K_GetFootstepHeapIndex(t_footstep_t *footstep); + + Returns a footstep defintion's index in the + footstep definition heap. + + Input Arguments:- + footstep - The footstep definition to return the index of. + + Return:- + The footstep heap index, SIZE_MAX if the footstep was invalid. +--------------------------------------------------*/ + +size_t K_GetFootstepHeapIndex(t_footstep_t *footstep); + + +/*-------------------------------------------------- + size_t K_GetNumFootstepDefs(void); + + Returns the number of footstep definitions. + + Input Arguments:- + None + + Return:- + Length of footstepDefs. +--------------------------------------------------*/ + +size_t K_GetNumFootstepDefs(void); + + +/*-------------------------------------------------- + t_footstep_t *K_GetFootstepByIndex(size_t checkIndex); + + Retrieves a footstep definition by its heap index. + + Input Arguments:- + checkIndex - The heap index to retrieve. + + Return:- + The footstep definition, NULL if it didn't exist. +--------------------------------------------------*/ + +t_footstep_t *K_GetFootstepByIndex(size_t checkIndex); + + +/*-------------------------------------------------- + t_footstep_t *K_GetFootstepByName(const char *checkName); + + Retrieves a footstep definition by its lookup name. + + Input Arguments:- + checkName - The lookup name to retrieve. + + Return:- + The footstep definition, NULL if it didn't exist. +--------------------------------------------------*/ + +t_footstep_t *K_GetFootstepByName(const char *checkName); + + +/*-------------------------------------------------- + size_t K_GetTerrainHeapIndex(terrain_t *terrain); + + Returns a terrain defintion's index in the + terrain definition heap. + + Input Arguments:- + terrain - The terrain definition to return the index of. + + Return:- + The terrain heap index, SIZE_MAX if the terrain was invalid. +--------------------------------------------------*/ size_t K_GetTerrainHeapIndex(terrain_t *terrain); -terrain_t *K_GetTerrainByIndex(UINT16 checkIndex); + + +/*-------------------------------------------------- + size_t K_GetNumTerrainDefs(void); + + Returns the number of terrain definitions. + + Input Arguments:- + None + + Return:- + Length of terrainDefs. +--------------------------------------------------*/ + +size_t K_GetNumTerrainDefs(void); + + +/*-------------------------------------------------- + terrain_t *K_GetTerrainByIndex(size_t checkIndex); + + Retrieves a terrain definition by its heap index. + + Input Arguments:- + checkIndex - The heap index to retrieve. + + Return:- + The terrain definition, NULL if it didn't exist. +--------------------------------------------------*/ + +terrain_t *K_GetTerrainByIndex(size_t checkIndex); + + +/*-------------------------------------------------- + terrain_t *K_GetTerrainByName(const char *checkName); + + Retrieves a terrain definition by its lookup name. + + Input Arguments:- + checkName - The lookup name to retrieve. + + Return:- + The terrain definition, NULL if it didn't exist. +--------------------------------------------------*/ + terrain_t *K_GetTerrainByName(const char *checkName); + +/*-------------------------------------------------- + terrain_t *K_GetDefaultTerrain(void); + + Returns the default terrain definition, used + in cases where terrain is not set for a texture. + + Input Arguments:- + None + + Return:- + The default terrain definition, NULL if it didn't exist. +--------------------------------------------------*/ + terrain_t *K_GetDefaultTerrain(void); + + +/*-------------------------------------------------- + terrain_t *K_GetTerrainForTextureName(const char *checkName); + + Returns the terrain definition applied to + the texture name inputted. + + Input Arguments:- + checkName - The texture's name. + + Return:- + The texture's terrain definition if it exists, + otherwise the default terrain if it exists, + otherwise NULL. +--------------------------------------------------*/ + terrain_t *K_GetTerrainForTextureName(const char *checkName); + + +/*-------------------------------------------------- + terrain_t *K_GetTerrainForTextureNum(INT32 textureNum); + + Returns the terrain definition applied to + the texture ID inputted. + + Input Arguments:- + textureNum - The texture's ID. + + Return:- + The texture's terrain definition if it exists, + otherwise the default terrain if it exists, + otherwise NULL. +--------------------------------------------------*/ + terrain_t *K_GetTerrainForTextureNum(INT32 textureNum); + + +/*-------------------------------------------------- + terrain_t *K_GetTerrainForFlatNum(INT32 flatID); + + Returns the terrain definition applied to + the level flat ID. + + Input Arguments:- + flatID - The level flat's ID. + + Return:- + The level flat's terrain definition if it exists, + otherwise the default terrain if it exists, + otherwise NULL. +--------------------------------------------------*/ + terrain_t *K_GetTerrainForFlatNum(INT32 flatID); + +/*-------------------------------------------------- + void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID); + + Updates an object's terrain pointer, based on + the level flat ID supplied. Intended to be called + when the object moves to new floors. + + Input Arguments:- + mo - The object to update. + flatID - The level flat ID the object is standing on. + + Return:- + None +--------------------------------------------------*/ + void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID); + + +/*-------------------------------------------------- + void K_ProcessTerrainEffect(mobj_t *mo); + + Handles applying terrain effects to the object, + intended to be called in a thinker. + + Currently only intended for players, but + could be modified to be inclusive of all + object types. + + Input Arguments:- + mo - The object to apply effects to. + + Return:- + None +--------------------------------------------------*/ + void K_ProcessTerrainEffect(mobj_t *mo); + + +/*-------------------------------------------------- + void K_SetDefaultFriction(mobj_t *mo); + + Resets an object to their default friction values. + If they are on terrain with different friction, + they will update to that value. + + Input Arguments:- + mo - The object to reset the friction values of. + + Return:- + None +--------------------------------------------------*/ + void K_SetDefaultFriction(mobj_t *mo); + +/*-------------------------------------------------- + void K_InitTerrain(UINT16 wadNum); + + Finds the TERRAIN lumps in a WAD/PK3, and + processes all of them. + + Input Arguments:- + wadNum - WAD file ID to process. + + Return:- + None +--------------------------------------------------*/ + void K_InitTerrain(UINT16 wadNum); #endif // __K_TERRAIN_H__ diff --git a/src/p_user.c b/src/p_user.c index 1a78e8b62..4faa84b16 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2138,8 +2138,6 @@ void P_MovePlayer(player_t *player) player->mo->rollangle = 0; } - player->mo->movefactor = FRACUNIT; // We're not going to do any more with this, so let's change it back for the next frame. - //{ SRB2kart // Drifting sound @@ -4504,8 +4502,6 @@ void P_PlayerThink(player_t *player) P_MovePlayer(player); } - player->mo->movefactor = FRACUNIT; // We're not going to do any more with this, so let's change it back for the next frame. - // Unset statis flag after moving. // In other words, if you manually set stasis via code, // it lasts for one tic. From 867ddb143d85b2bb66e5db0c13d6ab65367542a2 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 9 Dec 2021 15:54:22 -0500 Subject: [PATCH 105/379] Instead of embedding specific textures to be tripwire in hardcode ... make it a TERRAIN flag! --- src/k_kart.c | 11 +++++------ src/k_terrain.c | 12 ++++++++++-- src/k_terrain.h | 4 +++- src/lua_playerlib.c | 4 ++-- src/p_setup.c | 8 +++----- src/p_spec.c | 2 +- 6 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index a2edc297f..9fdc98f6f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5086,7 +5086,7 @@ void K_DoSneaker(player_t *player, INT32 type) { const fixed_t intendedboost = FRACUNIT/2; - if (!player->floorboost || player->floorboost == 3) + if (player->floorboost == 0 || player->floorboost == 3) { const sfxenum_t normalsfx = sfx_cdfm01; const sfxenum_t smallsfx = sfx_cdfm40; @@ -5109,7 +5109,7 @@ void K_DoSneaker(player_t *player, INT32 type) player->numsneakers++; } - if (!player->sneakertimer) + if (player->sneakertimer == 0) { if (type == 2) { @@ -5143,13 +5143,12 @@ void K_DoSneaker(player_t *player, INT32 type) { player->pflags |= PF_ATTACKDOWN; K_PlayBoostTaunt(player->mo); - } player->sneakertimer = sneakertime; // set angle for spun out players: - player->boostangle = (INT32)player->mo->angle; + player->boostangle = player->mo->angle; } static void K_DoShrink(player_t *user) @@ -6675,7 +6674,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) // update boost angle if not spun out if (!player->spinouttimer && !player->wipeoutslow) - player->boostangle = (INT32)player->mo->angle; + player->boostangle = player->mo->angle; K_GetKartBoostPower(player); @@ -6948,7 +6947,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->sneakertimer && player->wipeoutslow > 0 && player->wipeoutslow < wipeoutslowtime+1) player->wipeoutslow = wipeoutslowtime+1; - if (player->floorboost) + if (player->floorboost > 0) player->floorboost--; if (player->driftboost) diff --git a/src/k_terrain.c b/src/k_terrain.c index 9ba13757d..dfd059893 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -274,7 +274,7 @@ terrain_t *K_GetTerrainForTextureName(const char *checkName) { t_floor_t *f = &terrainFloorDefs[i]; - if (stricmp(checkName, f->textureName) == 0) + if (strncasecmp(checkName, f->textureName, 8) == 0) { return K_GetTerrainByIndex(f->terrainID); } @@ -387,7 +387,7 @@ void K_ProcessTerrainEffect(mobj_t *mo) // Sneaker panel if (terrain->flags & TRF_SNEAKERPANEL) { - if (!player->floorboost) + if (player->floorboost == 0) player->floorboost = 3; else player->floorboost = 2; @@ -734,6 +734,14 @@ static void K_ParseTerrainParameter(UINT32 i, char *param, char *val) { K_FlagBoolean(&terrain->flags, TRF_SNEAKERPANEL, val); } + else if (stricmp(param, "bumpy") == 0 || stricmp(param, "stairJank") == 0) + { + K_FlagBoolean(&terrain->flags, TRF_STAIRJANK, val); + } + else if (stricmp(param, "tripwire") == 0) + { + K_FlagBoolean(&terrain->flags, TRF_TRIPWIRE, val); + } } /*-------------------------------------------------- diff --git a/src/k_terrain.h b/src/k_terrain.h index 0e36388ea..e53a72358 100644 --- a/src/k_terrain.h +++ b/src/k_terrain.h @@ -48,7 +48,9 @@ typedef enum { // Terrain flag values. TRF_LIQUID = 1, // Texture water properties (wavy, slippery, etc) - TRF_SNEAKERPANEL = 1<<1 // Texture is a booster + TRF_SNEAKERPANEL = 1<<1, // Texture is a booster + TRF_STAIRJANK = 1<<2, // Texture is bumpy road + TRF_TRIPWIRE = 1<<3 // Texture is a tripwire when used as a midtexture } terrain_flags_t; typedef struct terrain_s diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index f7c20ab64..23e2e2b67 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -281,7 +281,7 @@ static int player_get(lua_State *L) else if (fastcmp(field,"handleboost")) lua_pushinteger(L, plr->handleboost); else if (fastcmp(field,"boostangle")) - lua_pushinteger(L, plr->boostangle); + lua_pushangle(L, plr->boostangle); else if (fastcmp(field,"draftpower")) lua_pushinteger(L, plr->draftpower); else if (fastcmp(field,"draftleeway")) @@ -626,7 +626,7 @@ static int player_set(lua_State *L) else if (fastcmp(field,"handleboost")) plr->handleboost = luaL_checkinteger(L, 3); else if (fastcmp(field,"boostangle")) - plr->boostangle = luaL_checkinteger(L, 3); + plr->boostangle = luaL_checkangle(L, 3); else if (fastcmp(field,"draftpower")) plr->draftpower = luaL_checkinteger(L, 3); else if (fastcmp(field,"draftleeway")) diff --git a/src/p_setup.c b/src/p_setup.c index a60c4399d..8b6133e48 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -92,6 +92,7 @@ #include "k_bot.h" #include "k_grandprix.h" #include "k_brightmap.h" +#include "k_terrain.h" // TRF_TRIPWIRE // Replay names have time #if !defined (UNDER_CE) @@ -1940,18 +1941,15 @@ static void P_ProcessLinedefsAfterSidedefs(void) size_t i = numlines; register line_t *ld = lines; - const INT32 TEX_TRIPWIRE = R_TextureNumForName("TRIPWIRE"); - const INT32 TEX_4RIPWIRE = R_TextureNumForName("4RIPWIRE"); - for (; i--; ld++) { INT32 midtexture = sides[ld->sidenum[0]].midtexture; + terrain_t *terrain = K_GetTerrainForTextureNum(midtexture); ld->frontsector = sides[ld->sidenum[0]].sector; //e6y: Can't be -1 here ld->backsector = ld->sidenum[1] != 0xffff ? sides[ld->sidenum[1]].sector : 0; - if (midtexture == TEX_TRIPWIRE || - midtexture == TEX_4RIPWIRE) + if (terrain != NULL && (terrain->flags & TRF_TRIPWIRE)) { ld->tripwire = true; } diff --git a/src/p_spec.c b/src/p_spec.c index b58b8aa40..fca9e9035 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4660,7 +4660,7 @@ DoneSection2: case 6: // SRB2kart 190117 - Sneaker Panel if (roversector || P_MobjReadyToTrigger(player->mo, sector)) { - if (!player->floorboost) + if (player->floorboost == 0) player->floorboost = 3; else player->floorboost = 2; From 1e0f9faf1901fc6f3aa58b798d181e5b89094c89 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 9 Dec 2021 16:12:09 -0500 Subject: [PATCH 106/379] Implement bumpy floor --- src/k_terrain.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/k_terrain.c b/src/k_terrain.c index dfd059893..1ad7e45a3 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -415,6 +415,26 @@ void K_ProcessTerrainEffect(mobj_t *mo) P_InstaThrust(mo, mo->angle, speed); } + // Bumpy floor + if (terrain->flags & TRF_STAIRJANK) + { + /* use a shorter sound if not two tics have passed + * since the last step */ + S_StartSound(mo, player->stairjank + >= 16 ? sfx_s23b : sfx_s268); + + if (player->stairjank == 0) + { + mobj_t *spark = P_SpawnMobjFromMobj(mo, + 0, 0, 0, MT_JANKSPARK); + spark->fuse = 9; + spark->cusval = K_StairJankFlip(ANGLE_90); + P_SetTarget(&spark->target, mo); + } + + player->stairjank = 17; + } + // (Offroad is handled elsewhere!) } From ee8f7468bb0655a90a58f71725a7ffa1b9e66767 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 9 Dec 2021 16:32:31 -0500 Subject: [PATCH 107/379] Allow "texture" as an alias to "floor" Since we I'm using it for Tripwire too --- src/k_terrain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index 1ad7e45a3..9cfd399d0 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -970,7 +970,7 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) valid = false; } } - else if (stricmp(tkn, "floor") == 0) + else if (stricmp(tkn, "floor") == 0 || stricmp(tkn, "texture") == 0) { Z_Free(tkn); tkn = M_GetToken(NULL); From 80acc8fe3f209f9c3122adebe15d538013e0752e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 18 Dec 2021 18:30:19 -0500 Subject: [PATCH 108/379] Add scale & color parameters to terrain particles --- src/k_terrain.c | 16 ++++++++++++++++ src/k_terrain.h | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/src/k_terrain.c b/src/k_terrain.c index 9cfd399d0..c2022d1b9 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -596,6 +596,14 @@ static void K_ParseSplashParameter(size_t i, char *param, char *val) { splash->sfx = get_sfx(val); } + else if (stricmp(param, "scale") == 0) + { + splash->scale = FLOAT_TO_FIXED(atof(val)); + } + else if (stricmp(param, "color") == 0) + { + splash->color = get_skincolor(val); + } } /*-------------------------------------------------- @@ -659,6 +667,14 @@ static void K_ParseFootstepParameter(size_t i, char *param, char *val) { footstep->sfx = get_sfx(val); } + else if (stricmp(param, "scale") == 0) + { + footstep->scale = FLOAT_TO_FIXED(atof(val)); + } + else if (stricmp(param, "color") == 0) + { + footstep->color = get_skincolor(val); + } } /*-------------------------------------------------- diff --git a/src/k_terrain.h b/src/k_terrain.h index e53a72358..d494f5edf 100644 --- a/src/k_terrain.h +++ b/src/k_terrain.h @@ -31,6 +31,8 @@ typedef struct t_splash_s UINT16 mobjType; // Thing type. MT_NULL to not spawn anything. UINT16 sfx; // Sound to play. + fixed_t scale; // Thing scale multiplier. + UINT16 color; // Colorize effect. SKINCOLOR_NONE has no colorize. } t_splash_t; typedef struct t_footstep_s @@ -42,6 +44,8 @@ typedef struct t_footstep_s UINT16 mobjType; // Thing type. MT_NULL to not spawn anything. UINT16 sfx; // Sound to play. + fixed_t scale; // Thing scale multiplier. + UINT16 color; // Colorize effect. SKINCOLOR_NONE has no colorize. } t_footstep_t; typedef enum From 97b45612df0ffec3cdc55221c953d28c43c129c6 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 18 Dec 2021 21:23:41 -0500 Subject: [PATCH 109/379] Add footstep particles --- src/k_kart.c | 20 ++---- src/k_kart.h | 2 +- src/k_terrain.c | 164 ++++++++++++++++++++++++++++++++++++++++++++-- src/k_terrain.h | 16 +++++ src/lua_baselib.c | 3 +- src/p_user.c | 2 +- 6 files changed, 181 insertions(+), 26 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 9fdc98f6f..53426531a 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4357,7 +4357,7 @@ void K_SpawnSparkleTrail(mobj_t *mo) sparkle->color = mo->color; } -void K_SpawnWipeoutTrail(mobj_t *mo, boolean offroad) +void K_SpawnWipeoutTrail(mobj_t *mo) { mobj_t *dust; angle_t aoff; @@ -4384,13 +4384,6 @@ void K_SpawnWipeoutTrail(mobj_t *mo, boolean offroad) dust->destscale = mo->scale; P_SetScale(dust, mo->scale); K_FlipFromObject(dust, mo); - - if (offroad) // offroad effect - { - dust->momx = mo->momx/2; - dust->momy = mo->momy/2; - dust->momz = P_GetMobjZMovement(mo)/2; - } } void K_SpawnDraftDust(mobj_t *mo) @@ -6722,16 +6715,11 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) ghost->renderflags |= RF_DONTDRAW; } + // Could probably be moved somewhere else. + K_HandleFootstepParticles(player->mo); + if (P_IsObjectOnGround(player->mo)) { - // Offroad dust - if (player->boostpower < FRACUNIT) - { - K_SpawnWipeoutTrail(player->mo, true); - if (leveltime % 6 == 0) - S_StartSound(player->mo, sfx_cdfm70); - } - // Draft dust if (player->draftpower >= FRACUNIT) { diff --git a/src/k_kart.h b/src/k_kart.h index dffc1cedf..04d80fd05 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -76,7 +76,7 @@ void K_RunFinishLineBeam(void); UINT16 K_DriftSparkColor(player_t *player, INT32 charge); void K_SpawnBoostTrail(player_t *player); void K_SpawnSparkleTrail(mobj_t *mo); -void K_SpawnWipeoutTrail(mobj_t *mo, boolean offroad); +void K_SpawnWipeoutTrail(mobj_t *mo); void K_SpawnDraftDust(mobj_t *mo); void K_DriftDustHandling(mobj_t *spawner); void K_Squish(mobj_t *mo); diff --git a/src/k_terrain.c b/src/k_terrain.c index c2022d1b9..4f9f63cbf 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -20,6 +20,7 @@ #include "doomtype.h" #include "fastcmp.h" #include "m_fixed.h" +#include "m_random.h" #include "p_local.h" #include "p_mobj.h" #include "r_textures.h" @@ -41,6 +42,7 @@ static t_floor_t *terrainFloorDefs = NULL; static size_t numTerrainFloorDefs = 0; static size_t defaultTerrain = SIZE_MAX; +static size_t defaultOffroadFootstep = SIZE_MAX; /*-------------------------------------------------- size_t K_GetSplashHeapIndex(t_splash_t *splash) @@ -509,6 +511,114 @@ void K_SetDefaultFriction(mobj_t *mo) } } +/*-------------------------------------------------- + static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) + + See header file for description. +--------------------------------------------------*/ +static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) +{ + mobj_t *dust = NULL; + angle_t pushAngle = ANGLE_MAX; + angle_t tireAngle = ANGLE_MAX; + fixed_t momentum = INT32_MAX; + + if (mo->player != NULL) + { + tireAngle = (mo->player->drawangle + ANGLE_180); + } + else + { + tireAngle = (mo->angle + ANGLE_180); + } + + if ((leveltime / 2) & 1) + { + tireAngle -= ANGLE_45; + tireAngle -= P_RandomRange(0, ANGLE_11hh); + } + else + { + tireAngle += ANGLE_45; + tireAngle += P_RandomRange(0, ANGLE_11hh); + } + + pushAngle = K_MomentumAngle(mo) + ANGLE_180; + + dust = P_SpawnMobjFromMobj( + mo, + (P_RandomRange(-2, 2) * FRACUNIT) + (24 * FINECOSINE(tireAngle >> ANGLETOFINESHIFT)), + (P_RandomRange(-2, 2) * FRACUNIT) + (24 * FINESINE(tireAngle >> ANGLETOFINESHIFT)), + 0, fs->mobjType + ); + + P_SetTarget(&dust->target, mo); + dust->angle = K_MomentumAngle(mo); + + dust->destscale = FixedMul(mo->scale, fs->scale); + P_SetScale(dust, dust->destscale); + + dust->momx = mo->momx; + dust->momy = mo->momy; + dust->momz = mo->momz; + + momentum = P_AproxDistance(mo->momx, mo->momy) / 2; + dust->momx += FixedMul(momentum, FINECOSINE(pushAngle >> ANGLETOFINESHIFT)); + dust->momy += FixedMul(momentum, FINESINE(pushAngle >> ANGLETOFINESHIFT)); + dust->momz += (momentum / 16) * P_MobjFlip(mo); + + if (fs->color != SKINCOLOR_NONE) + { + dust->color = fs->color; + } + + if (fs->sfx != sfx_None && (leveltime % 6 == 0)) + { + S_StartSound(mo, fs->sfx); + } +} + +/*-------------------------------------------------- + void K_HandleFootstepParticles(mobj_t *mo) + + See header file for description. +--------------------------------------------------*/ +void K_HandleFootstepParticles(mobj_t *mo) +{ + t_footstep_t *fs = NULL; + + if (mo == NULL || P_MobjWasRemoved(mo) == true) + { + // Invalid object. + return; + } + + if (mo->terrain == NULL || mo->terrain->footstepID == SIZE_MAX) + { + // If no terrain, check for offroad. + // If we're in offroad, use the default particle. + + if (mo->player != NULL && mo->player->boostpower < FRACUNIT) + { + fs = K_GetFootstepByIndex(defaultOffroadFootstep); + } + } + else + { + fs = K_GetFootstepByIndex(mo->terrain->footstepID); + } + + if (fs == NULL || fs->mobjType == MT_NULL) + { + // No particles to spawn. + return; + } + + // Idea for later: if different spawning styles are desired, + // we can put a switch case here! + K_SpawnFootstepParticle(mo, fs); +} + /*-------------------------------------------------- static void K_FlagBoolean(UINT32 *inputFlags, UINT32 newFlag, char *val) @@ -550,6 +660,8 @@ static void K_SplashDefaults(t_splash_t *splash) { splash->mobjType = MT_NULL; splash->sfx = sfx_None; + splash->scale = FRACUNIT; + splash->color = SKINCOLOR_NONE; } /*-------------------------------------------------- @@ -590,11 +702,11 @@ static void K_ParseSplashParameter(size_t i, char *param, char *val) if (stricmp(param, "mobjType") == 0) { - splash->mobjType = get_mobjtype(val); + splash->mobjType = get_number(val) + 1; } else if (stricmp(param, "sfx") == 0) { - splash->sfx = get_sfx(val); + splash->sfx = get_number(val); } else if (stricmp(param, "scale") == 0) { @@ -602,7 +714,7 @@ static void K_ParseSplashParameter(size_t i, char *param, char *val) } else if (stricmp(param, "color") == 0) { - splash->color = get_skincolor(val); + splash->color = get_number(val); } } @@ -621,6 +733,8 @@ static void K_FootstepDefaults(t_footstep_t *footstep) { footstep->mobjType = MT_NULL; footstep->sfx = sfx_None; + footstep->scale = FRACUNIT; + footstep->color = SKINCOLOR_NONE; } /*-------------------------------------------------- @@ -661,11 +775,11 @@ static void K_ParseFootstepParameter(size_t i, char *param, char *val) if (stricmp(param, "mobjType") == 0) { - footstep->mobjType = get_mobjtype(val); + footstep->mobjType = get_number(val) + 1; } else if (stricmp(param, "sfx") == 0) { - footstep->sfx = get_sfx(val); + footstep->sfx = get_number(val); } else if (stricmp(param, "scale") == 0) { @@ -673,7 +787,7 @@ static void K_ParseFootstepParameter(size_t i, char *param, char *val) } else if (stricmp(param, "color") == 0) { - footstep->color = get_skincolor(val); + footstep->color = get_number(val); } } @@ -1090,6 +1204,7 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) else { defaultTerrain = i; + CONS_Printf("DefaultTerrain set to '%s'\n", tkn); } } else @@ -1098,6 +1213,43 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) valid = false; } } + else if (stricmp(tkn, "defaultOffroadFootstep") == 0) + { + Z_Free(tkn); + tkn = M_GetToken(NULL); + pos = M_GetTokenPos(); + + if (tkn && pos < size) + { + t_footstep_t *fs = NULL; + + for (i = 0; i < numFootstepDefs; i++) + { + fs = &footstepDefs[i]; + + if (stricmp(tkn, fs->name) == 0) + { + break; + } + } + + if (i == numFootstepDefs) + { + CONS_Alert(CONS_ERROR, "Invalid DefaultOffroadFootstep type.\n"); + valid = false; + } + else + { + defaultOffroadFootstep = i; + CONS_Printf("DefaultOffroadFootstep set to '%s'\n", tkn); + } + } + else + { + CONS_Alert(CONS_ERROR, "No DefaultOffroadFootstep type.\n"); + valid = false; + } + } else { CONS_Alert(CONS_ERROR, "Unknown field '%s' found in TERRAIN lump.\n", tkn); diff --git a/src/k_terrain.h b/src/k_terrain.h index d494f5edf..32921d393 100644 --- a/src/k_terrain.h +++ b/src/k_terrain.h @@ -394,6 +394,22 @@ void K_ProcessTerrainEffect(mobj_t *mo); void K_SetDefaultFriction(mobj_t *mo); +/*-------------------------------------------------- + void K_HandleFootstepParticles(mobj_t *mo); + + Spawns the footstep particles for an object's + terrain type. Intended to be called every tic. + + Input Arguments:- + mo - The object to spawn footsteps for. + + Return:- + None +--------------------------------------------------*/ + +void K_HandleFootstepParticles(mobj_t *mo); + + /*-------------------------------------------------- void K_InitTerrain(UINT16 wadNum); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index d9e57ede1..f067aca9a 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -3623,11 +3623,10 @@ static int lib_kSpawnSparkleTrail(lua_State *L) static int lib_kSpawnWipeoutTrail(lua_State *L) { mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); - boolean offroad = lua_optboolean(L, 2); NOHUD if (!mo) return LUA_ErrInvalid(L, "mobj_t"); - K_SpawnWipeoutTrail(mo, offroad); + K_SpawnWipeoutTrail(mo); return 0; } diff --git a/src/p_user.c b/src/p_user.c index 4faa84b16..43ac1e277 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2265,7 +2265,7 @@ void P_MovePlayer(player_t *player) K_SpawnSparkleTrail(player->mo); if (player->wipeoutslow > 1 && (leveltime & 1)) - K_SpawnWipeoutTrail(player->mo, false); + K_SpawnWipeoutTrail(player->mo); K_DriftDustHandling(player->mo); From 895881fc009b24a08c2f031677cf1b8f5356b57d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 18 Dec 2021 23:30:57 -0500 Subject: [PATCH 110/379] Use mobjzmovement for footsteps --- src/k_terrain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index 4f9f63cbf..d94acb886 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -560,7 +560,7 @@ static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) dust->momx = mo->momx; dust->momy = mo->momy; - dust->momz = mo->momz; + dust->momz = P_GetMobjZMovement(mo) / 2; momentum = P_AproxDistance(mo->momx, mo->momy) / 2; dust->momx += FixedMul(momentum, FINECOSINE(pushAngle >> ANGLETOFINESHIFT)); From a7788f1047cbc06c79dcfc44b680fac9b76c39ce Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 19 Dec 2021 00:57:45 -0500 Subject: [PATCH 111/379] Add terrain splashes --- src/k_terrain.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++-- src/k_terrain.h | 29 +++++++++++ src/p_local.h | 2 +- src/p_user.c | 10 ++-- 4 files changed, 156 insertions(+), 10 deletions(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index d94acb886..1bbef1bcb 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -511,6 +511,102 @@ void K_SetDefaultFriction(mobj_t *mo) } } +/*-------------------------------------------------- + static void K_SpawnSplashParticles(mobj_t *mo, t_splash_t *s, fixed_t impact) + + See header file for description. +--------------------------------------------------*/ +static void K_SpawnSplashParticles(mobj_t *mo, t_splash_t *s, fixed_t impact) +{ + const UINT8 numParticles = s->numParticles; + const angle_t particleSpread = ANGLE_MAX / numParticles; + size_t i; + + for (i = 0; i < numParticles; i++) + { + mobj_t *dust = NULL; + angle_t pushAngle = (particleSpread * i); + fixed_t momH = INT32_MAX; + fixed_t momV = INT32_MAX; + + if (numParticles == 1) + { + // Random angle. + pushAngle = P_RandomRange(0, ANGLE_MAX); + } + + dust = P_SpawnMobjFromMobj( + mo, + (12 * FINECOSINE(pushAngle >> ANGLETOFINESHIFT)), + (12 * FINESINE(pushAngle >> ANGLETOFINESHIFT)), + 0, s->mobjType + ); + + P_SetTarget(&dust->target, mo); + dust->angle = pushAngle; + + dust->destscale = FixedMul(mo->scale, s->scale); + P_SetScale(dust, dust->destscale); + + dust->momx = mo->momx / 2; + dust->momy = mo->momy / 2; + dust->momz = 0; + + momH = FixedMul(impact, s->pushH); + momV = FixedMul(impact, s->pushV); + + dust->momx += FixedMul(momH, FINECOSINE(pushAngle >> ANGLETOFINESHIFT)); + dust->momy += FixedMul(momH, FINESINE(pushAngle >> ANGLETOFINESHIFT)); + dust->momz += momV * P_MobjFlip(mo); + + if (s->color != SKINCOLOR_NONE) + { + dust->color = s->color; + } + + if (s->sfx != sfx_None) + { + S_StartSound(mo, s->sfx); + } + } +} + +/*-------------------------------------------------- + void K_SpawnSplashForMobj(mobj_t *mo, fixed_t impact) + + See header file for description. +--------------------------------------------------*/ +void K_SpawnSplashForMobj(mobj_t *mo, fixed_t impact) +{ + t_splash_t *s = NULL; + + if (mo == NULL || P_MobjWasRemoved(mo) == true) + { + // Invalid object. + return; + } + + if (mo->terrain == NULL || mo->terrain->splashID == SIZE_MAX) + { + // No impact for this terrain type. + return; + } + else + { + s = K_GetSplashByIndex(mo->terrain->splashID); + } + + if (s == NULL || s->mobjType == MT_NULL || s->numParticles == 0) + { + // No particles to spawn. + return; + } + + // Idea for later: if different spawning styles are desired, + // we can put a switch case here! + K_SpawnSplashParticles(mo, s, impact); +} + /*-------------------------------------------------- static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) @@ -522,6 +618,8 @@ static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) angle_t pushAngle = ANGLE_MAX; angle_t tireAngle = ANGLE_MAX; fixed_t momentum = INT32_MAX; + fixed_t momH = INT32_MAX; + fixed_t momV = INT32_MAX; if (mo->player != NULL) { @@ -562,17 +660,20 @@ static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) dust->momy = mo->momy; dust->momz = P_GetMobjZMovement(mo) / 2; - momentum = P_AproxDistance(mo->momx, mo->momy) / 2; - dust->momx += FixedMul(momentum, FINECOSINE(pushAngle >> ANGLETOFINESHIFT)); - dust->momy += FixedMul(momentum, FINESINE(pushAngle >> ANGLETOFINESHIFT)); - dust->momz += (momentum / 16) * P_MobjFlip(mo); + momentum = P_AproxDistance(mo->momx, mo->momy); + momH = FixedMul(momentum, fs->pushH); + momV = FixedMul(momentum, fs->pushV); + + dust->momx += FixedMul(momH, FINECOSINE(pushAngle >> ANGLETOFINESHIFT)); + dust->momy += FixedMul(momH, FINESINE(pushAngle >> ANGLETOFINESHIFT)); + dust->momz += (momV / 16) * P_MobjFlip(mo); if (fs->color != SKINCOLOR_NONE) { dust->color = fs->color; } - if (fs->sfx != sfx_None && (leveltime % 6 == 0)) + if ((fs->sfx != sfx_None) && (fs->sfxFreq > 0) && (leveltime % fs->sfxFreq == 0)) { S_StartSound(mo, fs->sfx); } @@ -662,6 +763,13 @@ static void K_SplashDefaults(t_splash_t *splash) splash->sfx = sfx_None; splash->scale = FRACUNIT; splash->color = SKINCOLOR_NONE; + + splash->pushH = FRACUNIT/4; + splash->pushV = FRACUNIT/64; + splash->spread = 2; + splash->cone = ANGLE_11hh; + + splash->numParticles = 8; } /*-------------------------------------------------- @@ -735,6 +843,13 @@ static void K_FootstepDefaults(t_footstep_t *footstep) footstep->sfx = sfx_None; footstep->scale = FRACUNIT; footstep->color = SKINCOLOR_NONE; + + footstep->pushH = FRACUNIT/2; + footstep->pushV = FRACUNIT/32; + footstep->spread = 2; + footstep->cone = ANGLE_11hh; + + footstep->sfxFreq = 6; } /*-------------------------------------------------- diff --git a/src/k_terrain.h b/src/k_terrain.h index 32921d393..e4055be23 100644 --- a/src/k_terrain.h +++ b/src/k_terrain.h @@ -33,6 +33,13 @@ typedef struct t_splash_s UINT16 sfx; // Sound to play. fixed_t scale; // Thing scale multiplier. UINT16 color; // Colorize effect. SKINCOLOR_NONE has no colorize. + + fixed_t pushH; // Push-out horizontal multiplier. + fixed_t pushV; // Push-out vertical multiplier. + fixed_t spread; // Randomized spread distance. + angle_t cone; // Randomized angle of the push-out. + + UINT8 numParticles; // Number of particles to spawn. } t_splash_t; typedef struct t_footstep_s @@ -46,6 +53,13 @@ typedef struct t_footstep_s UINT16 sfx; // Sound to play. fixed_t scale; // Thing scale multiplier. UINT16 color; // Colorize effect. SKINCOLOR_NONE has no colorize. + + fixed_t pushH; // Push-out horizontal multiplier. + fixed_t pushV; // Push-out vertical multiplier. + fixed_t spread; // Randomized spread distance. + angle_t cone; // Randomized angle of the push-out. + + tic_t sfxFreq; // How frequently to play the sound. } t_footstep_t; typedef enum @@ -394,6 +408,21 @@ void K_ProcessTerrainEffect(mobj_t *mo); void K_SetDefaultFriction(mobj_t *mo); +/*-------------------------------------------------- + void K_SpawnSplashForMobj(mobj_t *mo, fixed_t impact); + + Spawns the splash particles for an object's + terrain type. Intended to be called when hitting a floor. + + Input Arguments:- + mo - The object to spawn a splash for. + + Return:- + None +--------------------------------------------------*/ +void K_SpawnSplashForMobj(mobj_t *mo, fixed_t impact); + + /*-------------------------------------------------- void K_HandleFootstepParticles(mobj_t *mo); diff --git a/src/p_local.h b/src/p_local.h index 3f25fbf79..2cb291b11 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -168,7 +168,7 @@ boolean P_IsObjectOnGroundIn(mobj_t *mo, sector_t *sec); boolean P_IsObjectOnRealGround(mobj_t *mo, sector_t *sec); // SRB2Kart #define P_IsObjectFlipped(o) ((o)->eflags & MFE_VERTICALFLIP) boolean P_InQuicksand(mobj_t *mo); -boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff); +boolean P_PlayerHitFloor(player_t *player, boolean fromAir); void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative); void P_RestoreMusic(player_t *player); diff --git a/src/p_user.c b/src/p_user.c index 43ac1e277..f8d556a6b 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -52,6 +52,7 @@ #include "k_respawn.h" #include "k_bot.h" #include "k_grandprix.h" +#include "k_terrain.h" // K_SpawnSplashForMobj #ifdef HW3SOUND #include "hardware/hw3sound.h" @@ -1274,17 +1275,18 @@ void P_DoPlayerExit(player_t *player) // // Handles player hitting floor surface. // Returns whether to clip momz. -boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff) +boolean P_PlayerHitFloor(player_t *player, boolean fromAir) { boolean clipmomz; - (void)dorollstuff; - I_Assert(player->mo != NULL); clipmomz = !(P_CheckDeathPitCollide(player->mo)); - // SRB2Kart: removed lots of really vanilla-specific code here + if (fromAir == true && clipmomz == true) + { + K_SpawnSplashForMobj(player->mo, abs(player->mo->momz)); + } return clipmomz; } From 5429fc5dc6fa1069f5e331c8b78aad3d1de9a262 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 19 Dec 2021 01:09:37 -0500 Subject: [PATCH 112/379] Properly put in the parameters --- src/k_terrain.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/k_terrain.c b/src/k_terrain.c index 1bbef1bcb..8b8fa64df 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -824,6 +824,26 @@ static void K_ParseSplashParameter(size_t i, char *param, char *val) { splash->color = get_number(val); } + else if (stricmp(param, "pushH") == 0) + { + splash->pushH = FLOAT_TO_FIXED(atof(val)); + } + else if (stricmp(param, "pushV") == 0) + { + splash->pushV = FLOAT_TO_FIXED(atof(val)); + } + else if (stricmp(param, "spread") == 0) + { + splash->spread = FLOAT_TO_FIXED(atof(val)); + } + else if (stricmp(param, "cone") == 0) + { + splash->cone = AngleFixed(FLOAT_TO_FIXED(atof(val))); // lol + } + else if (stricmp(param, "numParticles") == 0) + { + splash->numParticles = (UINT8)atoi(val); + } } /*-------------------------------------------------- @@ -904,6 +924,26 @@ static void K_ParseFootstepParameter(size_t i, char *param, char *val) { footstep->color = get_number(val); } + else if (stricmp(param, "pushH") == 0) + { + footstep->pushH = FLOAT_TO_FIXED(atof(val)); + } + else if (stricmp(param, "pushV") == 0) + { + footstep->pushV = FLOAT_TO_FIXED(atof(val)); + } + else if (stricmp(param, "spread") == 0) + { + footstep->spread = FLOAT_TO_FIXED(atof(val)); + } + else if (stricmp(param, "cone") == 0) + { + footstep->cone = AngleFixed(FLOAT_TO_FIXED(atof(val))); // lol + } + else if (stricmp(param, "sfxFreq") == 0) + { + footstep->sfxFreq = (tic_t)atoi(val); + } } /*-------------------------------------------------- From 150f514c80dc1345329e6118d87d2ef01933b8dd Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 23 Dec 2021 03:23:42 -0500 Subject: [PATCH 113/379] Remove off by one """"fix"""" --- src/k_terrain.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index 8b8fa64df..d178b212c 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -810,7 +810,7 @@ static void K_ParseSplashParameter(size_t i, char *param, char *val) if (stricmp(param, "mobjType") == 0) { - splash->mobjType = get_number(val) + 1; + splash->mobjType = get_number(val); } else if (stricmp(param, "sfx") == 0) { @@ -910,7 +910,7 @@ static void K_ParseFootstepParameter(size_t i, char *param, char *val) if (stricmp(param, "mobjType") == 0) { - footstep->mobjType = get_number(val) + 1; + footstep->mobjType = get_number(val); } else if (stricmp(param, "sfx") == 0) { From e365b7f0b37920f28eb2baf25c2ead688fc1911e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 23 Dec 2021 22:48:23 -0500 Subject: [PATCH 114/379] Fix it not using footstep->cone --- src/k_terrain.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index d178b212c..0328d29b3 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -633,12 +633,12 @@ static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) if ((leveltime / 2) & 1) { tireAngle -= ANGLE_45; - tireAngle -= P_RandomRange(0, ANGLE_11hh); + tireAngle -= P_RandomRange(0, footstep->cone); } else { tireAngle += ANGLE_45; - tireAngle += P_RandomRange(0, ANGLE_11hh); + tireAngle += P_RandomRange(0, footstep->cone); } pushAngle = K_MomentumAngle(mo) + ANGLE_180; From fee1ccdc0dddb6140939df9c5594d6bb2f87759b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 23 Dec 2021 22:50:57 -0500 Subject: [PATCH 115/379] Wrong var name oops I autopiloted --- src/k_terrain.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index 0328d29b3..33c7d4cf9 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -633,12 +633,12 @@ static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) if ((leveltime / 2) & 1) { tireAngle -= ANGLE_45; - tireAngle -= P_RandomRange(0, footstep->cone); + tireAngle -= P_RandomRange(0, fs->cone); } else { tireAngle += ANGLE_45; - tireAngle += P_RandomRange(0, footstep->cone); + tireAngle += P_RandomRange(0, fs->cone); } pushAngle = K_MomentumAngle(mo) + ANGLE_180; From 7ce3d5bea0eb5942499b2dcfa038e462731180f7 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 23 Dec 2021 22:58:25 -0500 Subject: [PATCH 116/379] Oops ... can't use raw angles for P_RandomRange anyway. --- src/k_terrain.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index 33c7d4cf9..b50fead33 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -633,12 +633,12 @@ static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) if ((leveltime / 2) & 1) { tireAngle -= ANGLE_45; - tireAngle -= P_RandomRange(0, fs->cone); + tireAngle -= P_RandomRange(0, fs->cone / ANG1) * ANG1; } else { tireAngle += ANGLE_45; - tireAngle += P_RandomRange(0, fs->cone); + tireAngle += P_RandomRange(0, fs->cone / ANG1) * ANG1; } pushAngle = K_MomentumAngle(mo) + ANGLE_180; From 6ef8ddf3eef1bb7c790e87f301d776458e6235f8 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 23 Dec 2021 23:15:36 -0500 Subject: [PATCH 117/379] Let's just convert this in a more sane fashion... --- src/k_terrain.c | 4 ++-- src/tables.c | 10 ++++++++++ src/tables.h | 2 ++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index b50fead33..da5a93f90 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -838,7 +838,7 @@ static void K_ParseSplashParameter(size_t i, char *param, char *val) } else if (stricmp(param, "cone") == 0) { - splash->cone = AngleFixed(FLOAT_TO_FIXED(atof(val))); // lol + splash->cone = FloatToAngle(atof(val)); } else if (stricmp(param, "numParticles") == 0) { @@ -938,7 +938,7 @@ static void K_ParseFootstepParameter(size_t i, char *param, char *val) } else if (stricmp(param, "cone") == 0) { - footstep->cone = AngleFixed(FLOAT_TO_FIXED(atof(val))); // lol + footstep->cone = FloatToAngle(atof(val)); } else if (stricmp(param, "sfxFreq") == 0) { diff --git a/src/tables.c b/src/tables.c index 42ad6a73c..8c4dc50e1 100644 --- a/src/tables.c +++ b/src/tables.c @@ -185,6 +185,16 @@ INT32 AngleDeltaSigned(angle_t a1, angle_t a2) return (INT32)(a1) - (INT32)(a2); } +float AngleToFloat(angle_t x) +{ + return x / (float)ANG1; +} + +angle_t FloatToAngle(float f) +{ + return (angle_t)(f * ANG1); +} + #include "t_ftan.c" #include "t_fsin.c" diff --git a/src/tables.h b/src/tables.h index 5e5b6e57b..e122975e1 100644 --- a/src/tables.h +++ b/src/tables.h @@ -108,6 +108,8 @@ FUNCMATH angle_t FixedAngleC(fixed_t fa, fixed_t factor); // difference between two angle_t FUNCMATH INT32 AngleDelta(angle_t a1, angle_t a2); FUNCMATH INT32 AngleDeltaSigned(angle_t a1, angle_t a2); +FUNCMATH float AngleToFloat(angle_t x); +FUNCMATH angle_t FloatToAngle(float f); /// The FixedAcos function FUNCMATH angle_t FixedAcos(fixed_t x); From 3d6da0ce435db9af4b3363369958a6965d82b9ae Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Dec 2021 09:50:00 -0500 Subject: [PATCH 118/379] Super random push Looks better to me, and makes cone have a more pronounced effect --- src/k_terrain.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index da5a93f90..907c610fa 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -630,19 +630,21 @@ static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) tireAngle = (mo->angle + ANGLE_180); } + pushAngle = K_MomentumAngle(mo) + ANGLE_180; + if ((leveltime / 2) & 1) { tireAngle -= ANGLE_45; tireAngle -= P_RandomRange(0, fs->cone / ANG1) * ANG1; + pushAngle -= P_RandomRange(0, fs->cone / ANG1) * ANG1; } else { tireAngle += ANGLE_45; tireAngle += P_RandomRange(0, fs->cone / ANG1) * ANG1; + pushAngle += P_RandomRange(0, fs->cone / ANG1) * ANG1; } - pushAngle = K_MomentumAngle(mo) + ANGLE_180; - dust = P_SpawnMobjFromMobj( mo, (P_RandomRange(-2, 2) * FRACUNIT) + (24 * FINECOSINE(tireAngle >> ANGLETOFINESHIFT)), From 3ffea1f9dd5711f88191df35b94a68dce5a4fcc6 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 23 Dec 2021 21:08:19 -0800 Subject: [PATCH 119/379] Fix incompatible prototype --- src/k_terrain.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index 907c610fa..8c7a0643d 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -991,7 +991,7 @@ static void K_NewTerrainDefs(void) } /*-------------------------------------------------- - static void K_ParseTerrainParameter(UINT32 i, char *param, char *val) + static void K_ParseTerrainParameter(size_t i, char *param, char *val) Parser function for Terrain blocks. @@ -1003,7 +1003,7 @@ static void K_NewTerrainDefs(void) Return:- None --------------------------------------------------*/ -static void K_ParseTerrainParameter(UINT32 i, char *param, char *val) +static void K_ParseTerrainParameter(size_t i, char *param, char *val) { terrain_t *terrain = &terrainDefs[i]; @@ -1082,7 +1082,7 @@ static void K_NewTerrainFloorDefs(void) Return:- false if any errors occured, otherwise true. --------------------------------------------------*/ -static boolean K_DoTERRAINLumpParse(size_t num, void (*parser)(UINT32, char *, char *)) +static boolean K_DoTERRAINLumpParse(size_t num, void (*parser)(size_t, char *, char *)) { char *param, *val; From b6e935e51daf4222fd2893515bee8c7bcdf8482e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 2 Jan 2022 21:42:20 -0500 Subject: [PATCH 120/379] Add required speed percent --- src/k_terrain.c | 24 +++++++++++++++++++++++- src/k_terrain.h | 2 ++ src/lua_mobjlib.c | 3 --- src/lua_playerlib.c | 5 +---- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index 8c7a0643d..323e55734 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -618,16 +618,29 @@ static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) angle_t pushAngle = ANGLE_MAX; angle_t tireAngle = ANGLE_MAX; fixed_t momentum = INT32_MAX; + fixed_t speedValue = INT32_MAX; fixed_t momH = INT32_MAX; fixed_t momV = INT32_MAX; + momentum = P_AproxDistance(mo->momx, mo->momy); + if (mo->player != NULL) { tireAngle = (mo->player->drawangle + ANGLE_180); + speedValue = K_GetKartSpeedFromStat(mo->player->kartspeed); } else { tireAngle = (mo->angle + ANGLE_180); + speedValue = K_GetKartSpeedFromStat(5); + } + + speedValue = FixedMul(speedValue, mo->scale); + speedValue = FixedMul(speedValue, fs->requiredSpeed); + + if (momentum < speedValue) + { + return; } pushAngle = K_MomentumAngle(mo) + ANGLE_180; @@ -662,7 +675,6 @@ static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) dust->momy = mo->momy; dust->momz = P_GetMobjZMovement(mo) / 2; - momentum = P_AproxDistance(mo->momx, mo->momy); momH = FixedMul(momentum, fs->pushH); momV = FixedMul(momentum, fs->pushV); @@ -872,6 +884,8 @@ static void K_FootstepDefaults(t_footstep_t *footstep) footstep->cone = ANGLE_11hh; footstep->sfxFreq = 6; + footstep->frequency = 1; + footstep->requiredSpeed = 0; } /*-------------------------------------------------- @@ -946,6 +960,14 @@ static void K_ParseFootstepParameter(size_t i, char *param, char *val) { footstep->sfxFreq = (tic_t)atoi(val); } + else if (stricmp(param, "frequency") == 0) + { + footstep->frequency = (tic_t)atoi(val); + } + else if (stricmp(param, "requiredSpeed") == 0) + { + footstep->requiredSpeed = FLOAT_TO_FIXED(atof(val)); + } } /*-------------------------------------------------- diff --git a/src/k_terrain.h b/src/k_terrain.h index e4055be23..924f7a1b1 100644 --- a/src/k_terrain.h +++ b/src/k_terrain.h @@ -60,6 +60,8 @@ typedef struct t_footstep_s angle_t cone; // Randomized angle of the push-out. tic_t sfxFreq; // How frequently to play the sound. + tic_t frequency; // How frequently to spawn the particles. + fixed_t requiredSpeed; // Speed percentage you need to be at to trigger the particles. } t_footstep_t; typedef enum diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 8a4a1e7a5..4c2a4f24d 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -494,9 +494,6 @@ static int mobj_set(lua_State *L) if (hook_cmd_running) return luaL_error(L, "Do not alter mobj_t in CMD building code!"); - if (hook_cmd_running) - return luaL_error(L, "Do not alter mobj_t in BuildCMD code!"); - switch(field) { case mobj_valid: diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 23e2e2b67..17e1a39b1 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -499,9 +499,6 @@ static int player_set(lua_State *L) if (hook_cmd_running) return luaL_error(L, "Do not alter player_t in CMD building code!"); - if (hook_cmd_running) - return luaL_error(L, "Do not alter player_t in BuildCMD code!"); - if (fastcmp(field,"mo")) { mobj_t *newmo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); plr->mo->player = NULL; // remove player pointer from old mobj @@ -861,7 +858,7 @@ static int karthud_set(lua_State *L) if (hud_running) return luaL_error(L, "Do not alter player_t in HUD rendering code!"); if (hook_cmd_running) - return luaL_error(L, "Do not alter player_t in BuildCMD code!"); + return luaL_error(L, "Do not alter player_t in CMD building code!"); karthud[ks] = i; return 0; } From d1a308d38e4478e3cd24c554fa1b0f981dd05d9b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 3 Jan 2022 00:07:46 -0500 Subject: [PATCH 121/379] Implement particle spawn frequency, offset spawning by player num --- src/k_terrain.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index 323e55734..68a78af69 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -612,7 +612,7 @@ void K_SpawnSplashForMobj(mobj_t *mo, fixed_t impact) See header file for description. --------------------------------------------------*/ -static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) +static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs, tic_t timer) { mobj_t *dust = NULL; angle_t pushAngle = ANGLE_MAX; @@ -621,6 +621,13 @@ static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) fixed_t speedValue = INT32_MAX; fixed_t momH = INT32_MAX; fixed_t momV = INT32_MAX; + fixed_t xOff = INT32_MAX; + fixed_t yOff = INT32_MAX; + + if (timer % fs->frequency != 0) + { + return; + } momentum = P_AproxDistance(mo->momx, mo->momy); @@ -645,7 +652,7 @@ static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) pushAngle = K_MomentumAngle(mo) + ANGLE_180; - if ((leveltime / 2) & 1) + if (((timer / fs->frequency) / 2) & 1) { tireAngle -= ANGLE_45; tireAngle -= P_RandomRange(0, fs->cone / ANG1) * ANG1; @@ -658,10 +665,13 @@ static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) pushAngle += P_RandomRange(0, fs->cone / ANG1) * ANG1; } + xOff = P_RandomRange(-fs->spread / FRACUNIT, fs->spread / FRACUNIT) * FRACUNIT; + yOff = P_RandomRange(-fs->spread / FRACUNIT, fs->spread / FRACUNIT) * FRACUNIT; + dust = P_SpawnMobjFromMobj( mo, - (P_RandomRange(-2, 2) * FRACUNIT) + (24 * FINECOSINE(tireAngle >> ANGLETOFINESHIFT)), - (P_RandomRange(-2, 2) * FRACUNIT) + (24 * FINESINE(tireAngle >> ANGLETOFINESHIFT)), + xOff + (24 * FINECOSINE(tireAngle >> ANGLETOFINESHIFT)), + yOff + (24 * FINESINE(tireAngle >> ANGLETOFINESHIFT)), 0, fs->mobjType ); @@ -687,7 +697,7 @@ static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) dust->color = fs->color; } - if ((fs->sfx != sfx_None) && (fs->sfxFreq > 0) && (leveltime % fs->sfxFreq == 0)) + if ((fs->sfx != sfx_None) && (fs->sfxFreq > 0) && (timer % fs->sfxFreq == 0)) { S_StartSound(mo, fs->sfx); } @@ -700,6 +710,7 @@ static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) --------------------------------------------------*/ void K_HandleFootstepParticles(mobj_t *mo) { + tic_t timer = leveltime; t_footstep_t *fs = NULL; if (mo == NULL || P_MobjWasRemoved(mo) == true) @@ -723,15 +734,21 @@ void K_HandleFootstepParticles(mobj_t *mo) fs = K_GetFootstepByIndex(mo->terrain->footstepID); } - if (fs == NULL || fs->mobjType == MT_NULL) + if (fs == NULL || fs->mobjType == MT_NULL || fs->frequency <= 0) { // No particles to spawn. return; } + if (mo->player != NULL) + { + // Offset timer by player ID. + timer += mo->player - players; + } + // Idea for later: if different spawning styles are desired, // we can put a switch case here! - K_SpawnFootstepParticle(mo, fs); + K_SpawnFootstepParticle(mo, fs, timer); } /*-------------------------------------------------- @@ -780,7 +797,7 @@ static void K_SplashDefaults(t_splash_t *splash) splash->pushH = FRACUNIT/4; splash->pushV = FRACUNIT/64; - splash->spread = 2; + splash->spread = 2*FRACUNIT; splash->cone = ANGLE_11hh; splash->numParticles = 8; @@ -880,7 +897,7 @@ static void K_FootstepDefaults(t_footstep_t *footstep) footstep->pushH = FRACUNIT/2; footstep->pushV = FRACUNIT/32; - footstep->spread = 2; + footstep->spread = 2*FRACUNIT; footstep->cone = ANGLE_11hh; footstep->sfxFreq = 6; From a3b4035c6644c31cd2fefb9856be8bd3c2e97ad3 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 3 Jan 2022 00:17:59 -0500 Subject: [PATCH 122/379] Minimum impact amount for splashes --- src/k_terrain.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/k_terrain.c b/src/k_terrain.c index 68a78af69..bae39d216 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -578,6 +578,7 @@ static void K_SpawnSplashParticles(mobj_t *mo, t_splash_t *s, fixed_t impact) --------------------------------------------------*/ void K_SpawnSplashForMobj(mobj_t *mo, fixed_t impact) { + const fixed_t minImpact = 4 * mo->scale; t_splash_t *s = NULL; if (mo == NULL || P_MobjWasRemoved(mo) == true) @@ -602,6 +603,11 @@ void K_SpawnSplashForMobj(mobj_t *mo, fixed_t impact) return; } + if (impact < minImpact) + { + impact = minImpact; + } + // Idea for later: if different spawning styles are desired, // we can put a switch case here! K_SpawnSplashParticles(mo, s, impact); From 269b114e2fc5a0e250ab4335af3536e21c083d57 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 3 Jan 2022 01:40:38 -0500 Subject: [PATCH 123/379] Only do spread if above 0 --- src/k_terrain.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index bae39d216..f5323e94b 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -627,8 +627,8 @@ static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs, tic_t timer) fixed_t speedValue = INT32_MAX; fixed_t momH = INT32_MAX; fixed_t momV = INT32_MAX; - fixed_t xOff = INT32_MAX; - fixed_t yOff = INT32_MAX; + fixed_t xOff = 0; + fixed_t yOff = 0; if (timer % fs->frequency != 0) { @@ -671,8 +671,11 @@ static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs, tic_t timer) pushAngle += P_RandomRange(0, fs->cone / ANG1) * ANG1; } - xOff = P_RandomRange(-fs->spread / FRACUNIT, fs->spread / FRACUNIT) * FRACUNIT; - yOff = P_RandomRange(-fs->spread / FRACUNIT, fs->spread / FRACUNIT) * FRACUNIT; + if (fs->spread > 0) + { + xOff = P_RandomRange(-fs->spread / FRACUNIT, fs->spread / FRACUNIT) * FRACUNIT; + yOff = P_RandomRange(-fs->spread / FRACUNIT, fs->spread / FRACUNIT) * FRACUNIT; + } dust = P_SpawnMobjFromMobj( mo, From 693c25310d9ac83b752b34b7fc4883e5a2ac13d3 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 4 Jan 2022 12:45:20 -0500 Subject: [PATCH 124/379] NULL terrain for spectators --- src/k_terrain.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/k_terrain.c b/src/k_terrain.c index f5323e94b..e03ee2059 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -344,6 +344,13 @@ void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID) return; } + if (mo->player != NULL && mo->player->spectator == true) + { + // We don't want a terrain pointer for spectators. + mo->terrain = NULL; + return; + } + // Update the object's terrain pointer. mo->terrain = K_GetTerrainForFlatNum(flatID); } From 2d34cd150d44dfd7571cfa93bb42f3044bc071fd Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 4 Jan 2022 23:35:59 +0000 Subject: [PATCH 125/379] Fix infinite pain floors If inflictor is null and you're in hitlag do not damage again --- src/p_inter.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index 337d2b84f..8fb55c0ad 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1845,10 +1845,8 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (!(target->flags & MF_SHOOTABLE)) return false; // shouldn't happen... -#if 0 - if (!(damagetype & DMG_DEATHMASK) && target->hitlag > 0) + if (!(damagetype & DMG_DEATHMASK) && target->hitlag > 0 && inflictor == NULL) return false; -#endif } if (target->flags2 & MF2_SKULLFLY) From 328f6aa4e07fed86b7462a5d486ba7ec27e0df6d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 5 Jan 2022 17:44:11 -0500 Subject: [PATCH 126/379] Fix default terrain friction --- src/k_terrain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index e03ee2059..945ba8450 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -1019,7 +1019,7 @@ static void K_TerrainDefaults(terrain_t *terrain) terrain->splashID = SIZE_MAX; terrain->footstepID = SIZE_MAX; - terrain->friction = FRACUNIT; + terrain->friction = 0; terrain->offroad = 0; terrain->damageType = -1; terrain->trickPanel = 0; From c80d6c2a9080daa16e091527e31fa3d2f6ea8078 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 5 Jan 2022 17:58:48 -0500 Subject: [PATCH 127/379] Fix missing line for trick panels --- src/k_terrain.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/k_terrain.c b/src/k_terrain.c index 945ba8450..f0a5347ea 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -416,6 +416,9 @@ void K_ProcessTerrainEffect(mobj_t *mo) player->pflags |= PF_TRICKDELAY; K_DoPogoSpring(mo, upwards, 1); + // Reduce speed + speed /= 2; + if (speed < minspeed) { speed = minspeed; From ad61f053976f023493438b51c46892a442b076d0 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 5 Jan 2022 22:32:07 -0800 Subject: [PATCH 128/379] Fix nametag distance check --- src/k_hud.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index ae6124a75..0404046be 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -2850,8 +2850,8 @@ static void K_drawKartNameTags(void) } v.x = R_InterpolateFixed(ntplayer->mo->old_x, ntplayer->mo->x); - v.y = R_InterpolateFixed(ntplayer->mo->old_x, ntplayer->mo->x); - v.z = R_InterpolateFixed(ntplayer->mo->old_x, ntplayer->mo->x); + v.y = R_InterpolateFixed(ntplayer->mo->old_x, ntplayer->mo->y); + v.z = R_InterpolateFixed(ntplayer->mo->old_x, ntplayer->mo->z); if (!(ntplayer->mo->eflags & MFE_VERTICALFLIP)) { From 6bc731094f1c1a8c5db840ae916c0ae6d0f00b11 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 6 Jan 2022 00:46:14 -0800 Subject: [PATCH 129/379] Oops almost fucked it again 11ced1c3d --- src/k_hud.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index 0404046be..fd83afd56 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -2850,8 +2850,8 @@ static void K_drawKartNameTags(void) } v.x = R_InterpolateFixed(ntplayer->mo->old_x, ntplayer->mo->x); - v.y = R_InterpolateFixed(ntplayer->mo->old_x, ntplayer->mo->y); - v.z = R_InterpolateFixed(ntplayer->mo->old_x, ntplayer->mo->z); + v.y = R_InterpolateFixed(ntplayer->mo->old_y, ntplayer->mo->y); + v.z = R_InterpolateFixed(ntplayer->mo->old_z, ntplayer->mo->z); if (!(ntplayer->mo->eflags & MFE_VERTICALFLIP)) { From 8e9299c29d6d4cd4c8e47478a58dbc6f6551156d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 6 Jan 2022 01:20:31 -0500 Subject: [PATCH 130/379] Save showfps --- src/v_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v_video.c b/src/v_video.c index 2832dbac5..bb06228e0 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -45,7 +45,7 @@ UINT8 *screens[5]; // screens[3] = fade screen start // screens[4] = fade screen end, postimage tempoarary buffer -consvar_t cv_ticrate = CVAR_INIT ("showfps", "No", 0, CV_YesNo, NULL); +consvar_t cv_ticrate = CVAR_INIT ("showfps", "No", CV_SAVE, CV_YesNo, NULL); static void CV_palette_OnChange(void); From c68e8fac9707731da64269dd8a5a927e51d55cb9 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 5 Jan 2022 21:23:09 -0800 Subject: [PATCH 131/379] Disable titlecard fade on join --- src/p_setup.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/p_setup.c b/src/p_setup.c index 8b6133e48..0d728d450 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -4225,7 +4225,9 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) return true; // If so... - G_PreLevelTitleCard(); + // but not if joining because the fade may time us out + if (!fromnetsave) + G_PreLevelTitleCard(); return true; } From 9e5b70ad8fbd62bfff0bce29e288840850dffd55 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 5 Jan 2022 03:17:38 -0500 Subject: [PATCH 132/379] No flashing tics in Battle --- src/k_kart.c | 11 +++++++++-- src/p_user.c | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 53426531a..255dff396 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2962,11 +2962,18 @@ UINT16 K_GetKartFlashing(player_t *player) { UINT16 tics = flashingtics; - if (!player) + if (gametype == GT_BATTLE) + { + // TODO: gametyperules + return 1; + } + + if (player == NULL) + { return tics; + } tics += (tics/8) * (player->kartspeed); - return tics; } diff --git a/src/p_user.c b/src/p_user.c index f8d556a6b..70fb23a08 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4555,7 +4555,7 @@ void P_PlayerThink(player_t *player) || (player->pflags & PF_NOCONTEST) // NO CONTEST explosion || ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0 && player->karmadelay))) { - if (player->flashing > 0 && player->flashing < K_GetKartFlashing(player) + if (player->flashing > 1 && player->flashing < K_GetKartFlashing(player) && (leveltime & 1)) player->mo->renderflags |= RF_DONTDRAW; else From 21c42c3d796280778dba075d9658db908cf256a4 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 5 Jan 2022 03:24:19 -0500 Subject: [PATCH 133/379] Buff tether in Battle - /4 minimum distance - x2 speed --- src/k_kart.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 255dff396..8a1f02451 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1721,6 +1721,7 @@ static void K_UpdateDraft(player_t *player) { fixed_t topspd = K_GetKartSpeed(player, false); fixed_t draftdistance; + fixed_t minDist; UINT8 leniency; UINT8 i; @@ -1740,6 +1741,13 @@ static void K_UpdateDraft(player_t *player) draftdistance = FixedMul(draftdistance, K_GetKartGameSpeedScalar(gamespeed)); } + minDist = 640 * player->mo->scale; + if (gametype == GT_BATTLE) + { + // TODO: gametyperules + minDist /= 4; + } + // On the contrary, the leniency period biases toward high weight. // (See also: the leniency variable in K_SpawnDraftDust) leniency = (3*TICRATE)/4 + ((player->kartweight-1) * (TICRATE/4)); @@ -1798,7 +1806,7 @@ static void K_UpdateDraft(player_t *player) #ifndef EASYDRAFTTEST // TOO close to draft. - if (dist < FixedMul(RING_DIST>>1, player->mo->scale)) + if (dist < minDist) continue; // Not close enough to draft. @@ -2866,6 +2874,13 @@ static void K_GetKartBoostPower(player_t *player) { // 30% - 44%, each point of speed adds 1.75% fixed_t draftspeed = ((3*FRACUNIT)/10) + ((player->kartspeed-1) * ((7*FRACUNIT)/400)); + + if (gametype == GT_BATTLE) + { + // TODO: gametyperules + draftspeed *= 2; + } + speedboost += FixedMul(draftspeed, player->draftpower); // (Drafting suffers no boost stack penalty.) numboosts++; } From 10cfc739477ca57d395d3684253398cf49556fea Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 5 Jan 2022 03:46:19 -0500 Subject: [PATCH 134/379] Less Karma Bomb CBT - Karma delay is 3sec instead of 10sec - Karma delay is only used for initially changing into a bomb --- src/g_game.c | 2 +- src/k_kart.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index ae0c2036b..6d0620701 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -260,7 +260,7 @@ INT32 stealtime = TICRATE/2; INT32 sneakertime = TICRATE + (TICRATE/3); INT32 itemtime = 8*TICRATE; INT32 bubbletime = TICRATE/2; -INT32 comebacktime = 10*TICRATE; +INT32 comebacktime = 3*TICRATE; INT32 bumptime = 6; INT32 greasetics = 3*TICRATE; INT32 wipeoutslowtime = 20; diff --git a/src/k_kart.c b/src/k_kart.c index 8a1f02451..bd6924e12 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3481,13 +3481,14 @@ void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers) karmahitbox->destscale = player->mo->destscale; P_SetScale(karmahitbox, player->mo->scale); + player->karmadelay = comebacktime; + if (netgame) { CONS_Printf(M_GetText("%s lost all of their bumpers!\n"), player_names[player-players]); } } - player->karmadelay = comebacktime; K_CalculateBattleWanted(); K_CheckBumpers(); } From 63305015205ee65c1ecc1a49078f0ca6d46bd93f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 5 Jan 2022 05:10:09 -0500 Subject: [PATCH 135/379] PLEASE NO RNG --- src/k_battle.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/k_battle.c b/src/k_battle.c index c4902fb05..4b8bf219a 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -469,16 +469,15 @@ void K_RunPaperItemSpawners(void) firstUnspawnedEmerald ); } - else if (P_RandomChance(FRACUNIT/3)) + else { drop = K_SpawnSphereBox( spotList[r]->x, spotList[r]->y, spotList[r]->z + (128 * mapobjectscale * flip), FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip, 10 ); - } - else - { + K_FlipFromObject(drop, spotList[r]); + drop = K_CreatePaperItem( spotList[r]->x, spotList[r]->y, spotList[r]->z + (128 * mapobjectscale * flip), FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip, From 11dee8d25e85901b3fe320d71dc43e3ae72f610f Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 11 Jan 2022 22:49:01 -0800 Subject: [PATCH 136/379] Cache terrain on levelflats --- src/k_terrain.c | 5 +---- src/p_setup.c | 3 +++ src/p_setup.h | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index f0a5347ea..44b8b3163 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -312,16 +312,13 @@ terrain_t *K_GetTerrainForTextureNum(INT32 textureNum) --------------------------------------------------*/ terrain_t *K_GetTerrainForFlatNum(INT32 flatID) { - levelflat_t *levelFlat = NULL; - if (flatID < 0 || flatID >= (signed)numlevelflats) { // Clearly invalid floor... return NULL; } - levelFlat = &levelflats[flatID]; - return K_GetTerrainForTextureName(levelFlat->name); + return levelflats[flatID].terrain; } /*-------------------------------------------------- diff --git a/src/p_setup.c b/src/p_setup.c index 0d728d450..be07b662e 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -661,6 +661,9 @@ flatfound: levelflat->u.flat.baselumpnum = LUMPERROR; } + levelflat->terrain = + K_GetTerrainForTextureName(levelflat->name); + CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name); return ( numlevelflats++ ); diff --git a/src/p_setup.h b/src/p_setup.h index 0a7587ec0..dfa79da14 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -17,6 +17,7 @@ #include "doomdata.h" #include "doomstat.h" #include "r_defs.h" +#include "k_terrain.h" // map md5, sent to players via PT_SERVERINFO extern unsigned char mapmd5[16]; @@ -71,6 +72,8 @@ typedef struct UINT16 width, height; + terrain_t *terrain; + // for flat animation INT32 animseq; // start pos. in the anim sequence INT32 numpics; From 7516a04eba41df1aa8d00cb765e996361209188b Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 12 Jan 2022 00:33:35 -0800 Subject: [PATCH 137/379] Fix NOMIXER --- src/sdl/sdl_sound.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sdl/sdl_sound.c b/src/sdl/sdl_sound.c index d7a5cb384..ac7a35167 100644 --- a/src/sdl/sdl_sound.c +++ b/src/sdl/sdl_sound.c @@ -517,7 +517,7 @@ static inline void I_SetChannels(void) } } -void I_SetSfxVolume(UINT8 volume) +void I_SetSfxVolume(int volume) { INT32 i; @@ -1466,7 +1466,7 @@ void I_ResumeSong(void) #endif } -void I_SetMusicVolume(UINT8 volume) +void I_SetMusicVolume(int volume) { (void)volume; } @@ -1477,6 +1477,9 @@ boolean I_SetSongTrack(int track) return false; } +void I_UpdateSongLagThreshold(void){} +void I_UpdateSongLagConditions(void){} + /// ------------------------ /// MUSIC FADING /// ------------------------ From c812d6fdd79cdbcfbc406ea0a500b720d8bae67d Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 12 Jan 2022 03:36:02 -0800 Subject: [PATCH 138/379] Interpolate from time of previous tic Previously interpolated from last 35th of a second, which may be offset from game time due to connection lag. Consider this the proper fix to 6ecac4159a too. --- src/d_clisrv.c | 14 ++++++++++---- src/d_clisrv.h | 2 +- src/d_main.c | 16 +++++++++++++--- src/i_system.h | 4 ++-- src/sdl/i_system.c | 5 ++--- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 6bf1983e3..c40a80a20 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5259,8 +5259,10 @@ static void SV_Maketic(void) maketic++; } -void TryRunTics(tic_t realtics) +boolean TryRunTics(tic_t realtics) { + boolean ticking; + // the machine has lagged but it is not so bad if (realtics > TICRATE/7) // FIXME: consistency failure!! { @@ -5304,7 +5306,9 @@ void TryRunTics(tic_t realtics) } #endif - if (neededtic > gametic) + ticking = neededtic > gametic; + + if (ticking) { if (realtics) hu_stopped = false; @@ -5314,10 +5318,10 @@ void TryRunTics(tic_t realtics) { if (realtics) hu_stopped = true; - return; + return false; } - if (neededtic > gametic) + if (ticking) { if (advancedemo) { @@ -5354,6 +5358,8 @@ void TryRunTics(tic_t realtics) if (realtics) hu_stopped = true; } + + return ticking; } diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 88712f01c..7cd8acce5 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -488,7 +488,7 @@ boolean Playing(void); void D_QuitNetGame(void); //? How many ticks to run? -void TryRunTics(tic_t realtic); +boolean TryRunTics(tic_t realtic); // extra data for lmps // these functions scare me. they contain magic. diff --git a/src/d_main.c b/src/d_main.c index 4776f4e77..a0cf64e58 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -687,6 +687,7 @@ tic_t rendergametic; void D_SRB2Loop(void) { tic_t oldentertics = 0, entertic = 0, realtics = 0, rendertimeout = INFTICS; + boolean ticked; if (dedicated) server = true; @@ -774,11 +775,20 @@ void D_SRB2Loop(void) realtics = 1; // process tics (but maybe not if realtic == 0) - TryRunTics(realtics); + ticked = TryRunTics(realtics); - if (cv_frameinterpolation.value == 1 && !(paused || P_AutoPause() || hu_stopped)) + if (cv_frameinterpolation.value == 1 && !(paused || P_AutoPause())) { - fixed_t entertimefrac = I_GetTimeFrac(); + static float tictime; + float entertime = I_GetTimeFrac(); + + fixed_t entertimefrac; + + if (ticked) + tictime = entertime; + + entertimefrac = FLOAT_TO_FIXED(entertime - tictime); + // renderdeltatics is a bit awkard to evaluate, since the system time interface is whole tic-based renderdeltatics = realtics * FRACUNIT; if (entertimefrac > rendertimefrac) diff --git a/src/i_system.h b/src/i_system.h index 789117eaa..4a8bf0c27 100644 --- a/src/i_system.h +++ b/src/i_system.h @@ -46,9 +46,9 @@ UINT32 I_GetFreeMem(UINT32 *total); */ tic_t I_GetTime(void); -/** \brief Get the current time as a fraction of a tic since the last tic. +/** \brief Get the current time in tics including fractions. */ -fixed_t I_GetTimeFrac(void); +float I_GetTimeFrac(void); /** \brief Returns precise time value for performance measurement. */ diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index bb2e4ea21..32d3980ff 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1568,11 +1568,10 @@ tic_t I_GetTime(void) return (tic_t)f; } -fixed_t I_GetTimeFrac(void) +float I_GetTimeFrac(void) { UpdateElapsedTics(); - - return FLOAT_TO_FIXED((float) (elapsed_tics - floor(elapsed_tics))); + return elapsed_tics; } precise_t I_GetPreciseTime(void) From fd9590cc3dd9721a489048679bb25ddf7f4b9c67 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 12 Jan 2022 04:36:37 -0800 Subject: [PATCH 139/379] Do not interpolate if time between game tics <1/35 --- src/d_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/d_main.c b/src/d_main.c index a0cf64e58..df83e378d 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -789,6 +789,9 @@ void D_SRB2Loop(void) entertimefrac = FLOAT_TO_FIXED(entertime - tictime); + if (entertimefrac < FRACUNIT) + entertimefrac = FRACUNIT; + // renderdeltatics is a bit awkard to evaluate, since the system time interface is whole tic-based renderdeltatics = realtics * FRACUNIT; if (entertimefrac > rendertimefrac) From e909cb0a733f3eccc061abc11bddadfddfcc125b Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 12 Jan 2022 12:57:52 -0800 Subject: [PATCH 140/379] Don't interpolate frames if avg <35 Proper solution for 85ce207e9 --- src/d_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index df83e378d..37ed9c4bc 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -787,10 +787,10 @@ void D_SRB2Loop(void) if (ticked) tictime = entertime; - entertimefrac = FLOAT_TO_FIXED(entertime - tictime); - - if (entertimefrac < FRACUNIT) + if (aproxfps < 35.0) entertimefrac = FRACUNIT; + else + entertimefrac = FLOAT_TO_FIXED(entertime - tictime); // renderdeltatics is a bit awkard to evaluate, since the system time interface is whole tic-based renderdeltatics = realtics * FRACUNIT; From 178979d95c7b06148c75f18ebf669cc27a26f1eb Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 12 Jan 2022 04:30:03 -0800 Subject: [PATCH 141/379] Do not speed up underwater/heatwave effect in OpenGL --- src/hardware/hw_main.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index aa55544a5..e50f68d1d 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -6752,7 +6752,6 @@ void HWR_DoPostProcessor(player_t *player) // 10 by 10 grid. 2 coordinates (xy) float v[SCREENVERTS][SCREENVERTS][2]; static double disStart = 0; - static fixed_t last_fractime = 0; UINT8 x, y; INT32 WAVELENGTH; @@ -6784,16 +6783,7 @@ void HWR_DoPostProcessor(player_t *player) } HWD.pfnPostImgRedraw(v); if (!(paused || P_AutoPause())) - disStart += 1; - if (renderdeltatics > FRACUNIT) - { - disStart = disStart - FIXED_TO_FLOAT(last_fractime) + 1 + FIXED_TO_FLOAT(rendertimefrac); - } - else - { - disStart = disStart - FIXED_TO_FLOAT(last_fractime) + FIXED_TO_FLOAT(rendertimefrac); - } - last_fractime = rendertimefrac; + disStart += FIXED_TO_FLOAT(renderdeltatics); // Capture the screen again for screen waving on the intermission if(gamestate != GS_INTERMISSION) From c83d65f6ab0ef71ea3360eeb29184203e2447345 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 12 Jan 2022 04:30:43 -0800 Subject: [PATCH 142/379] Closer OpenGL underwater/heatwave effect to Software --- src/hardware/hw_main.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index e50f68d1d..82bada373 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -6761,15 +6761,15 @@ void HWR_DoPostProcessor(player_t *player) // Modifies the wave. if (*type == postimg_water) { - WAVELENGTH = 20; // Lower is longer - AMPLITUDE = 20; // Lower is bigger - FREQUENCY = 16; // Lower is faster + WAVELENGTH = 5; + AMPLITUDE = 20; + FREQUENCY = 8; } else { - WAVELENGTH = 10; // Lower is longer - AMPLITUDE = 30; // Lower is bigger - FREQUENCY = 4; // Lower is faster + WAVELENGTH = 10; + AMPLITUDE = 60; + FREQUENCY = 4; } for (x = 0; x < SCREENVERTS; x++) From 945ef8bf0a9f15aa404a6ae44c2b098f532c089f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 17 Dec 2021 18:29:35 -0500 Subject: [PATCH 143/379] Reduce turning for bots when they stair jank --- src/k_bot.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index 24f3517a2..44f9c27c2 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -662,7 +662,10 @@ fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t --------------------------------------------------*/ static botprediction_t *K_CreateBotPrediction(player_t *player) { - const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN); // Reduce prediction based on how fast you can turn + // Stair janking makes it harder to steer, so attempt to steer harder. + const UINT8 jankDiv = (player->stairjank > 0 ? 2 : 1); + + const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN) / jankDiv; // Reduce prediction based on how fast you can turn const INT16 normal = KART_FULLTURN; // "Standard" handling to compare to const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict From 19203e6a4a28cc3d18aff1c54f57dd8dcbfd6a41 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 18 Dec 2021 16:31:08 -0500 Subject: [PATCH 144/379] Bot predict nudging scale is based on the prediction's radius --- src/k_botsearch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_botsearch.c b/src/k_botsearch.c index 846521e6f..a06eb55f1 100644 --- a/src/k_botsearch.c +++ b/src/k_botsearch.c @@ -619,7 +619,7 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player) fixed_t avgX = 0, avgY = 0; fixed_t avgDist = 0; - const fixed_t baseNudge = 128 * mapobjectscale; + const fixed_t baseNudge = predict->radius; fixed_t maxNudge = distToPredict; fixed_t nudgeDist = 0; angle_t nudgeDir = 0; From 627c59e07f419b1e515e77b47108a74b6a727478 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 5 Jan 2022 02:09:31 -0500 Subject: [PATCH 145/379] Bot lookback - Bots use lookback when they have a attack item that hits back & they see you. - Bot item throwing was made slower for easier difficulties. (Lv. 9 should still be pretty fast.) - Bot cone detection was made much much farther & wider Lookback sparkle is a signal for "they are contemplating throwing backwards" --- src/k_botitem.c | 81 +++++++++++++++++++++++++++++++++++++------------ src/k_kart.c | 20 ++++++------ 2 files changed, 72 insertions(+), 29 deletions(-) diff --git a/src/k_botitem.c b/src/k_botitem.c index 4bb83931c..90e3d8172 100644 --- a/src/k_botitem.c +++ b/src/k_botitem.c @@ -382,6 +382,8 @@ static void K_BotItemGenericTap(player_t *player, ticcmd_t *cmd) --------------------------------------------------*/ static boolean K_BotRevealsGenericTrap(player_t *player, INT16 turnamt, boolean mine) { + const fixed_t coneDist = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); + if (abs(turnamt) >= KART_FULLTURN/2) { // DON'T reveal on turns, we can place bananas on turns whenever we have multiple to spare, @@ -404,7 +406,7 @@ static boolean K_BotRevealsGenericTrap(player_t *player, INT16 turnamt, boolean } // Check your behind. - if (K_PlayerInCone(player, player->mo->radius * 16, 10, true) != NULL) + if (K_PlayerInCone(player, coneDist, 15, true) != NULL) { return true; } @@ -536,16 +538,19 @@ static void K_BotItemRocketSneaker(player_t *player, ticcmd_t *cmd) --------------------------------------------------*/ static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt) { + const fixed_t coneDist = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); SINT8 throwdir = -1; + boolean tryLookback = false; player_t *target = NULL; player->botvars.itemconfirm++; - target = K_PlayerInCone(player, player->mo->radius * 16, 10, true); + target = K_PlayerInCone(player, coneDist, 15, true); if (target != NULL) { K_ItemConfirmForTarget(player, target, player->botvars.difficulty); throwdir = -1; + tryLookback = true; } if (abs(turnamt) >= KART_FULLTURN/2) @@ -564,7 +569,12 @@ static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt) } } - if (player->botvars.itemconfirm > 2*TICRATE || player->bananadrag >= TICRATE) + if (tryLookback == true && throwdir == -1) + { + cmd->buttons |= BT_LOOKBACK; + } + + if (player->botvars.itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE) { K_BotGenericPressItem(player, cmd, throwdir); } @@ -585,12 +595,14 @@ static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt) --------------------------------------------------*/ static void K_BotItemMine(player_t *player, ticcmd_t *cmd, INT16 turnamt) { + const fixed_t coneDist = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); SINT8 throwdir = 0; + boolean tryLookback = false; player_t *target = NULL; player->botvars.itemconfirm++; - target = K_PlayerInCone(player, player->mo->radius * 16, 10, true); + target = K_PlayerInCone(player, coneDist, 15, true); if (target != NULL) { K_ItemConfirmForTarget(player, target, player->botvars.difficulty); @@ -601,6 +613,7 @@ static void K_BotItemMine(player_t *player, ticcmd_t *cmd, INT16 turnamt) { player->botvars.itemconfirm += player->botvars.difficulty / 2; throwdir = -1; + tryLookback = true; } else { @@ -619,7 +632,12 @@ static void K_BotItemMine(player_t *player, ticcmd_t *cmd, INT16 turnamt) } } - if (player->botvars.itemconfirm > 2*TICRATE || player->bananadrag >= TICRATE) + if (tryLookback == true && throwdir == -1) + { + cmd->buttons |= BT_LOOKBACK; + } + + if (player->botvars.itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE) { K_BotGenericPressItem(player, cmd, throwdir); } @@ -640,6 +658,7 @@ static void K_BotItemMine(player_t *player, ticcmd_t *cmd, INT16 turnamt) --------------------------------------------------*/ static void K_BotItemLandmine(player_t *player, ticcmd_t *cmd, INT16 turnamt) { + const fixed_t coneDist = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); player_t *target = NULL; player->botvars.itemconfirm++; @@ -649,13 +668,14 @@ static void K_BotItemLandmine(player_t *player, ticcmd_t *cmd, INT16 turnamt) player->botvars.itemconfirm += player->botvars.difficulty / 2; } - target = K_PlayerInCone(player, player->mo->radius * 16, 10, true); + target = K_PlayerInCone(player, coneDist, 15, true); if (target != NULL) { K_ItemConfirmForTarget(player, target, player->botvars.difficulty); + cmd->buttons |= BT_LOOKBACK; } - if (player->botvars.itemconfirm > 2*TICRATE) + if (player->botvars.itemconfirm > 10*TICRATE) { K_BotGenericPressItem(player, cmd, -1); } @@ -675,8 +695,10 @@ static void K_BotItemLandmine(player_t *player, ticcmd_t *cmd, INT16 turnamt) --------------------------------------------------*/ static void K_BotItemEggman(player_t *player, ticcmd_t *cmd) { + const fixed_t coneDist = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y); SINT8 throwdir = -1; + boolean tryLookback = false; player_t *target = NULL; player->botvars.itemconfirm++; @@ -688,11 +710,12 @@ static void K_BotItemEggman(player_t *player, ticcmd_t *cmd) throwdir = 1; } - target = K_PlayerInCone(player, player->mo->radius * 16, 10, true); + target = K_PlayerInCone(player, coneDist, 15, true); if (target != NULL) { K_ItemConfirmForTarget(player, target, player->botvars.difficulty); throwdir = -1; + tryLookback = true; } if (stealth > 1 || player->itemroulette > 0) @@ -701,7 +724,12 @@ static void K_BotItemEggman(player_t *player, ticcmd_t *cmd) throwdir = -1; } - if (player->botvars.itemconfirm > 2*TICRATE || player->bananadrag >= TICRATE) + if (tryLookback == true && throwdir == -1) + { + cmd->buttons |= BT_LOOKBACK; + } + + if (player->botvars.itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE) { K_BotGenericPressItem(player, cmd, throwdir); } @@ -720,6 +748,7 @@ static void K_BotItemEggman(player_t *player, ticcmd_t *cmd) --------------------------------------------------*/ static boolean K_BotRevealsEggbox(player_t *player) { + const fixed_t coneDist = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y); player_t *target = NULL; @@ -737,7 +766,7 @@ static boolean K_BotRevealsEggbox(player_t *player) } // Check your behind. - target = K_PlayerInCone(player, player->mo->radius * 16, 10, true); + target = K_PlayerInCone(player, coneDist, 15, true); if (target != NULL) { return true; @@ -810,8 +839,9 @@ static void K_BotItemEggmanExplosion(player_t *player, ticcmd_t *cmd) static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd) { const fixed_t topspeed = K_GetKartSpeed(player, false); - fixed_t radius = (player->mo->radius * 32); + fixed_t radius = FixedMul(2560 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); SINT8 throwdir = -1; + boolean tryLookback = false; UINT8 snipeMul = 2; player_t *target = NULL; @@ -823,24 +853,30 @@ static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd) player->botvars.itemconfirm++; - target = K_PlayerInCone(player, radius, 10, false); + target = K_PlayerInCone(player, radius, 15, false); if (target != NULL) { K_ItemConfirmForTarget(player, target, player->botvars.difficulty * snipeMul); throwdir = 1; } - else if (K_PlayerInCone(player, radius, 10, true)) + else { - target = K_PlayerInCone(player, radius, 10, true); + target = K_PlayerInCone(player, radius, 15, true); if (target != NULL) { K_ItemConfirmForTarget(player, target, player->botvars.difficulty); throwdir = -1; + tryLookback = true; } } - if (player->botvars.itemconfirm > 5*TICRATE) + if (tryLookback == true && throwdir == -1) + { + cmd->buttons |= BT_LOOKBACK; + } + + if (player->botvars.itemconfirm > 25*TICRATE) { K_BotGenericPressItem(player, cmd, throwdir); } @@ -861,8 +897,9 @@ static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd) static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) { const fixed_t topspeed = K_GetKartSpeed(player, false); - fixed_t radius = (player->mo->radius * 32); + fixed_t radius = FixedMul(2560 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); SINT8 throwdir = 1; + boolean tryLookback = false; UINT8 snipeMul = 2; INT32 lastTarg = player->lastjawztarget; player_t *target = NULL; @@ -875,11 +912,12 @@ static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) player->botvars.itemconfirm++; - target = K_PlayerInCone(player, radius, 10, true); + target = K_PlayerInCone(player, radius, 15, true); if (target != NULL) { K_ItemConfirmForTarget(player, target, player->botvars.difficulty); throwdir = -1; + tryLookback = true; } if (lastTarg != -1 @@ -913,7 +951,12 @@ static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) } } - if (player->botvars.itemconfirm > 5*TICRATE) + if (tryLookback == true && throwdir == -1) + { + cmd->buttons |= BT_LOOKBACK; + } + + if (player->botvars.itemconfirm > 25*TICRATE) { K_BotGenericPressItem(player, cmd, throwdir); } @@ -1007,7 +1050,7 @@ static void K_BotItemBubble(player_t *player, ticcmd_t *cmd) } else if (player->bubbleblowup >= bubbletime) { - if (player->botvars.itemconfirm >= 10*TICRATE) + if (player->botvars.itemconfirm > 10*TICRATE) { hold = true; } diff --git a/src/k_kart.c b/src/k_kart.c index bd6924e12..b9fff95ba 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2231,19 +2231,19 @@ void K_KartMoveAnimation(player_t *player) SINT8 destGlanceDir = 0; SINT8 drift = player->drift; - // Uses turning over steering -- it's important to show player feedback immediately. - if (player->cmd.turning < -minturn) - { - turndir = -1; - } - else if (player->cmd.turning > minturn) - { - turndir = 1; - } - if (!lookback) { player->pflags &= ~PF_LOOKDOWN; + + // Uses turning over steering -- it's important to show player feedback immediately. + if (player->cmd.turning < -minturn) + { + turndir = -1; + } + else if (player->cmd.turning > minturn) + { + turndir = 1; + } } else if (drift == 0) { From 0ac6639b95d2fc17690c848ffd244370f968a59b Mon Sep 17 00:00:00 2001 From: SteelT Date: Wed, 12 Jan 2022 20:57:25 -0500 Subject: [PATCH 146/379] Fix sounds playing multiple times during intro if interpolation is on --- src/f_finale.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index 8b62206c0..cd20f58eb 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -346,16 +346,6 @@ static void F_IntroDrawScene(void) // DRAW A FULL PIC INSTEAD OF FLAT! if (intro_scenenum == 0) { - if (finalecount == 8) - S_StartSound(NULL, sfx_vroom); - else if (finalecount == 47) - { - // Need to use M_Random otherwise it always uses the same sound - INT32 rskin = M_RandomKey(numskins); - UINT8 rtaunt = M_RandomKey(2); - sfxenum_t rsound = skins[rskin].soundsid[SKSKBST1+rtaunt]; - S_StartSound(NULL, rsound); - } background = W_CachePatchName("KARTKREW", PU_CACHE); highres = true; } @@ -454,6 +444,20 @@ void F_IntroTicker(void) timetonext--; + if (intro_scenenum == 0) + { + if (finalecount == 8) + S_StartSound(NULL, sfx_vroom); + else if (finalecount == 47) + { + // Need to use M_Random otherwise it always uses the same sound + INT32 rskin = M_RandomKey(numskins); + UINT8 rtaunt = M_RandomKey(2); + sfxenum_t rsound = skins[rskin].soundsid[SKSKBST1+rtaunt]; + S_StartSound(NULL, rsound); + } + } + F_WriteText(); // check for skipping From 9739df3318fa74186c960903908eddf94ec8f7ef Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 16 Jan 2022 23:21:38 +0000 Subject: [PATCH 147/379] blessings be to hannu Hannu's dropshadow hack, copied across and edited for Kart's column drawer setting functions. --- src/r_draw.h | 1 + src/r_draw8.c | 33 +++++++++++++++++++++++++++++++++ src/r_things.c | 10 +++++++++- src/screen.c | 1 + src/screen.h | 1 + 5 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/r_draw.h b/src/r_draw.h index 7b44d6185..741ddcc2a 100644 --- a/src/r_draw.h +++ b/src/r_draw.h @@ -180,6 +180,7 @@ void R_DrawViewBorder(void); void R_DrawColumn_8(void); void R_DrawShadeColumn_8(void); void R_DrawTranslucentColumn_8(void); +void R_DrawDropShadowColumn_8(void); void R_DrawTranslatedColumn_8(void); void R_DrawTranslatedTranslucentColumn_8(void); void R_Draw2sMultiPatchColumn_8(void); diff --git a/src/r_draw8.c b/src/r_draw8.c index 0dd8463f6..61b22bc66 100644 --- a/src/r_draw8.c +++ b/src/r_draw8.c @@ -566,6 +566,39 @@ void R_DrawTranslucentColumn_8(void) } } +// Hack: A cut-down copy of R_DrawTranslucentColumn_8 that does not read texture +// data since something about calculating the texture reading address for drop shadows is broken. +// dc_texturemid and dc_iscale get wrong values for drop shadows, however those are not strictly +// needed for the current design of the shadows, so this function bypasses the issue +// by not using those variables at all. +void R_DrawDropShadowColumn_8(void) +{ + register INT32 count; + register UINT8 *dest; + + count = dc_yh - dc_yl + 1; + + if (count <= 0) // Zero length, column does not exceed a pixel. + return; + + dest = &topleft[dc_yl*vid.width + dc_x]; + + { +#define DSCOLOR 15 // palette index for the color of the shadow + register const UINT8 *transmap_offset = dc_transmap + (dc_colormap[DSCOLOR] << 8); +#undef DSCOLOR + while ((count -= 2) >= 0) + { + *dest = *(transmap_offset + (*dest)); + dest += vid.width; + *dest = *(transmap_offset + (*dest)); + dest += vid.width; + } + if (count & 1) + *dest = *(transmap_offset + (*dest)); + } +} + /** \brief The R_DrawTranslatedTranslucentColumn_8 function Spiffy function. Not only does it colormap a sprite, but does translucency as well. Uber-kudos to Cyan Helkaraxe diff --git a/src/r_things.c b/src/r_things.c index c31d295a8..26e66149b 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -836,7 +836,15 @@ static void R_DrawVisSprite(vissprite_t *vis) dc_fullbright = colormaps; dc_translation = R_GetSpriteTranslation(vis); - if (R_SpriteIsFlashing(vis)) // Bosses "flash" + // Hack: Use a special column function for drop shadows that bypasses + // invalid memory access crashes caused by R_ProjectDropShadow putting wrong values + // in dc_texturemid and dc_iscale when the shadow is sloped. + if (vis->cut & SC_SHADOW) + { + R_SetColumnFunc(COLDRAWFUNC_DROPSHADOW, false); + dc_transmap = vis->transmap; + } + else if (R_SpriteIsFlashing(vis)) // Bosses "flash" R_SetColumnFunc(COLDRAWFUNC_TRANS, false); // translate certain pixels to white else if (vis->mobj->color && vis->transmap) // Color mapping { diff --git a/src/screen.c b/src/screen.c index 745b2a065..377a4b89c 100644 --- a/src/screen.c +++ b/src/screen.c @@ -133,6 +133,7 @@ void SCR_SetDrawFuncs(void) colfuncs[COLDRAWFUNC_TWOSMULTIPATCH] = R_Draw2sMultiPatchColumn_8; colfuncs[COLDRAWFUNC_TWOSMULTIPATCHTRANS] = R_Draw2sMultiPatchTranslucentColumn_8; colfuncs[COLDRAWFUNC_FOG] = R_DrawFogColumn_8; + colfuncs[COLDRAWFUNC_DROPSHADOW] = R_DrawDropShadowColumn_8; spanfuncs[SPANDRAWFUNC_TRANS] = R_DrawTranslucentSpan_8; spanfuncs[SPANDRAWFUNC_TILTED] = R_DrawTiltedSpan_8; diff --git a/src/screen.h b/src/screen.h index 66c52dd67..f279e8444 100644 --- a/src/screen.h +++ b/src/screen.h @@ -131,6 +131,7 @@ enum COLDRAWFUNC_TWOSMULTIPATCH, COLDRAWFUNC_TWOSMULTIPATCHTRANS, COLDRAWFUNC_FOG, + COLDRAWFUNC_DROPSHADOW, COLDRAWFUNC_MAX }; From 12dc69badbbdc46b28c9d9380fbc6e8e2d5a1097 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 16 Jan 2022 22:02:11 +0000 Subject: [PATCH 148/379] After I experienced a few consistent crashes for invalid subsector references in P_BounceMove/P_SlideMove, add a few extra checks for whether the mobj is removed. --- src/p_map.c | 9 ++++++++- src/p_mobj.c | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/p_map.c b/src/p_map.c index 8fb58cd0a..fb79b6677 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -3732,7 +3732,7 @@ stairstep: tmymove = 0; } if (!P_TryMove(mo, newx, newy, true)) { - if (success) + if (success || P_MobjWasRemoved(mo)) return; // Good enough!! else goto retry; @@ -3856,6 +3856,9 @@ void P_BounceMove(mobj_t *mo) INT32 hitcount; fixed_t mmomx = 0, mmomy = 0; + if (P_MobjWasRemoved(mo)) + return; + if (mo->player) { P_BouncePlayerMove(mo); @@ -3979,7 +3982,11 @@ bounceback: mo->momy = tmymove; if (!P_TryMove(mo, mo->x + tmxmove, mo->y + tmymove, true)) + { + if (P_MobjWasRemoved(mo)) + return; goto retry; + } } // diff --git a/src/p_mobj.c b/src/p_mobj.c index f1e4ce0c0..11b51b1e5 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1694,6 +1694,8 @@ void P_XYMovement(mobj_t *mo) else { P_BounceMove(mo); + if (P_MobjWasRemoved(mo)) + return; xmove = ymove = 0; S_StartSound(mo, mo->info->activesound); @@ -1843,6 +1845,9 @@ void P_SceneryXYMovement(mobj_t *mo) if (!P_SceneryTryMove(mo, mo->x + mo->momx, mo->y + mo->momy)) P_BounceMove(mo); + if (P_MobjWasRemoved(mo)) + return; + if ((!(mo->eflags & MFE_VERTICALFLIP) && mo->z > mo->floorz) || (mo->eflags & MFE_VERTICALFLIP && mo->z+mo->height < mo->ceilingz)) return; // no friction when airborne From 9c725a0e420f8cdfe7155088508ccf812d9224f7 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 14 Jan 2022 22:36:49 -0800 Subject: [PATCH 149/379] Fix overflow in precipitation sector search --- src/p_mobj.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 11b51b1e5..8e839d8f1 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10534,7 +10534,8 @@ void P_PrecipitationEffects(void) volume = 255; // Sky above? We get it full blast. else { - fixed_t x, y, yl, yh, xl, xh; + /* GCC is optimizing away y >= yl, FUCK YOU */ + volatile fixed_t x, y, yl, yh, xl, xh; fixed_t closedist, newdist; // Essentially check in a 1024 unit radius of the player for an outdoor area. @@ -10543,8 +10544,8 @@ void P_PrecipitationEffects(void) xl = players[g_localplayers[0]].mo->x - 1024*FRACUNIT; xh = players[g_localplayers[0]].mo->x + 1024*FRACUNIT; closedist = 2048*FRACUNIT; - for (y = yl; y <= yh; y += FRACUNIT*64) - for (x = xl; x <= xh; x += FRACUNIT*64) + for (y = yl; y >= yl && y <= yh; y += FRACUNIT*64) + for (x = xl; x >= xl && x <= xh; x += FRACUNIT*64) { if (R_PointInSubsector(x, y)->sector->ceilingpic == skyflatnum) // Found the outdoors! { @@ -13314,4 +13315,4 @@ fixed_t P_GetMobjZMovement(mobj_t *mo) speed = FixedHypot(mo->momx, mo->momy); return P_ReturnThrustY(mo, slope->zangle, P_ReturnThrustX(mo, angDiff, speed)); -} \ No newline at end of file +} From 31255a8cf99d1986bcbbeb2168903a63f7dde10d Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 16 Jan 2022 20:57:56 +0000 Subject: [PATCH 150/379] Rewrote the fix to: - be comprehensive (still allow rain to be heard against the topleft corner, not just the bottomright corner) - not use volatile (now uses INT64) - not perform this pointinsubsector search if the map has no rain/thunder (yes I tested EHZ with this check dummied out so this bug won't crop up again later) --- src/p_mobj.c | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 8e839d8f1..502d38d25 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10486,6 +10486,8 @@ void P_PrecipitationEffects(void) boolean effects_lightning = (precipprops[curWeather].effects & PRECIPFX_LIGHTNING); boolean lightningStrike = false; + boolean sounds_rain = (rainsfx != sfx_None && (!leveltime || leveltime % rainfreq == 1)); + // No thunder except every other tic. if (!(leveltime & 1)) { @@ -10530,30 +10532,48 @@ void P_PrecipitationEffects(void) if (sound_disabled) return; // Sound off? D'aw, no fun. + if (!sounds_rain && !sounds_thunder) + return; // no need to calculate volume at ALL + if (players[g_localplayers[0]].mo->subsector->sector->ceilingpic == skyflatnum) volume = 255; // Sky above? We get it full blast. else { - /* GCC is optimizing away y >= yl, FUCK YOU */ - volatile fixed_t x, y, yl, yh, xl, xh; + INT64 x, y, yl, yh, xl, xh; fixed_t closedist, newdist; // Essentially check in a 1024 unit radius of the player for an outdoor area. - yl = players[g_localplayers[0]].mo->y - 1024*FRACUNIT; - yh = players[g_localplayers[0]].mo->y + 1024*FRACUNIT; - xl = players[g_localplayers[0]].mo->x - 1024*FRACUNIT; - xh = players[g_localplayers[0]].mo->x + 1024*FRACUNIT; - closedist = 2048*FRACUNIT; - for (y = yl; y >= yl && y <= yh; y += FRACUNIT*64) - for (x = xl; x >= xl && x <= xh; x += FRACUNIT*64) +#define RADIUSSTEP (64*FRACUNIT) +#define SEARCHRADIUS (16*RADIUSSTEP) + yl = yh = players[g_localplayers[0]].mo->y; + yl -= SEARCHRADIUS; + while (yl < INT32_MIN) + yl += RADIUSSTEP; + yh += SEARCHRADIUS; + while (yh > INT32_MAX) + yh -= RADIUSSTEP; + + xl = xh = players[g_localplayers[0]].mo->x; + xl -= SEARCHRADIUS; + while (xl < INT32_MIN) + xl += RADIUSSTEP; + xh += SEARCHRADIUS; + while (xh > INT32_MAX) + xh -= RADIUSSTEP; + + closedist = SEARCHRADIUS*2; +#undef SEARCHRADIUS + for (y = yl; y <= yh; y += RADIUSSTEP) + for (x = xl; x <= xh; x += RADIUSSTEP) { - if (R_PointInSubsector(x, y)->sector->ceilingpic == skyflatnum) // Found the outdoors! + if (R_PointInSubsector((fixed_t)x, (fixed_t)y)->sector->ceilingpic == skyflatnum) // Found the outdoors! { - newdist = S_CalculateSoundDistance(players[g_localplayers[0]].mo->x, players[g_localplayers[0]].mo->y, 0, x, y, 0); + newdist = S_CalculateSoundDistance(players[g_localplayers[0]].mo->x, players[g_localplayers[0]].mo->y, 0, (fixed_t)x, (fixed_t)y, 0); if (newdist < closedist) closedist = newdist; } } +#undef RADIUSSTEP volume = 255 - (closedist>>(FRACBITS+2)); } @@ -10563,7 +10583,7 @@ void P_PrecipitationEffects(void) else if (volume > 255) volume = 255; - if (rainsfx != sfx_None && (!leveltime || leveltime % rainfreq == 1)) + if (sounds_rain) S_StartSoundAtVolume(players[g_localplayers[0]].mo, rainsfx, volume); if (!sounds_thunder) From ed43ee51020809f292cf4d487f36d274d312b7ab Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 16 Jan 2022 23:53:14 +0000 Subject: [PATCH 151/379] Terrain pointers were not getting properly relinked on netsave load, now they are. Thanks Sal for taking a moment out of your day to rubber duck with me --- src/p_saveg.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/p_saveg.c b/src/p_saveg.c index 7710ee0de..6aff9a33f 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -4119,6 +4119,15 @@ static void P_RelinkPointers(void) if (!(mobj->itnext = P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "itnext not found on %d\n", mobj->type); } + if (mobj->terrain) + { + temp = (UINT32)(size_t)mobj->terrain; + mobj->terrain = K_GetTerrainByIndex(temp); + if (mobj->terrain == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "terrain not found on %d\n", mobj->type); + } + } if (mobj->player) { if ( mobj->player->awayviewmobj) From 27aa5d43e57431bbf99c2dca1e066ad32f581993 Mon Sep 17 00:00:00 2001 From: SteelT Date: Mon, 17 Jan 2022 15:46:44 -0500 Subject: [PATCH 152/379] Don't compile with dynamic base --- src/Makefile.d/win32.mk | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Makefile.d/win32.mk b/src/Makefile.d/win32.mk index ec66107d4..83edc3c45 100644 --- a/src/Makefile.d/win32.mk +++ b/src/Makefile.d/win32.mk @@ -8,6 +8,11 @@ else EXENAME?=srb2kart64.exe endif +# disable dynamicbase if under msys2 +ifdef MSYSTEM +libs+=-Wl,--disable-dynamicbase +endif + sources+=win32/Srb2win.rc opts+=-DSTDC_HEADERS libs+=-ladvapi32 -lkernel32 -lmsvcrt -luser32 From a90953c22d8ccb5644975efcc2df62bbb2d2e9fd Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 17 Jan 2022 23:07:04 -0800 Subject: [PATCH 153/379] Uncap frame interpolation on the viewpoint --- src/r_fps.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/r_fps.c b/src/r_fps.c index 59f10bae8..67f96498d 100644 --- a/src/r_fps.c +++ b/src/r_fps.c @@ -84,8 +84,10 @@ void R_InterpolateView(fixed_t frac) { if (frac < 0) frac = 0; +#if 0 if (frac > FRACUNIT) frac = FRACUNIT; +#endif viewx = oldview->x + R_LerpFixed(oldview->x, newview->x, frac); viewy = oldview->y + R_LerpFixed(oldview->y, newview->y, frac); From 912f9eaa15c2125cdf3d7e24520adb2dae57f54d Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 18 Dec 2021 22:31:33 -0800 Subject: [PATCH 154/379] Create folder with branch name when saving replays --- src/g_demo.c | 6 +++--- src/p_setup.c | 12 +++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/g_demo.c b/src/g_demo.c index ce0b0d9fa..25404a809 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -60,7 +60,7 @@ boolean nodrawers; // for comparative timing purposes boolean noblit; // for comparative timing purposes tic_t demostarttime; // for comparative timing purposes -static char demoname[128]; +static char demoname[MAX_WADPATH]; static UINT8 *demobuffer = NULL; static UINT8 *demotime_p, *demoinfo_p; UINT8 *demo_p; @@ -3791,7 +3791,7 @@ void G_SaveDemo(void) demo_slug[strindex] = 0; if (dash) demo_slug[strindex-1] = 0; - writepoint = strstr(demoname, "-") + 1; + writepoint = strstr(strrchr(demoname, *PATHSEP), "-") + 1; demo_slug[128 - (writepoint - demoname) - 4] = 0; sprintf(writepoint, "%s.lmp", demo_slug); } @@ -3808,7 +3808,7 @@ void G_SaveDemo(void) md5_buffer((char *)p+16, (demobuffer + length) - (p+16), p); #endif - if (FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer)) // finally output the file. + if (FIL_WriteFile(demoname, demobuffer, demo_p - demobuffer)) // finally output the file. demo.savemode = DSM_SAVED; free(demobuffer); demo.recording = false; diff --git a/src/p_setup.c b/src/p_setup.c index be07b662e..2fc2ff53c 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3798,12 +3798,14 @@ static void P_InitGametype(void) //@TODO I'd like to fix dedis crashing when recording replays for the future too... if (!demo.playback && multiplayer && !dedicated) { - static char buf[256]; - char *path; - sprintf(buf, "media"PATHSEP"replay"PATHSEP"online"PATHSEP"%d-%s", (int) (time(NULL)), G_BuildMapName(gamemap)); + char buf[MAX_WADPATH]; + int parts; - path = va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"online", srb2home); - M_MkdirEach(path, M_PathParts(path) - 4, 0755); + sprintf(buf, "%s"PATHSEP"media"PATHSEP"replay"PATHSEP"online"PATHSEP"%s-%s"PATHSEP"%d-%s", + srb2home, compbranch, comprevision, (int) (time(NULL)), G_BuildMapName(gamemap)); + + parts = M_PathParts(buf); + M_MkdirEachUntil(buf, parts - 5, parts - 1, 0755); G_RecordDemo(buf); } From 43bd6cd664daefdfa25c0aca0b63e1b510587dd0 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 20 Dec 2021 01:20:17 -0800 Subject: [PATCH 155/379] Replays: use git commit in DEVELOP, version string in release --- src/p_setup.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 2fc2ff53c..c2acee545 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3799,10 +3799,16 @@ static void P_InitGametype(void) if (!demo.playback && multiplayer && !dedicated) { char buf[MAX_WADPATH]; + char ver[128]; int parts; - sprintf(buf, "%s"PATHSEP"media"PATHSEP"replay"PATHSEP"online"PATHSEP"%s-%s"PATHSEP"%d-%s", - srb2home, compbranch, comprevision, (int) (time(NULL)), G_BuildMapName(gamemap)); +#ifdef DEVELOP + sprintf(ver, "%s-%s", compbranch, comprevision); +#else + strcpy(ver, VERSIONSTRING); +#endif + sprintf(buf, "%s"PATHSEP"media"PATHSEP"replay"PATHSEP"online"PATHSEP"%s"PATHSEP"%d-%s", + srb2home, ver, (int) (time(NULL)), G_BuildMapName(gamemap)); parts = M_PathParts(buf); M_MkdirEachUntil(buf, parts - 5, parts - 1, 0755); From b4e056d06fec64c7852fcdb17c5f55abf7b76e74 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 29 Dec 2021 22:13:06 -0800 Subject: [PATCH 156/379] Fix M_MkdirEach and M_PathParts OKAY I didn't test this on Windows before, it was BROKEN AND SHITTY CODE. --- src/m_misc.c | 80 +++++++++++++++++++++++----------------------------- 1 file changed, 36 insertions(+), 44 deletions(-) diff --git a/src/m_misc.c b/src/m_misc.c index ef9a7c837..09cd0636f 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -2672,26 +2672,25 @@ const char *M_FileError(FILE *fp) /** Return the number of parts of this path. */ -int M_PathParts(const char *path) +int M_PathParts(const char *p) { - int n; - const char *p; - const char *t; - if (path == NULL) + int parts = 0; + + if (p == NULL) return 0; - for (n = 0, p = path ;; ++n) + +#ifdef _WIN32 + if (!strncmp(&p[1], ":\\", 2)) + p += 3; +#endif + + while (*(p += strspn(p, PATHSEP))) { - t = p; - if (( p = strchr(p, PATHSEP[0]) )) - p += strspn(p, PATHSEP); - else - { - if (*t)/* there is something after the final delimiter */ - n++; - break; - } + parts++; + p += strcspn(p, PATHSEP); } - return n; + + return parts; } /** Check whether a path is an absolute path. @@ -2709,50 +2708,43 @@ boolean M_IsPathAbsolute(const char *path) */ void M_MkdirEachUntil(const char *cpath, int start, int end, int mode) { - char path[MAX_WADPATH]; + char path[256]; char *p; - char *t; + int n; + int c; if (end > 0 && end <= start) return; strlcpy(path, cpath, sizeof path); + #ifdef _WIN32 - if (strncmp(&path[1], ":\\", 2) == 0) + if (!strncmp(&path[1], ":\\", 2)) p = &path[3]; else #endif p = path; - if (end > 0) - end -= start; - - for (; start > 0; --start) + while (end != 0 && *(p += strspn(p, PATHSEP))) { - p += strspn(p, PATHSEP); - if (!( p = strchr(p, PATHSEP[0]) )) - return; - } - p += strspn(p, PATHSEP); - for (;;) - { - if (end > 0 && !--end) - break; + n = strcspn(p, PATHSEP); - t = p; - if (( p = strchr(p, PATHSEP[0]) )) - { - *p = '\0'; - I_mkdir(path, mode); - *p = PATHSEP[0]; - p += strspn(p, PATHSEP); - } + if (start > 0) + start--; else { - if (*t) - I_mkdir(path, mode); - break; + c = p[n]; + p[n] = '\0'; + + I_mkdir(path, mode); + + p[n] = c; } + + p += n; + + if (end > 0) + end--; } } @@ -2814,4 +2806,4 @@ const char * M_Ftrim (double f) dig[i + 1] = '\0'; return &dig[1];/* skip the 0 */ } -} \ No newline at end of file +} From 196dc77e6faafe41203ef132580c3178ad4562de Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 17 Jan 2022 22:46:01 -0800 Subject: [PATCH 157/379] Track skybox within player struct TODO: Lua Splitscreen and change viewpoint uses the proper skyboxes! --- src/d_player.h | 8 +++++++ src/hardware/hw_main.c | 2 +- src/lua_baselib.c | 4 ++++ src/p_mobj.c | 3 +++ src/p_saveg.c | 34 +++++++++++++++++++++++++++++ src/p_setup.c | 15 ++++++++----- src/p_spec.c | 49 ++++++++++++++++++++++++++++++------------ src/p_spec.h | 1 - src/r_main.c | 19 ++++++++-------- src/r_plane.h | 2 -- src/r_portal.c | 31 +++++++++++++++----------- src/r_portal.h | 4 ++-- 12 files changed, 125 insertions(+), 47 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 738f8240b..4a4b85981 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -290,6 +290,12 @@ typedef struct botvars_s tic_t spindashconfirm; // When high enough, they will try spindashing } botvars_t; +// player_t struct for all skybox variables +typedef struct { + mobj_t * viewpoint; + mobj_t * centerpoint; +} skybox_t; + // ======================================================================== // PLAYER STRUCTURE // ======================================================================== @@ -311,6 +317,8 @@ typedef struct player_s // bounded/scaled total momentum. fixed_t bob; + skybox_t skybox; + angle_t viewrollangle; angle_t old_viewrollangle; // camera tilt diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 82bada373..e8eea7e11 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -6221,7 +6221,7 @@ void HWR_RenderPlayerView(void) const float fpov = FIXED_TO_FLOAT(cv_fov[viewssnum].value+player->fovadd); postimg_t *type = &postimgtype[viewssnum]; - const boolean skybox = (skyboxmo[0] && cv_skybox.value); // True if there's a skybox object and skyboxes are on + const boolean skybox = (player->skybox.viewpoint && cv_skybox.value); // True if there's a skybox object and skyboxes are on FRGBAFloat ClearColor; diff --git a/src/lua_baselib.c b/src/lua_baselib.c index f067aca9a..50c927530 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -2014,8 +2014,12 @@ static int lib_pSetSkyboxMobj(lua_State *L) if (w > 1 || w < 0) return luaL_error(L, "skybox mobj index %d is out of range for P_SetSkyboxMobj argument #2 (expected 0 or 1)", w); +#if 0 if (!user || P_IsLocalPlayer(user)) skyboxmo[w] = mo; +#else + CONS_Alert(CONS_WARNING, "TODO: P_SetSkyboxMobj is unimplemented\n"); +#endif return 0; } diff --git a/src/p_mobj.c b/src/p_mobj.c index 502d38d25..4a147e065 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10860,6 +10860,9 @@ void P_SpawnPlayer(INT32 playernum) p->awayviewmobj = NULL; p->awayviewtics = 0; + p->skybox.viewpoint = skyboxviewpnts[0]; + p->skybox.centerpoint = skyboxcenterpnts[0]; + P_SetTarget(&p->follower, NULL); // cleanse follower from existence // set the scale to the mobj's destscale so settings get correctly set. if we don't, they sometimes don't. diff --git a/src/p_saveg.c b/src/p_saveg.c index 6aff9a33f..0561d365a 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -60,6 +60,8 @@ typedef enum AWAYVIEW = 0x01, FOLLOWITEM = 0x02, FOLLOWER = 0x04, + SKYBOXVIEW = 0x08, + SKYBOXCENTER = 0x10, } player_saveflags; static inline void P_ArchivePlayer(void) @@ -178,6 +180,12 @@ static void P_NetArchivePlayers(void) if (players[i].follower) flags |= FOLLOWER; + if (players[i].skybox.viewpoint) + flags |= SKYBOXVIEW; + + if (players[i].skybox.centerpoint) + flags |= SKYBOXCENTER; + WRITEINT16(save_p, players[i].lastsidehit); WRITEINT16(save_p, players[i].lastlinehit); @@ -190,6 +198,12 @@ static void P_NetArchivePlayers(void) WRITEUINT16(save_p, flags); + if (flags & SKYBOXVIEW) + WRITEUINT32(save_p, players[i].skybox.viewpoint->mobjnum); + + if (flags & SKYBOXCENTER) + WRITEUINT32(save_p, players[i].skybox.centerpoint->mobjnum); + if (flags & AWAYVIEW) WRITEUINT32(save_p, players[i].awayviewmobj->mobjnum); @@ -447,6 +461,12 @@ static void P_NetUnArchivePlayers(void) flags = READUINT16(save_p); + if (flags & SKYBOXVIEW) + players[i].skybox.viewpoint = (mobj_t *)(size_t)READUINT32(save_p); + + if (flags & SKYBOXCENTER) + players[i].skybox.centerpoint = (mobj_t *)(size_t)READUINT32(save_p); + if (flags & AWAYVIEW) players[i].awayviewmobj = (mobj_t *)(size_t)READUINT32(save_p); @@ -4130,6 +4150,20 @@ static void P_RelinkPointers(void) } if (mobj->player) { + if ( mobj->player->skybox.viewpoint) + { + temp = (UINT32)(size_t)mobj->player->skybox.viewpoint; + mobj->player->skybox.viewpoint = NULL; + if (!P_SetTarget(&mobj->player->skybox.viewpoint, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "skybox.viewpoint not found on %d\n", mobj->type); + } + if ( mobj->player->skybox.centerpoint) + { + temp = (UINT32)(size_t)mobj->player->skybox.centerpoint; + mobj->player->skybox.centerpoint = NULL; + if (!P_SetTarget(&mobj->player->skybox.centerpoint, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "skybox.centerpoint not found on %d\n", mobj->type); + } if ( mobj->player->awayviewmobj) { temp = (UINT32)(size_t)mobj->player->awayviewmobj; diff --git a/src/p_setup.c b/src/p_setup.c index c2acee545..ef5d80070 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3514,6 +3514,7 @@ static void P_InitLevelSettings(void) speedscramble = encorescramble = -1; } +#if 0 // Respawns all the mapthings and mobjs in the map from the already loaded map data. void P_RespawnThings(void) { @@ -3548,6 +3549,7 @@ void P_RespawnThings(void) skyboxmo[0] = skyboxviewpnts[(viewid >= 0) ? viewid : 0]; skyboxmo[1] = skyboxcenterpnts[(centerid >= 0) ? centerid : 0]; } +#endif static void P_RunLevelScript(const char *scriptname) { @@ -3622,14 +3624,19 @@ static void P_ResetSpawnpoints(void) // reset the player starts for (i = 0; i < MAXPLAYERS; i++) + { playerstarts[i] = bluectfstarts[i] = redctfstarts[i] = NULL; + if (playeringame[i]) + { + players[i].skybox.viewpoint = NULL; + players[i].skybox.centerpoint = NULL; + } + } + for (i = 0; i < MAX_DM_STARTS; i++) deathmatchstarts[i] = NULL; - for (i = 0; i < 2; i++) - skyboxmo[i] = NULL; - for (i = 0; i < 16; i++) skyboxviewpnts[i] = skyboxcenterpnts[i] = NULL; } @@ -4076,8 +4083,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) P_SpawnSpecialsAfterSlopes(); P_SpawnMapThings(!fromnetsave); - skyboxmo[0] = skyboxviewpnts[0]; - skyboxmo[1] = skyboxcenterpnts[0]; for (numcoopstarts = 0; numcoopstarts < MAXPLAYERS; numcoopstarts++) if (!playerstarts[numcoopstarts]) diff --git a/src/p_spec.c b/src/p_spec.c index fca9e9035..61862df96 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -52,7 +52,6 @@ // Not sure if this is necessary, but it was in w_wad.c, so I'm putting it here too -Shadow Hog #include -mobj_t *skyboxmo[2]; // current skybox mobjs: 0 = viewpoint, 1 = centerpoint mobj_t *skyboxviewpnts[16]; // array of MT_SKYBOX viewpoint mobjs mobj_t *skyboxcenterpnts[16]; // array of MT_SKYBOX centerpoint mobjs @@ -2090,6 +2089,19 @@ static mobj_t *P_GetObjectTypeInSectorNum(mobjtype_t type, size_t s) return NULL; } +static void P_SwitchSkybox(INT32 ldflags, player_t *player, skybox_t *skybox) +{ + if (!(ldflags & ML_EFFECT4)) // Solid Midtexture turns off viewpoint setting + { + player->skybox.viewpoint = skybox->viewpoint; + } + + if (ldflags & ML_BLOCKPLAYERS) // Block Enemies turns ON centerpoint setting + { + player->skybox.centerpoint = skybox->centerpoint; + } +} + /** Processes the line special triggered by an object. * * \param line Line with the special command on it. @@ -3155,7 +3167,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) break; } case 448: // Change skybox viewpoint/centerpoint - if ((mo && mo->player && P_IsLocalPlayer(mo->player)) || (line->flags & ML_NOCLIMB)) + if ((mo && mo->player) || (line->flags & ML_NOCLIMB)) { INT32 viewid = sides[line->sidenum[0]].textureoffset>>FRACBITS; INT32 centerid = sides[line->sidenum[0]].rowoffset>>FRACBITS; @@ -3168,23 +3180,32 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) } else { + skybox_t skybox; + // set viewpoint mobj - if (!(line->flags & ML_EFFECT4)) // Solid Midtexture turns off viewpoint setting - { - if (viewid >= 0 && viewid < 16) - skyboxmo[0] = skyboxviewpnts[viewid]; - else - skyboxmo[0] = NULL; - } + if (viewid >= 0 && viewid < 16) + skybox.viewpoint = skyboxviewpnts[viewid]; + else + skybox.viewpoint = NULL; // set centerpoint mobj - if (line->flags & ML_BLOCKPLAYERS) // Block Enemies turns ON centerpoint setting + if (centerid >= 0 && centerid < 16) + skybox.centerpoint = skyboxcenterpnts[centerid]; + else + skybox.centerpoint = NULL; + + if (line->flags & ML_NOCLIMB) // Applies to all players { - if (centerid >= 0 && centerid < 16) - skyboxmo[1] = skyboxcenterpnts[centerid]; - else - skyboxmo[1] = NULL; + INT32 i; + + for (i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i]) + P_SwitchSkybox(line->flags, &players[i], &skybox); + } } + else + P_SwitchSkybox(line->flags, mo->player, &skybox); } CONS_Debug(DBG_GAMELOGIC, "Line type 448 Executor: viewid = %d, centerid = %d, viewpoint? = %s, centerpoint? = %s\n", diff --git a/src/p_spec.h b/src/p_spec.h index 9181dee7a..03f4c32fe 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -17,7 +17,6 @@ #ifndef __P_SPEC__ #define __P_SPEC__ -extern mobj_t *skyboxmo[2]; // current skybox mobjs: 0 = viewpoint, 1 = centerpoint extern mobj_t *skyboxviewpnts[16]; // array of MT_SKYBOX viewpoint mobjs extern mobj_t *skyboxcenterpnts[16]; // array of MT_SKYBOX centerpoint mobjs diff --git a/src/r_main.c b/src/r_main.c index 0803f5a9e..f386df629 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -28,7 +28,7 @@ #include "am_map.h" #include "d_main.h" #include "v_video.h" -#include "p_spec.h" // skyboxmo +//#include "p_spec.h" #include "p_setup.h" #include "z_zone.h" #include "m_random.h" // quake camera shake @@ -1331,7 +1331,7 @@ void R_SkyboxFrame(player_t *player) // cut-away view stuff newview->sky = true; - r_viewmobj = skyboxmo[0]; + r_viewmobj = player->skybox.viewpoint; #ifdef PARANOIA if (!r_viewmobj) { @@ -1372,6 +1372,7 @@ void R_SkyboxFrame(player_t *player) { mapheader_t *mh = mapheaderinfo[gamemap-1]; vector3_t campos = {0,0,0}; // Position of player's actual view point + mobj_t *center = player->skybox.centerpoint; if (player->awayviewtics) { campos.x = player->awayviewmobj->x; @@ -1393,18 +1394,18 @@ void R_SkyboxFrame(player_t *player) campos.y += quake.y; campos.z += quake.z; - if (skyboxmo[1]) // Is there a viewpoint? + if (center) // Is there a viewpoint? { fixed_t x = 0, y = 0; if (mh->skybox_scalex > 0) - x = (campos.x - skyboxmo[1]->x) / mh->skybox_scalex; + x = (campos.x - center->x) / mh->skybox_scalex; else if (mh->skybox_scalex < 0) - x = (campos.x - skyboxmo[1]->x) * -mh->skybox_scalex; + x = (campos.x - center->x) * -mh->skybox_scalex; if (mh->skybox_scaley > 0) - y = (campos.y - skyboxmo[1]->y) / mh->skybox_scaley; + y = (campos.y - center->y) / mh->skybox_scaley; else if (mh->skybox_scaley < 0) - y = (campos.y - skyboxmo[1]->y) * -mh->skybox_scaley; + y = (campos.y - center->y) * -mh->skybox_scaley; if (r_viewmobj->angle == 0) { @@ -1617,8 +1618,8 @@ void R_RenderPlayerView(void) // Add skybox portals caused by sky visplanes. - if (cv_skybox.value && skyboxmo[0]) - Portal_AddSkyboxPortals(); + if (cv_skybox.value && player->skybox.viewpoint) + Portal_AddSkyboxPortals(player); // Portal rendering. Hijacks the BSP traversal. ps_sw_portaltime = I_GetPreciseTime(); diff --git a/src/r_plane.h b/src/r_plane.h index 41305fb3c..fb489f704 100644 --- a/src/r_plane.h +++ b/src/r_plane.h @@ -127,6 +127,4 @@ typedef struct planemgr_s extern visffloor_t ffloor[MAXFFLOORS]; extern INT32 numffloors; - -void Portal_AddSkyboxPortals (void); #endif diff --git a/src/r_portal.c b/src/r_portal.c index 1aca145ec..66763dff3 100644 --- a/src/r_portal.c +++ b/src/r_portal.c @@ -256,12 +256,17 @@ static boolean TrimVisplaneBounds (const visplane_t* plane, INT16* start, INT16* * Applies the necessary offsets and rotation to give * a depth illusion to the skybox. */ -void Portal_AddSkybox (const visplane_t* plane) +void Portal_AddSkybox +( const player_t * player, + const visplane_t * plane) { INT16 start, end; mapheader_t *mh; portal_t* portal; + mobj_t *viewpoint = player->skybox.viewpoint; + mobj_t *center = player->skybox.centerpoint; + if (TrimVisplaneBounds(plane, &start, &end)) return; @@ -269,28 +274,28 @@ void Portal_AddSkybox (const visplane_t* plane) Portal_ClipVisplane(plane, portal); - portal->viewx = skyboxmo[0]->x; - portal->viewy = skyboxmo[0]->y; - portal->viewz = skyboxmo[0]->z; - portal->viewangle = viewangle + skyboxmo[0]->angle; + portal->viewx = viewpoint->x; + portal->viewy = viewpoint->y; + portal->viewz = viewpoint->z; + portal->viewangle = viewangle + viewpoint->angle; mh = mapheaderinfo[gamemap-1]; // If a relative viewpoint exists, offset the viewpoint. - if (skyboxmo[1]) + if (center) { fixed_t x = 0, y = 0; - angle_t ang = skyboxmo[0]->angle>>ANGLETOFINESHIFT; + angle_t ang = viewpoint->angle>>ANGLETOFINESHIFT; if (mh->skybox_scalex > 0) - x = (viewx - skyboxmo[1]->x) / mh->skybox_scalex; + x = (viewx - center->x) / mh->skybox_scalex; else if (mh->skybox_scalex < 0) - x = (viewx - skyboxmo[1]->x) * -mh->skybox_scalex; + x = (viewx - center->x) * -mh->skybox_scalex; if (mh->skybox_scaley > 0) - y = (viewy - skyboxmo[1]->y) / mh->skybox_scaley; + y = (viewy - center->y) / mh->skybox_scaley; else if (mh->skybox_scaley < 0) - y = (viewy - skyboxmo[1]->y) * -mh->skybox_scaley; + y = (viewy - center->y) * -mh->skybox_scaley; // Apply transform to account for the skybox viewport angle. portal->viewx += FixedMul(x,FINECOSINE(ang)) - FixedMul(y, FINESINE(ang)); @@ -308,7 +313,7 @@ void Portal_AddSkybox (const visplane_t* plane) /** Creates portals for the currently existing sky visplanes. * The visplanes are also removed and cleared from the list. */ -void Portal_AddSkyboxPortals (void) +void Portal_AddSkyboxPortals (const player_t *player) { visplane_t *pl; INT32 i; @@ -320,7 +325,7 @@ void Portal_AddSkyboxPortals (void) { if (pl->picnum == skyflatnum) { - Portal_AddSkybox(pl); + Portal_AddSkybox(player, pl); pl->minx = 0; pl->maxx = -1; diff --git a/src/r_portal.h b/src/r_portal.h index e665a26e6..3e77b2ef7 100644 --- a/src/r_portal.h +++ b/src/r_portal.h @@ -52,10 +52,10 @@ extern INT32 portalclipstart, portalclipend; void Portal_InitList (void); void Portal_Remove (portal_t* portal); void Portal_Add2Lines (const INT32 line1, const INT32 line2, const INT32 x1, const INT32 x2); -void Portal_AddSkybox (const visplane_t* plane); +void Portal_AddSkybox (const player_t* player, const visplane_t* plane); void Portal_ClipRange (portal_t* portal); void Portal_ClipApply (const portal_t* portal); -void Portal_AddSkyboxPortals (void); +void Portal_AddSkyboxPortals (const player_t* player); #endif From 10a9663ccb8f2308187362e8d0453b0b8135be1b Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 17 Jan 2022 21:18:38 -0800 Subject: [PATCH 158/379] Interpolate viewrollangle before R_CheckViewMorph --- src/d_main.c | 2 ++ src/r_fps.c | 7 ++++++- src/r_fps.h | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/d_main.c b/src/d_main.c index 37ed9c4bc..91d13dd75 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -296,6 +296,8 @@ static void D_Display(void) { for (i = 0; i <= r_splitscreen; ++i) { + R_SetViewContext(VIEWCONTEXT_PLAYER1 + i); + R_InterpolateViewRollAngle(rendertimefrac); R_CheckViewMorph(i); } } diff --git a/src/r_fps.c b/src/r_fps.c index 67f96498d..34e2e09a8 100644 --- a/src/r_fps.c +++ b/src/r_fps.c @@ -80,6 +80,11 @@ static void R_SetupFreelook(player_t *player, boolean skybox) #undef AIMINGTODY +void R_InterpolateViewRollAngle(fixed_t frac) +{ + viewroll = oldview->roll + R_LerpAngle(oldview->roll, newview->roll, frac); +} + void R_InterpolateView(fixed_t frac) { if (frac < 0) @@ -95,7 +100,7 @@ void R_InterpolateView(fixed_t frac) viewangle = oldview->angle + R_LerpAngle(oldview->angle, newview->angle, frac); aimingangle = oldview->aim + R_LerpAngle(oldview->aim, newview->aim, frac); - viewroll = oldview->roll + R_LerpAngle(oldview->roll, newview->roll, frac); + R_InterpolateViewRollAngle(frac); viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT); viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT); diff --git a/src/r_fps.h b/src/r_fps.h index 246c16e64..c93c4701e 100644 --- a/src/r_fps.h +++ b/src/r_fps.h @@ -51,6 +51,8 @@ extern viewvars_t *newview; // Interpolates the current view variables (r_state.h) against the selected view context in R_SetViewContext void R_InterpolateView(fixed_t frac); +// Special function just for software +void R_InterpolateViewRollAngle(fixed_t frac); // Buffer the current new views into the old views. Call once after each real tic. void R_UpdateViewInterpolation(void); // Set the current view context (the viewvars pointed to by newview) From 99e8ab9b58e7aca3fdfeaadaef9bb3edcd1c7124 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Tue, 18 Jan 2022 22:41:07 -0600 Subject: [PATCH 159/379] Director cam barebones functionality --- src/Sourcefile | 1 + src/k_director.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++ src/k_director.h | 7 ++ src/k_kart.c | 5 ++ src/p_tick.c | 2 + 5 files changed, 184 insertions(+) create mode 100644 src/k_director.c create mode 100644 src/k_director.h diff --git a/src/Sourcefile b/src/Sourcefile index 67844d410..8c3bff8d1 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -115,3 +115,4 @@ k_menufunc.c k_menudraw.c k_brightmap.c k_terrain.c +k_director.c diff --git a/src/k_director.c b/src/k_director.c new file mode 100644 index 000000000..685e581c9 --- /dev/null +++ b/src/k_director.c @@ -0,0 +1,169 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +/// \file k_director.c +/// \brief SRB2kart automatic spectator camera. + +#include "k_kart.h" +#include "k_respawn.h" +#include "doomdef.h" +#include "g_game.h" +#include "v_video.h" +#include "k_director.h" +#include "d_netcmd.h" +#include "p_local.h" + +#define SWITCHTIME TICRATE*5 +#define DEBOUNCETIME TICRATE*2 +#define BOREDOMTIME 3*TICRATE/2 +#define TRANSFERTIME TICRATE +#define BREAKAWAYDIST 4000 +#define CONFUSINGDIST 250 + +INT32 FindPlayerByPlace(INT32 place); +void K_UpdateDirectorPositions(void); +boolean K_CanSwitchDirector(void); +fixed_t K_GetFinishGap(INT32 leader, INT32 follower); +void K_DirectorSwitch(INT32 player); +void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source); +void K_UpdateDirector(void); + +INT32 cooldown = 0; +INT32 bored = 0; +INT32 positions[MAXPLAYERS] = {0}; +INT32 confidence[MAXPLAYERS] = {0}; + +INT32 freeze = 0; +INT32 attacker = 0; + +INT32 FindPlayerByPlace(INT32 place) +{ + INT32 playernum; + for (playernum = 0; playernum < MAXPLAYERS; ++playernum) + if (playeringame[playernum]) + { + if (players[playernum].position == place) + { + return playernum; + } + } + return -1; +} + +void K_UpdateDirectorPositions(void) { + INT32 playernum; + for (playernum = 0; playernum < MAXPLAYERS; ++playernum) + if (playeringame[playernum]) + { + if (players[playernum].position == positions[playernum]) { + confidence[playernum]++; + } + else { + confidence[playernum] = 0; + positions[playernum] = players[playernum].position; + } + } else { + positions[playernum] = 0; + confidence[playernum] = 0; + } +} + +boolean K_CanSwitchDirector(void) { + INT32 *displayplayerp = &displayplayers[0]; + if (players[*displayplayerp].trickpanel > 0) { + // CONS_Printf("NO SWITCH, panel"); + return false; + } + return cooldown >= SWITCHTIME; +} + +fixed_t K_GetFinishGap(INT32 leader, INT32 follower) { + fixed_t dista = players[follower].distancetofinish; + fixed_t distb = players[leader].distancetofinish; + if (players[follower].position < players[leader].position) { + return distb-dista; + } else { + return dista-distb; + } +} + +void K_DirectorSwitch(INT32 player) { + if (!K_CanSwitchDirector()) + return; + // CONS_Printf("SWITCHING: %s\n", player_names[player]); + G_ResetView(1, player, true); + cooldown = 0; +} + +void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source) { + if (!P_IsDisplayPlayer(player)) + return; + if (inflictor && inflictor->player) { + // CONS_Printf("INFLICTOR SET\n"); + attacker = inflictor->player-players; + freeze = TRANSFERTIME; + cooldown = SWITCHTIME; + } + if (source && source->player) { + // CONS_Printf("SOURCE SET\n"); + attacker = source->player-players; + freeze = TRANSFERTIME; + cooldown = SWITCHTIME; + } + // CONS_Printf("FOLLOW ATTACK\n"); + // CONS_Printf(M_GetText("%s attacked\n"), player_names[attacker]); +} + +void K_UpdateDirector(void) { + INT32 *displayplayerp = &displayplayers[0]; + INT32 targetposition; + + K_UpdateDirectorPositions(); + cooldown++; + + if (freeze >= 1) { + // CONS_Printf("FROZEN\n"); + if (freeze == 1) { + K_DirectorSwitch(attacker); + // CONS_Printf("ATTACKER SWITCH\n"); + } + freeze--; + return; + } + + for(targetposition = 2; targetposition < MAXPLAYERS; targetposition++) { + INT32 leader = FindPlayerByPlace(targetposition - 1); + INT32 follower = FindPlayerByPlace(targetposition); + fixed_t gap = K_GetFinishGap(leader, follower); + + /* + CONS_Printf("Eval %d GP %d CD %d FC %d DC %d\n", + targetposition, gap, cooldown, confidence[follower], confidence[*displayplayerp]); + */ + + if (gap > BREAKAWAYDIST) { + bored++; + if (bored > BOREDOMTIME) { + // CONS_Printf("BREAKAWAY, falling back to %d\n", targetposition); + continue; + } + } else if (bored > 0) { + bored--; + } + + /* + if (gap < CONFUSINGDIST && *displayplayerp == leader) { + CONS_Printf("No switch: too close\n"); + break; + } + */ + + if (*displayplayerp != follower && confidence[follower] > DEBOUNCETIME) { + K_DirectorSwitch(FindPlayerByPlace(targetposition)); + } + if (positions[*displayplayerp] != targetposition && confidence[*displayplayerp] > DEBOUNCETIME) { + K_DirectorSwitch(FindPlayerByPlace(targetposition)); + } + + break; + } +} \ No newline at end of file diff --git a/src/k_director.h b/src/k_director.h new file mode 100644 index 000000000..96701ffa7 --- /dev/null +++ b/src/k_director.h @@ -0,0 +1,7 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +/// \file k_director.h +/// \brief SRB2kart automatic spectator camera. + +void K_UpdateDirector(void); +void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source); \ No newline at end of file diff --git a/src/k_kart.c b/src/k_kart.c index b9fff95ba..6c61ab6b1 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -34,6 +34,7 @@ #include "k_bot.h" #include "k_hud.h" #include "k_terrain.h" +#include "k_director.h" // SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H: // gamespeed is cc (0 for easy, 1 for normal, 2 for hard) @@ -3231,6 +3232,7 @@ void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UIN void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type) { + K_DirectorFollowAttack(player, inflictor, source); (void)inflictor; (void)source; @@ -3273,6 +3275,7 @@ static void K_RemoveGrowShrink(player_t *player) void K_TumblePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) { + K_DirectorFollowAttack(player, inflictor, source); fixed_t gravityadjust; (void)source; @@ -3410,6 +3413,8 @@ INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) // A { INT32 ringburst = 10; + K_DirectorFollowAttack(player, inflictor, source); + (void)source; player->mo->momz = 18*mapobjectscale*P_MobjFlip(player->mo); // please stop forgetting mobjflip checks!!!! diff --git a/src/p_tick.c b/src/p_tick.c index bbfbc8e0a..6a26b43e2 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -706,6 +706,8 @@ void P_Ticker(boolean run) } } + K_UpdateDirector(); + // Always move the camera. for (i = 0; i <= r_splitscreen; i++) { From aa95f4b8dd3adfc0dcc1b20090c6980e8e667552 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Wed, 19 Jan 2022 23:56:23 -0600 Subject: [PATCH 160/379] Write the rest of the owl --- src/k_director.c | 219 +++++++++++++++++++++++++---------------------- src/k_director.h | 1 + src/k_hud.c | 2 + 3 files changed, 121 insertions(+), 101 deletions(-) diff --git a/src/k_director.c b/src/k_director.c index 685e581c9..26358680f 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -13,68 +13,30 @@ #include "p_local.h" #define SWITCHTIME TICRATE*5 -#define DEBOUNCETIME TICRATE*2 +#define DEBOUNCETIME TICRATE/6 #define BOREDOMTIME 3*TICRATE/2 #define TRANSFERTIME TICRATE #define BREAKAWAYDIST 4000 -#define CONFUSINGDIST 250 +#define WALKBACKDIST 600 +#define PINCHDIST 30000 -INT32 FindPlayerByPlace(INT32 place); void K_UpdateDirectorPositions(void); boolean K_CanSwitchDirector(void); fixed_t K_GetFinishGap(INT32 leader, INT32 follower); -void K_DirectorSwitch(INT32 player); +void K_DirectorSwitch(INT32 player, boolean force); void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source); void K_UpdateDirector(void); +void K_DirectorForceSwitch(INT32 player, INT32 time); -INT32 cooldown = 0; -INT32 bored = 0; -INT32 positions[MAXPLAYERS] = {0}; -INT32 confidence[MAXPLAYERS] = {0}; +INT32 cooldown = 0; // how long has it been since we last switched? +INT32 freeze = 0; // when nonzero, fixed switch pending, freeze logic! +INT32 attacker = 0; // who to switch to when freeze delay elapses +INT32 maxdist = 0; -INT32 freeze = 0; -INT32 attacker = 0; +INT32 sortedplayers[MAXPLAYERS] = {0}; // position-1 goes in, player index comes out. +INT32 gap[MAXPLAYERS] = {0}; // gap between a given position and their closest pursuer +INT32 boredom[MAXPLAYERS] = {0}; // how long has a given position had no credible attackers? -INT32 FindPlayerByPlace(INT32 place) -{ - INT32 playernum; - for (playernum = 0; playernum < MAXPLAYERS; ++playernum) - if (playeringame[playernum]) - { - if (players[playernum].position == place) - { - return playernum; - } - } - return -1; -} - -void K_UpdateDirectorPositions(void) { - INT32 playernum; - for (playernum = 0; playernum < MAXPLAYERS; ++playernum) - if (playeringame[playernum]) - { - if (players[playernum].position == positions[playernum]) { - confidence[playernum]++; - } - else { - confidence[playernum] = 0; - positions[playernum] = players[playernum].position; - } - } else { - positions[playernum] = 0; - confidence[playernum] = 0; - } -} - -boolean K_CanSwitchDirector(void) { - INT32 *displayplayerp = &displayplayers[0]; - if (players[*displayplayerp].trickpanel > 0) { - // CONS_Printf("NO SWITCH, panel"); - return false; - } - return cooldown >= SWITCHTIME; -} fixed_t K_GetFinishGap(INT32 leader, INT32 follower) { fixed_t dista = players[follower].distancetofinish; @@ -86,31 +48,88 @@ fixed_t K_GetFinishGap(INT32 leader, INT32 follower) { } } -void K_DirectorSwitch(INT32 player) { - if (!K_CanSwitchDirector()) +void K_UpdateDirectorPositions(void) { + INT32 playernum; + + memset(sortedplayers, -1, sizeof(sortedplayers)); + for (playernum = 0; playernum < MAXPLAYERS; ++playernum) { + if (playeringame[playernum]) + sortedplayers[players[playernum].position-1] = playernum; + } + + memset(gap, INT32_MAX, sizeof(gap)); + for (playernum = 0; playernum < MAXPLAYERS-1; ++playernum) { + if (sortedplayers[playernum] == -1 || sortedplayers[playernum+1] == -1) + break; + gap[playernum] = P_ScaleFromMap(K_GetFinishGap(sortedplayers[playernum], sortedplayers[playernum+1]), FRACUNIT); + if (gap[playernum] >= BREAKAWAYDIST) + boredom[playernum] = min(BOREDOMTIME*2, boredom[playernum]+1); + else if (boredom[playernum] > 0) + boredom[playernum]--; + } + + maxdist = P_ScaleFromMap(players[sortedplayers[0]].distancetofinish, FRACUNIT); +} + +boolean K_CanSwitchDirector(void) { + INT32 *displayplayerp = &displayplayers[0]; + if (players[*displayplayerp].trickpanel > 0) + return false; + return cooldown >= SWITCHTIME; +} + +void K_DirectorSwitch(INT32 player, boolean force) { + if (P_IsDisplayPlayer(&players[player])) + return; + if (!force && !K_CanSwitchDirector()) return; - // CONS_Printf("SWITCHING: %s\n", player_names[player]); G_ResetView(1, player, true); cooldown = 0; } +void K_DirectorForceSwitch(INT32 player, INT32 time) { + attacker = player; + freeze = time; +} + void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source) { if (!P_IsDisplayPlayer(player)) return; - if (inflictor && inflictor->player) { - // CONS_Printf("INFLICTOR SET\n"); - attacker = inflictor->player-players; - freeze = TRANSFERTIME; - cooldown = SWITCHTIME; + if (inflictor && inflictor->player) + K_DirectorForceSwitch(inflictor->player-players, TRANSFERTIME); + if (source && source->player) + K_DirectorForceSwitch(source->player-players, TRANSFERTIME); +} + +void K_DrawDirectorDebugger(void) { + INT32 playernum; + INT32 leader; + INT32 follower; + INT32 ytxt; + V_DrawThinString(10, 0, V_70TRANS, va("PLACE")); + V_DrawThinString(40, 0, V_70TRANS, va("CONF?")); + V_DrawThinString(80, 0, V_70TRANS, va("GAP")); + V_DrawThinString(120, 0, V_70TRANS, va("BORED")); + V_DrawThinString(150, 0, V_70TRANS, va("COOLDOWN: %d", cooldown)); + V_DrawThinString(230, 0, V_70TRANS, va("MAXDIST: %d", maxdist)); + for (playernum = 0; playernum < MAXPLAYERS-1; ++playernum) { + ytxt = 10*playernum+10; + leader = sortedplayers[playernum]; + follower = sortedplayers[playernum+1]; + if (leader == -1 || follower == -1) + break; + V_DrawThinString(10, ytxt, V_70TRANS, va("%d", playernum)); + V_DrawThinString(20, ytxt, V_70TRANS, va("%d", playernum+1)); + if (players[leader].positiondelay) + V_DrawThinString(40, ytxt, V_70TRANS, va("NG")); + V_DrawThinString(80, ytxt, V_70TRANS, va("%d", gap[playernum])); + if (boredom[playernum] >= BOREDOMTIME) + V_DrawThinString(120, ytxt, V_70TRANS, va("BORED")); + else + V_DrawThinString(120, ytxt, V_70TRANS, va("%d", boredom[playernum])); + V_DrawThinString(150, ytxt, V_70TRANS, va("%s", player_names[leader])); + V_DrawThinString(230, ytxt, V_70TRANS, va("%s", player_names[follower])); } - if (source && source->player) { - // CONS_Printf("SOURCE SET\n"); - attacker = source->player-players; - freeze = TRANSFERTIME; - cooldown = SWITCHTIME; - } - // CONS_Printf("FOLLOW ATTACK\n"); - // CONS_Printf(M_GetText("%s attacked\n"), player_names[attacker]); } void K_UpdateDirector(void) { @@ -120,50 +139,48 @@ void K_UpdateDirector(void) { K_UpdateDirectorPositions(); cooldown++; - if (freeze >= 1) { - // CONS_Printf("FROZEN\n"); - if (freeze == 1) { - K_DirectorSwitch(attacker); - // CONS_Printf("ATTACKER SWITCH\n"); - } + // handle pending forced switches + if (freeze > 0) { + if (freeze == 1) + K_DirectorSwitch(attacker, true); freeze--; return; } - for(targetposition = 2; targetposition < MAXPLAYERS; targetposition++) { - INT32 leader = FindPlayerByPlace(targetposition - 1); - INT32 follower = FindPlayerByPlace(targetposition); - fixed_t gap = K_GetFinishGap(leader, follower); + // aaight, time to walk through the standings to find the first interesting pair + for(targetposition = 0; targetposition < MAXPLAYERS; targetposition++) { + INT32 target; - /* - CONS_Printf("Eval %d GP %d CD %d FC %d DC %d\n", - targetposition, gap, cooldown, confidence[follower], confidence[*displayplayerp]); - */ - - if (gap > BREAKAWAYDIST) { - bored++; - if (bored > BOREDOMTIME) { - // CONS_Printf("BREAKAWAY, falling back to %d\n", targetposition); - continue; - } - } else if (bored > 0) { - bored--; - } - - /* - if (gap < CONFUSINGDIST && *displayplayerp == leader) { - CONS_Printf("No switch: too close\n"); + // you are out of players, try again + if (sortedplayers[targetposition+1] == -1) break; - } - */ - if (*displayplayerp != follower && confidence[follower] > DEBOUNCETIME) { - K_DirectorSwitch(FindPlayerByPlace(targetposition)); - } - if (positions[*displayplayerp] != targetposition && confidence[*displayplayerp] > DEBOUNCETIME) { - K_DirectorSwitch(FindPlayerByPlace(targetposition)); + // pair too far apart? try the next one + if (boredom[targetposition] >= BOREDOMTIME) + continue; + + // pair finished? try the next one + if (players[sortedplayers[targetposition+1]].exiting) + continue; + + if (maxdist > PINCHDIST) { + // if the "next" player is close enough, they should be able to see everyone fine! + // walk back through the standings to find a vantage that gets everyone in frame. + while (targetposition < MAXPLAYERS && gap[targetposition+1] < WALKBACKDIST) { + targetposition++; + } } + + target = sortedplayers[targetposition+1]; + + // if we're certain the back half of the pair is actually in this position, try to switch + if (*displayplayerp != target && !players[target].positiondelay) + K_DirectorSwitch(target, false); + // even if we're not certain, if we're certain we're watching the WRONG player, try to switch + if (players[*displayplayerp].position != targetposition && !players[target].positiondelay) + K_DirectorSwitch(target, false); + break; } } \ No newline at end of file diff --git a/src/k_director.h b/src/k_director.h index 96701ffa7..a7d9fd2a6 100644 --- a/src/k_director.h +++ b/src/k_director.h @@ -4,4 +4,5 @@ /// \brief SRB2kart automatic spectator camera. void K_UpdateDirector(void); +void K_DrawDirectorDebugger(void); void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source); \ No newline at end of file diff --git a/src/k_hud.c b/src/k_hud.c index fd83afd56..62253ba68 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -13,6 +13,7 @@ #include "k_kart.h" #include "k_battle.h" #include "k_color.h" +#include "k_director.h" #include "screen.h" #include "doomtype.h" #include "doomdef.h" @@ -4504,6 +4505,7 @@ void K_drawKartHUD(void) } K_DrawWaypointDebugger(); + K_DrawDirectorDebugger(); if (gametype == GT_BATTLE) { From 1d4a99b6a8dc1f8625eb7dfa766088f8f00d0a67 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 20 Jan 2022 02:07:48 -0600 Subject: [PATCH 161/379] very small tiny fixes --- src/d_netcmd.c | 1 + src/d_netcmd.h | 2 +- src/k_director.c | 15 ++++++++++++--- src/k_kart.c | 1 + 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index faecdae4d..f6b272cb6 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -433,6 +433,7 @@ consvar_t cv_kartdebugbotpredict = CVAR_INIT ("kartdebugbotpredict", "Off", CV_N consvar_t cv_kartdebugcheckpoint = CVAR_INIT ("kartdebugcheckpoint", "Off", CV_NOSHOWHELP, CV_OnOff, NULL); consvar_t cv_kartdebugnodes = CVAR_INIT ("kartdebugnodes", "Off", CV_NOSHOWHELP, CV_OnOff, NULL); consvar_t cv_kartdebugcolorize = CVAR_INIT ("kartdebugcolorize", "Off", CV_NOSHOWHELP, CV_OnOff, NULL); +consvar_t cv_kartdebugdirector = CVAR_INIT ("kartdebugdirector", "Off", CV_NOSHOWHELP, CV_OnOff, NULL); static CV_PossibleValue_t votetime_cons_t[] = {{10, "MIN"}, {3600, "MAX"}, {0, NULL}}; consvar_t cv_votetime = CVAR_INIT ("votetime", "20", CV_NETVAR, votetime_cons_t, NULL); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 0e8ef25e2..4813dd07e 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -87,7 +87,7 @@ extern consvar_t cv_kartusepwrlv; extern consvar_t cv_votetime; extern consvar_t cv_kartdebugitem, cv_kartdebugamount, cv_kartallowgiveitem, cv_kartdebugshrink, cv_kartdebugdistribution, cv_kartdebughuddrop; -extern consvar_t cv_kartdebugcheckpoint, cv_kartdebugnodes, cv_kartdebugcolorize; +extern consvar_t cv_kartdebugcheckpoint, cv_kartdebugnodes, cv_kartdebugcolorize, cv_kartdebugdirector; extern consvar_t cv_kartdebugwaypoints, cv_kartdebugbotpredict; extern consvar_t cv_itemfinder; diff --git a/src/k_director.c b/src/k_director.c index 26358680f..3461a6327 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -37,7 +37,6 @@ INT32 sortedplayers[MAXPLAYERS] = {0}; // position-1 goes in, player index comes INT32 gap[MAXPLAYERS] = {0}; // gap between a given position and their closest pursuer INT32 boredom[MAXPLAYERS] = {0}; // how long has a given position had no credible attackers? - fixed_t K_GetFinishGap(INT32 leader, INT32 follower) { fixed_t dista = players[follower].distancetofinish; fixed_t distb = players[leader].distancetofinish; @@ -75,12 +74,16 @@ boolean K_CanSwitchDirector(void) { INT32 *displayplayerp = &displayplayers[0]; if (players[*displayplayerp].trickpanel > 0) return false; - return cooldown >= SWITCHTIME; + if (cooldown < SWITCHTIME); + return false; + return true; } void K_DirectorSwitch(INT32 player, boolean force) { if (P_IsDisplayPlayer(&players[player])) return; + if (players[player].exiting) + return; if (!force && !K_CanSwitchDirector()) return; G_ResetView(1, player, true); @@ -88,6 +91,8 @@ void K_DirectorSwitch(INT32 player, boolean force) { } void K_DirectorForceSwitch(INT32 player, INT32 time) { + if (players[player].exiting) + return; attacker = player; freeze = time; } @@ -106,6 +111,8 @@ void K_DrawDirectorDebugger(void) { INT32 leader; INT32 follower; INT32 ytxt; + if (!cv_kartdebugdirector.value) + return; V_DrawThinString(10, 0, V_70TRANS, va("PLACE")); V_DrawThinString(40, 0, V_70TRANS, va("CONF?")); V_DrawThinString(80, 0, V_70TRANS, va("GAP")); @@ -148,7 +155,7 @@ void K_UpdateDirector(void) { } // aaight, time to walk through the standings to find the first interesting pair - for(targetposition = 0; targetposition < MAXPLAYERS; targetposition++) { + for(targetposition = 0; targetposition < MAXPLAYERS-1; targetposition++) { INT32 target; // you are out of players, try again @@ -163,9 +170,11 @@ void K_UpdateDirector(void) { if (players[sortedplayers[targetposition+1]].exiting) continue; + // don't risk switching away from forward pairs at race end, might miss something! if (maxdist > PINCHDIST) { // if the "next" player is close enough, they should be able to see everyone fine! // walk back through the standings to find a vantage that gets everyone in frame. + // (also creates a pretty cool effect w/ overtakes at speed) while (targetposition < MAXPLAYERS && gap[targetposition+1] < WALKBACKDIST) { targetposition++; } diff --git a/src/k_kart.c b/src/k_kart.c index 6c61ab6b1..d1b654411 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -261,6 +261,7 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_kartdebugcheckpoint); CV_RegisterVar(&cv_kartdebugnodes); CV_RegisterVar(&cv_kartdebugcolorize); + CV_RegisterVar(&cv_kartdebugdirector); } //} From 3fcea2a37ed8c90c552927afda724c1e0551f38f Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 20 Jan 2022 03:39:00 -0600 Subject: [PATCH 162/379] my workflow is beyond your understanding --- src/d_netcmd.c | 4 ++++ src/d_netcmd.h | 2 ++ src/k_director.c | 25 +++++++++++++++---------- src/k_kart.c | 3 ++- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f6b272cb6..2d80f31fd 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -510,6 +510,8 @@ static CV_PossibleValue_t perfstats_cons_t[] = { {0, "Off"}, {1, "Rendering"}, {2, "Logic"}, {3, "ThinkFrame"}, {0, NULL}}; consvar_t cv_perfstats = CVAR_INIT ("perfstats", "Off", 0, perfstats_cons_t, NULL); +consvar_t cv_director = CVAR_INIT ("director", "Off", 0, CV_OnOff, NULL); + char timedemo_name[256]; boolean timedemo_csv; char timedemo_csv_id[256]; @@ -736,6 +738,8 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_showping); CV_RegisterVar(&cv_showviewpointtext); + CV_RegisterVar(&cv_director); + CV_RegisterVar(&cv_dummyconsvar); #ifdef USE_STUN diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 4813dd07e..ba3c68452 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -113,6 +113,8 @@ extern consvar_t cv_sleep; extern consvar_t cv_perfstats; +extern consvar_t cv_director; + extern char timedemo_name[256]; extern boolean timedemo_csv; extern char timedemo_csv_id[256]; diff --git a/src/k_director.c b/src/k_director.c index 3461a6327..0de6b8957 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -12,13 +12,12 @@ #include "d_netcmd.h" #include "p_local.h" -#define SWITCHTIME TICRATE*5 -#define DEBOUNCETIME TICRATE/6 -#define BOREDOMTIME 3*TICRATE/2 -#define TRANSFERTIME TICRATE -#define BREAKAWAYDIST 4000 -#define WALKBACKDIST 600 -#define PINCHDIST 30000 +#define SWITCHTIME TICRATE*5 // cooldown between unforced switches +#define BOREDOMTIME 3*TICRATE/2 // how long until players considered far apart? +#define TRANSFERTIME TICRATE // how long to delay reaction shots? +#define BREAKAWAYDIST 4000 // how *far* until players considered far apart? +#define WALKBACKDIST 600 // how close should a trailing player be before we switch? +#define PINCHDIST 30000 // how close should the leader be to be considered "end of race"? void K_UpdateDirectorPositions(void); boolean K_CanSwitchDirector(void); @@ -31,7 +30,7 @@ void K_DirectorForceSwitch(INT32 player, INT32 time); INT32 cooldown = 0; // how long has it been since we last switched? INT32 freeze = 0; // when nonzero, fixed switch pending, freeze logic! INT32 attacker = 0; // who to switch to when freeze delay elapses -INT32 maxdist = 0; +INT32 maxdist = 0; // how far is the closest player from finishing? INT32 sortedplayers[MAXPLAYERS] = {0}; // position-1 goes in, player index comes out. INT32 gap[MAXPLAYERS] = {0}; // gap between a given position and their closest pursuer @@ -74,7 +73,7 @@ boolean K_CanSwitchDirector(void) { INT32 *displayplayerp = &displayplayers[0]; if (players[*displayplayerp].trickpanel > 0) return false; - if (cooldown < SWITCHTIME); + if (cooldown < SWITCHTIME) return false; return true; } @@ -111,20 +110,24 @@ void K_DrawDirectorDebugger(void) { INT32 leader; INT32 follower; INT32 ytxt; + if (!cv_kartdebugdirector.value) return; + V_DrawThinString(10, 0, V_70TRANS, va("PLACE")); V_DrawThinString(40, 0, V_70TRANS, va("CONF?")); V_DrawThinString(80, 0, V_70TRANS, va("GAP")); V_DrawThinString(120, 0, V_70TRANS, va("BORED")); V_DrawThinString(150, 0, V_70TRANS, va("COOLDOWN: %d", cooldown)); V_DrawThinString(230, 0, V_70TRANS, va("MAXDIST: %d", maxdist)); + for (playernum = 0; playernum < MAXPLAYERS-1; ++playernum) { ytxt = 10*playernum+10; leader = sortedplayers[playernum]; follower = sortedplayers[playernum+1]; if (leader == -1 || follower == -1) break; + V_DrawThinString(10, ytxt, V_70TRANS, va("%d", playernum)); V_DrawThinString(20, ytxt, V_70TRANS, va("%d", playernum+1)); if (players[leader].positiondelay) @@ -143,6 +146,9 @@ void K_UpdateDirector(void) { INT32 *displayplayerp = &displayplayers[0]; INT32 targetposition; + if (!cv_director.value) + return; + K_UpdateDirectorPositions(); cooldown++; @@ -180,7 +186,6 @@ void K_UpdateDirector(void) { } } - target = sortedplayers[targetposition+1]; // if we're certain the back half of the pair is actually in this position, try to switch diff --git a/src/k_kart.c b/src/k_kart.c index d1b654411..4332d4416 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3276,10 +3276,11 @@ static void K_RemoveGrowShrink(player_t *player) void K_TumblePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) { - K_DirectorFollowAttack(player, inflictor, source); fixed_t gravityadjust; (void)source; + K_DirectorFollowAttack(player, inflictor, source); + player->tumbleBounces = 1; if (player->tripWireState == TRIP_PASSED) From bcabe6d43da35094f1afbcfe92d72bc1925f99a3 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 20 Jan 2022 04:50:11 -0800 Subject: [PATCH 163/379] Include k_director.h --- src/p_tick.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/p_tick.c b/src/p_tick.c index 6a26b43e2..334f4e25e 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -34,6 +34,7 @@ #include "k_race.h" #include "k_battle.h" #include "k_waypoint.h" +#include "k_director.h" tic_t leveltime; From 88c8217f81c5b28b6e0fdd9310e36ce3fdf2b09c Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 20 Jan 2022 06:56:07 -0600 Subject: [PATCH 164/379] tabs, thank you vscode for not reading the room --- src/k_director.c | 238 +++++++++++++++++++++++------------------------ 1 file changed, 119 insertions(+), 119 deletions(-) diff --git a/src/k_director.c b/src/k_director.c index 0de6b8957..0dc33c140 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -37,164 +37,164 @@ INT32 gap[MAXPLAYERS] = {0}; // gap between a given position and their closest p INT32 boredom[MAXPLAYERS] = {0}; // how long has a given position had no credible attackers? fixed_t K_GetFinishGap(INT32 leader, INT32 follower) { - fixed_t dista = players[follower].distancetofinish; - fixed_t distb = players[leader].distancetofinish; - if (players[follower].position < players[leader].position) { - return distb-dista; - } else { - return dista-distb; - } + fixed_t dista = players[follower].distancetofinish; + fixed_t distb = players[leader].distancetofinish; + if (players[follower].position < players[leader].position) { + return distb-dista; + } else { + return dista-distb; + } } void K_UpdateDirectorPositions(void) { - INT32 playernum; + INT32 playernum; - memset(sortedplayers, -1, sizeof(sortedplayers)); + memset(sortedplayers, -1, sizeof(sortedplayers)); for (playernum = 0; playernum < MAXPLAYERS; ++playernum) { if (playeringame[playernum]) - sortedplayers[players[playernum].position-1] = playernum; - } + sortedplayers[players[playernum].position-1] = playernum; + } - memset(gap, INT32_MAX, sizeof(gap)); - for (playernum = 0; playernum < MAXPLAYERS-1; ++playernum) { - if (sortedplayers[playernum] == -1 || sortedplayers[playernum+1] == -1) - break; - gap[playernum] = P_ScaleFromMap(K_GetFinishGap(sortedplayers[playernum], sortedplayers[playernum+1]), FRACUNIT); - if (gap[playernum] >= BREAKAWAYDIST) - boredom[playernum] = min(BOREDOMTIME*2, boredom[playernum]+1); - else if (boredom[playernum] > 0) - boredom[playernum]--; - } + memset(gap, INT32_MAX, sizeof(gap)); + for (playernum = 0; playernum < MAXPLAYERS-1; ++playernum) { + if (sortedplayers[playernum] == -1 || sortedplayers[playernum+1] == -1) + break; + gap[playernum] = P_ScaleFromMap(K_GetFinishGap(sortedplayers[playernum], sortedplayers[playernum+1]), FRACUNIT); + if (gap[playernum] >= BREAKAWAYDIST) + boredom[playernum] = min(BOREDOMTIME*2, boredom[playernum]+1); + else if (boredom[playernum] > 0) + boredom[playernum]--; + } - maxdist = P_ScaleFromMap(players[sortedplayers[0]].distancetofinish, FRACUNIT); + maxdist = P_ScaleFromMap(players[sortedplayers[0]].distancetofinish, FRACUNIT); } boolean K_CanSwitchDirector(void) { - INT32 *displayplayerp = &displayplayers[0]; - if (players[*displayplayerp].trickpanel > 0) - return false; - if (cooldown < SWITCHTIME) - return false; - return true; + INT32 *displayplayerp = &displayplayers[0]; + if (players[*displayplayerp].trickpanel > 0) + return false; + if (cooldown < SWITCHTIME) + return false; + return true; } void K_DirectorSwitch(INT32 player, boolean force) { - if (P_IsDisplayPlayer(&players[player])) - return; - if (players[player].exiting) - return; - if (!force && !K_CanSwitchDirector()) - return; - G_ResetView(1, player, true); - cooldown = 0; + if (P_IsDisplayPlayer(&players[player])) + return; + if (players[player].exiting) + return; + if (!force && !K_CanSwitchDirector()) + return; + G_ResetView(1, player, true); + cooldown = 0; } void K_DirectorForceSwitch(INT32 player, INT32 time) { - if (players[player].exiting) - return; - attacker = player; - freeze = time; + if (players[player].exiting) + return; + attacker = player; + freeze = time; } void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source) { - if (!P_IsDisplayPlayer(player)) - return; - if (inflictor && inflictor->player) - K_DirectorForceSwitch(inflictor->player-players, TRANSFERTIME); - if (source && source->player) - K_DirectorForceSwitch(source->player-players, TRANSFERTIME); + if (!P_IsDisplayPlayer(player)) + return; + if (inflictor && inflictor->player) + K_DirectorForceSwitch(inflictor->player-players, TRANSFERTIME); + if (source && source->player) + K_DirectorForceSwitch(source->player-players, TRANSFERTIME); } void K_DrawDirectorDebugger(void) { - INT32 playernum; - INT32 leader; - INT32 follower; - INT32 ytxt; + INT32 playernum; + INT32 leader; + INT32 follower; + INT32 ytxt; - if (!cv_kartdebugdirector.value) - return; + if (!cv_kartdebugdirector.value) + return; - V_DrawThinString(10, 0, V_70TRANS, va("PLACE")); - V_DrawThinString(40, 0, V_70TRANS, va("CONF?")); - V_DrawThinString(80, 0, V_70TRANS, va("GAP")); - V_DrawThinString(120, 0, V_70TRANS, va("BORED")); - V_DrawThinString(150, 0, V_70TRANS, va("COOLDOWN: %d", cooldown)); - V_DrawThinString(230, 0, V_70TRANS, va("MAXDIST: %d", maxdist)); + V_DrawThinString(10, 0, V_70TRANS, va("PLACE")); + V_DrawThinString(40, 0, V_70TRANS, va("CONF?")); + V_DrawThinString(80, 0, V_70TRANS, va("GAP")); + V_DrawThinString(120, 0, V_70TRANS, va("BORED")); + V_DrawThinString(150, 0, V_70TRANS, va("COOLDOWN: %d", cooldown)); + V_DrawThinString(230, 0, V_70TRANS, va("MAXDIST: %d", maxdist)); - for (playernum = 0; playernum < MAXPLAYERS-1; ++playernum) { - ytxt = 10*playernum+10; - leader = sortedplayers[playernum]; - follower = sortedplayers[playernum+1]; - if (leader == -1 || follower == -1) - break; + for (playernum = 0; playernum < MAXPLAYERS-1; ++playernum) { + ytxt = 10*playernum+10; + leader = sortedplayers[playernum]; + follower = sortedplayers[playernum+1]; + if (leader == -1 || follower == -1) + break; - V_DrawThinString(10, ytxt, V_70TRANS, va("%d", playernum)); - V_DrawThinString(20, ytxt, V_70TRANS, va("%d", playernum+1)); - if (players[leader].positiondelay) - V_DrawThinString(40, ytxt, V_70TRANS, va("NG")); - V_DrawThinString(80, ytxt, V_70TRANS, va("%d", gap[playernum])); - if (boredom[playernum] >= BOREDOMTIME) - V_DrawThinString(120, ytxt, V_70TRANS, va("BORED")); - else - V_DrawThinString(120, ytxt, V_70TRANS, va("%d", boredom[playernum])); - V_DrawThinString(150, ytxt, V_70TRANS, va("%s", player_names[leader])); - V_DrawThinString(230, ytxt, V_70TRANS, va("%s", player_names[follower])); - } + V_DrawThinString(10, ytxt, V_70TRANS, va("%d", playernum)); + V_DrawThinString(20, ytxt, V_70TRANS, va("%d", playernum+1)); + if (players[leader].positiondelay) + V_DrawThinString(40, ytxt, V_70TRANS, va("NG")); + V_DrawThinString(80, ytxt, V_70TRANS, va("%d", gap[playernum])); + if (boredom[playernum] >= BOREDOMTIME) + V_DrawThinString(120, ytxt, V_70TRANS, va("BORED")); + else + V_DrawThinString(120, ytxt, V_70TRANS, va("%d", boredom[playernum])); + V_DrawThinString(150, ytxt, V_70TRANS, va("%s", player_names[leader])); + V_DrawThinString(230, ytxt, V_70TRANS, va("%s", player_names[follower])); + } } void K_UpdateDirector(void) { - INT32 *displayplayerp = &displayplayers[0]; - INT32 targetposition; + INT32 *displayplayerp = &displayplayers[0]; + INT32 targetposition; - if (!cv_director.value) - return; + if (!cv_director.value) + return; - K_UpdateDirectorPositions(); - cooldown++; + K_UpdateDirectorPositions(); + cooldown++; - // handle pending forced switches - if (freeze > 0) { - if (freeze == 1) - K_DirectorSwitch(attacker, true); - freeze--; - return; - } + // handle pending forced switches + if (freeze > 0) { + if (freeze == 1) + K_DirectorSwitch(attacker, true); + freeze--; + return; + } - // aaight, time to walk through the standings to find the first interesting pair - for(targetposition = 0; targetposition < MAXPLAYERS-1; targetposition++) { - INT32 target; + // aaight, time to walk through the standings to find the first interesting pair + for(targetposition = 0; targetposition < MAXPLAYERS-1; targetposition++) { + INT32 target; - // you are out of players, try again - if (sortedplayers[targetposition+1] == -1) - break; + // you are out of players, try again + if (sortedplayers[targetposition+1] == -1) + break; - // pair too far apart? try the next one - if (boredom[targetposition] >= BOREDOMTIME) - continue; + // pair too far apart? try the next one + if (boredom[targetposition] >= BOREDOMTIME) + continue; - // pair finished? try the next one - if (players[sortedplayers[targetposition+1]].exiting) - continue; + // pair finished? try the next one + if (players[sortedplayers[targetposition+1]].exiting) + continue; - // don't risk switching away from forward pairs at race end, might miss something! - if (maxdist > PINCHDIST) { - // if the "next" player is close enough, they should be able to see everyone fine! - // walk back through the standings to find a vantage that gets everyone in frame. - // (also creates a pretty cool effect w/ overtakes at speed) - while (targetposition < MAXPLAYERS && gap[targetposition+1] < WALKBACKDIST) { - targetposition++; - } - } + // don't risk switching away from forward pairs at race end, might miss something! + if (maxdist > PINCHDIST) { + // if the "next" player is close enough, they should be able to see everyone fine! + // walk back through the standings to find a vantage that gets everyone in frame. + // (also creates a pretty cool effect w/ overtakes at speed) + while (targetposition < MAXPLAYERS && gap[targetposition+1] < WALKBACKDIST) { + targetposition++; + } + } - target = sortedplayers[targetposition+1]; + target = sortedplayers[targetposition+1]; - // if we're certain the back half of the pair is actually in this position, try to switch - if (*displayplayerp != target && !players[target].positiondelay) - K_DirectorSwitch(target, false); - // even if we're not certain, if we're certain we're watching the WRONG player, try to switch - if (players[*displayplayerp].position != targetposition && !players[target].positiondelay) - K_DirectorSwitch(target, false); + // if we're certain the back half of the pair is actually in this position, try to switch + if (*displayplayerp != target && !players[target].positiondelay) + K_DirectorSwitch(target, false); + // even if we're not certain, if we're certain we're watching the WRONG player, try to switch + if (players[*displayplayerp].position != targetposition && !players[target].positiondelay) + K_DirectorSwitch(target, false); - break; - } + break; + } } \ No newline at end of file From 8c28b094a19c0c6926851f77dfecdbdac846e11d Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 20 Jan 2022 15:23:32 -0600 Subject: [PATCH 165/379] general post-review unfuck --- src/k_director.c | 254 +++++++++++++++++++++++++++++++++-------------- src/k_director.h | 13 +++ src/k_kart.c | 7 +- src/p_setup.c | 3 + 4 files changed, 198 insertions(+), 79 deletions(-) diff --git a/src/k_director.c b/src/k_director.c index 0dc33c140..d3b2ce7bd 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -12,13 +12,14 @@ #include "d_netcmd.h" #include "p_local.h" -#define SWITCHTIME TICRATE*5 // cooldown between unforced switches -#define BOREDOMTIME 3*TICRATE/2 // how long until players considered far apart? -#define TRANSFERTIME TICRATE // how long to delay reaction shots? -#define BREAKAWAYDIST 4000 // how *far* until players considered far apart? -#define WALKBACKDIST 600 // how close should a trailing player be before we switch? -#define PINCHDIST 30000 // how close should the leader be to be considered "end of race"? +#define SWITCHTIME TICRATE * 5 // cooldown between unforced switches +#define BOREDOMTIME 3 * TICRATE / 2 // how long until players considered far apart? +#define TRANSFERTIME TICRATE // how long to delay reaction shots? +#define BREAKAWAYDIST 4000 // how *far* until players considered far apart? +#define WALKBACKDIST 600 // how close should a trailing player be before we switch? +#define PINCHDIST 30000 // how close should the leader be to be considered "end of race"? +void K_InitDirector(void); void K_UpdateDirectorPositions(void); boolean K_CanSwitchDirector(void); fixed_t K_GetFinishGap(INT32 leader, INT32 follower); @@ -27,173 +28,274 @@ void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source) void K_UpdateDirector(void); void K_DirectorForceSwitch(INT32 player, INT32 time); -INT32 cooldown = 0; // how long has it been since we last switched? -INT32 freeze = 0; // when nonzero, fixed switch pending, freeze logic! -INT32 attacker = 0; // who to switch to when freeze delay elapses -INT32 maxdist = 0; // how far is the closest player from finishing? +struct directorinfo directorinfo; -INT32 sortedplayers[MAXPLAYERS] = {0}; // position-1 goes in, player index comes out. -INT32 gap[MAXPLAYERS] = {0}; // gap between a given position and their closest pursuer -INT32 boredom[MAXPLAYERS] = {0}; // how long has a given position had no credible attackers? - -fixed_t K_GetFinishGap(INT32 leader, INT32 follower) { - fixed_t dista = players[follower].distancetofinish; - fixed_t distb = players[leader].distancetofinish; - if (players[follower].position < players[leader].position) { - return distb-dista; - } else { - return dista-distb; - } -} - -void K_UpdateDirectorPositions(void) { +void K_InitDirector(void) +{ INT32 playernum; - memset(sortedplayers, -1, sizeof(sortedplayers)); - for (playernum = 0; playernum < MAXPLAYERS; ++playernum) { - if (playeringame[playernum]) - sortedplayers[players[playernum].position-1] = playernum; - } + directorinfo.cooldown = SWITCHTIME; + directorinfo.freeze = 0; + directorinfo.attacker = 0; + directorinfo.maxdist = 0; - memset(gap, INT32_MAX, sizeof(gap)); - for (playernum = 0; playernum < MAXPLAYERS-1; ++playernum) { - if (sortedplayers[playernum] == -1 || sortedplayers[playernum+1] == -1) - break; - gap[playernum] = P_ScaleFromMap(K_GetFinishGap(sortedplayers[playernum], sortedplayers[playernum+1]), FRACUNIT); - if (gap[playernum] >= BREAKAWAYDIST) - boredom[playernum] = min(BOREDOMTIME*2, boredom[playernum]+1); - else if (boredom[playernum] > 0) - boredom[playernum]--; + for (playernum = 0; playernum < MAXPLAYERS; playernum++) + { + directorinfo.sortedplayers[playernum] = -1; + directorinfo.gap[playernum] = INT32_MAX; + directorinfo.boredom[playernum] = 0; } - - maxdist = P_ScaleFromMap(players[sortedplayers[0]].distancetofinish, FRACUNIT); } -boolean K_CanSwitchDirector(void) { +fixed_t K_GetFinishGap(INT32 leader, INT32 follower) +{ + fixed_t dista = players[follower].distancetofinish; + fixed_t distb = players[leader].distancetofinish; + + if (players[follower].position < players[leader].position) + { + return distb - dista; + } + else + { + return dista - distb; + } +} + +void K_UpdateDirectorPositions(void) +{ + INT32 playernum; + INT32 position; + player_t target; + + memset(directorinfo.sortedplayers, -1, sizeof(directorinfo.sortedplayers)); + + for (playernum = 0; playernum < MAXPLAYERS; playernum++) + { + target = players[playernum]; + + if (playeringame[playernum] && !target.spectator && target.position > 0) + { + directorinfo.sortedplayers[target.position - 1] = playernum; + } + } + + for (position = 0; position < MAXPLAYERS - 1; position++) + { + directorinfo.gap[position] = INT32_MAX; + + if (directorinfo.sortedplayers[position] == -1 || directorinfo.sortedplayers[position + 1] == -1) + { + continue; + } + + directorinfo.gap[position] = P_ScaleFromMap(K_GetFinishGap(directorinfo.sortedplayers[position], directorinfo.sortedplayers[position + 1]), FRACUNIT); + + if (directorinfo.gap[position] >= BREAKAWAYDIST) + { + directorinfo.boredom[position] = min(BOREDOMTIME * 2, directorinfo.boredom[position] + 1); + } + else if (directorinfo.boredom[position] > 0) + { + directorinfo.boredom[position]--; + } + } + + directorinfo.maxdist = P_ScaleFromMap(players[directorinfo.sortedplayers[0]].distancetofinish, FRACUNIT); +} + +boolean K_CanSwitchDirector(void) +{ INT32 *displayplayerp = &displayplayers[0]; + if (players[*displayplayerp].trickpanel > 0) + { return false; - if (cooldown < SWITCHTIME) + } + + if (directorinfo.cooldown > 0) + { return false; + } + return true; } -void K_DirectorSwitch(INT32 player, boolean force) { +void K_DirectorSwitch(INT32 player, boolean force) +{ if (P_IsDisplayPlayer(&players[player])) + { return; + } + if (players[player].exiting) + { return; + } + if (!force && !K_CanSwitchDirector()) + { return; + } + G_ResetView(1, player, true); - cooldown = 0; + directorinfo.cooldown = SWITCHTIME; } -void K_DirectorForceSwitch(INT32 player, INT32 time) { +void K_DirectorForceSwitch(INT32 player, INT32 time) +{ if (players[player].exiting) + { return; - attacker = player; - freeze = time; + } + + directorinfo.attacker = player; + directorinfo.freeze = time; } -void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source) { +void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source) +{ if (!P_IsDisplayPlayer(player)) + { return; + } + if (inflictor && inflictor->player) - K_DirectorForceSwitch(inflictor->player-players, TRANSFERTIME); - if (source && source->player) - K_DirectorForceSwitch(source->player-players, TRANSFERTIME); + { + K_DirectorForceSwitch(inflictor->player - players, TRANSFERTIME); + } + else if (source && source->player) + { + K_DirectorForceSwitch(source->player - players, TRANSFERTIME); + } } -void K_DrawDirectorDebugger(void) { +void K_DrawDirectorDebugger(void) +{ INT32 playernum; INT32 leader; INT32 follower; INT32 ytxt; if (!cv_kartdebugdirector.value) + { return; + } V_DrawThinString(10, 0, V_70TRANS, va("PLACE")); V_DrawThinString(40, 0, V_70TRANS, va("CONF?")); V_DrawThinString(80, 0, V_70TRANS, va("GAP")); V_DrawThinString(120, 0, V_70TRANS, va("BORED")); - V_DrawThinString(150, 0, V_70TRANS, va("COOLDOWN: %d", cooldown)); - V_DrawThinString(230, 0, V_70TRANS, va("MAXDIST: %d", maxdist)); + V_DrawThinString(150, 0, V_70TRANS, va("COOLDOWN: %d", directorinfo.cooldown)); + V_DrawThinString(230, 0, V_70TRANS, va("MAXDIST: %d", directorinfo.maxdist)); + + for (playernum = 0; playernum < MAXPLAYERS - 1; playernum++) + { + ytxt = 10 * (playernum + 1); + leader = directorinfo.sortedplayers[playernum]; + follower = directorinfo.sortedplayers[playernum + 1]; - for (playernum = 0; playernum < MAXPLAYERS-1; ++playernum) { - ytxt = 10*playernum+10; - leader = sortedplayers[playernum]; - follower = sortedplayers[playernum+1]; if (leader == -1 || follower == -1) break; V_DrawThinString(10, ytxt, V_70TRANS, va("%d", playernum)); - V_DrawThinString(20, ytxt, V_70TRANS, va("%d", playernum+1)); + V_DrawThinString(20, ytxt, V_70TRANS, va("%d", playernum + 1)); + if (players[leader].positiondelay) + { V_DrawThinString(40, ytxt, V_70TRANS, va("NG")); - V_DrawThinString(80, ytxt, V_70TRANS, va("%d", gap[playernum])); - if (boredom[playernum] >= BOREDOMTIME) + } + + V_DrawThinString(80, ytxt, V_70TRANS, va("%d", directorinfo.gap[playernum])); + + if (directorinfo.boredom[playernum] >= BOREDOMTIME) + { V_DrawThinString(120, ytxt, V_70TRANS, va("BORED")); + } else - V_DrawThinString(120, ytxt, V_70TRANS, va("%d", boredom[playernum])); + { + V_DrawThinString(120, ytxt, V_70TRANS, va("%d", directorinfo.boredom[playernum])); + } + V_DrawThinString(150, ytxt, V_70TRANS, va("%s", player_names[leader])); V_DrawThinString(230, ytxt, V_70TRANS, va("%s", player_names[follower])); } } -void K_UpdateDirector(void) { +void K_UpdateDirector(void) +{ INT32 *displayplayerp = &displayplayers[0]; INT32 targetposition; if (!cv_director.value) + { return; + } K_UpdateDirectorPositions(); - cooldown++; + + if (directorinfo.cooldown > 0) { + directorinfo.cooldown--; + } // handle pending forced switches - if (freeze > 0) { - if (freeze == 1) - K_DirectorSwitch(attacker, true); - freeze--; + if (directorinfo.freeze > 0) + { + if (!(--directorinfo.freeze)) + K_DirectorSwitch(directorinfo.attacker, true); + + directorinfo.freeze--; return; } // aaight, time to walk through the standings to find the first interesting pair - for(targetposition = 0; targetposition < MAXPLAYERS-1; targetposition++) { + for (targetposition = 1; targetposition < MAXPLAYERS; targetposition++) + { INT32 target; // you are out of players, try again - if (sortedplayers[targetposition+1] == -1) + if (directorinfo.sortedplayers[targetposition] == -1) + { break; + } // pair too far apart? try the next one - if (boredom[targetposition] >= BOREDOMTIME) + if (directorinfo.boredom[targetposition - 1] >= BOREDOMTIME) + { continue; + } // pair finished? try the next one - if (players[sortedplayers[targetposition+1]].exiting) + if (players[directorinfo.sortedplayers[targetposition]].exiting) + { continue; + } // don't risk switching away from forward pairs at race end, might miss something! - if (maxdist > PINCHDIST) { + if (directorinfo.maxdist > PINCHDIST) + { // if the "next" player is close enough, they should be able to see everyone fine! // walk back through the standings to find a vantage that gets everyone in frame. // (also creates a pretty cool effect w/ overtakes at speed) - while (targetposition < MAXPLAYERS && gap[targetposition+1] < WALKBACKDIST) { + while (targetposition < MAXPLAYERS && directorinfo.gap[targetposition] < WALKBACKDIST) + { targetposition++; } } - target = sortedplayers[targetposition+1]; + target = directorinfo.sortedplayers[targetposition]; // if we're certain the back half of the pair is actually in this position, try to switch if (*displayplayerp != target && !players[target].positiondelay) + { K_DirectorSwitch(target, false); + } + // even if we're not certain, if we're certain we're watching the WRONG player, try to switch if (players[*displayplayerp].position != targetposition && !players[target].positiondelay) + { K_DirectorSwitch(target, false); + } break; } diff --git a/src/k_director.h b/src/k_director.h index a7d9fd2a6..db19fbe8b 100644 --- a/src/k_director.h +++ b/src/k_director.h @@ -3,6 +3,19 @@ /// \file k_director.h /// \brief SRB2kart automatic spectator camera. +extern struct directorinfo +{ + tic_t cooldown; // how long has it been since we last switched? + tic_t freeze; // when nonzero, fixed switch pending, freeze logic! + INT32 attacker; // who to switch to when freeze delay elapses + INT32 maxdist; // how far is the closest player from finishing? + + INT32 sortedplayers[MAXPLAYERS]; // position-1 goes in, player index comes out. + INT32 gap[MAXPLAYERS]; // gap between a given position and their closest pursuer + INT32 boredom[MAXPLAYERS]; // how long has a given position had no credible attackers? +} directorinfo; + +void K_InitDirector(void); void K_UpdateDirector(void); void K_DrawDirectorDebugger(void); void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source); \ No newline at end of file diff --git a/src/k_kart.c b/src/k_kart.c index 4332d4416..f7fc380a3 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3233,10 +3233,11 @@ void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UIN void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type) { - K_DirectorFollowAttack(player, inflictor, source); (void)inflictor; (void)source; + K_DirectorFollowAttack(player, inflictor, source); + player->spinouttype = type; if (( player->spinouttype & KSPIN_THRUST )) @@ -3415,10 +3416,10 @@ INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) // A { INT32 ringburst = 10; - K_DirectorFollowAttack(player, inflictor, source); - (void)source; + K_DirectorFollowAttack(player, inflictor, source); + player->mo->momz = 18*mapobjectscale*P_MobjFlip(player->mo); // please stop forgetting mobjflip checks!!!! player->mo->momx = player->mo->momy = 0; diff --git a/src/p_setup.c b/src/p_setup.c index ef5d80070..704bb99ec 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -93,6 +93,7 @@ #include "k_grandprix.h" #include "k_brightmap.h" #include "k_terrain.h" // TRF_TRIPWIRE +#include "k_director.h" // K_InitDirector // Replay names have time #if !defined (UNDER_CE) @@ -4142,6 +4143,8 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) memset(localaiming, 0, sizeof(localaiming)); } + K_InitDirector(); + wantedcalcdelay = wantedfrequency*2; indirectitemcooldown = 0; hyubgone = 0; From 95598dad55656e8c36ecc7dd6e40e617c40c5e00 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 20 Jan 2022 15:26:32 -0600 Subject: [PATCH 166/379] appease linker wrt internal functions --- src/k_director.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/k_director.c b/src/k_director.c index d3b2ce7bd..291cb3953 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -47,7 +47,7 @@ void K_InitDirector(void) } } -fixed_t K_GetFinishGap(INT32 leader, INT32 follower) +static fixed_t K_GetFinishGap(INT32 leader, INT32 follower) { fixed_t dista = players[follower].distancetofinish; fixed_t distb = players[leader].distancetofinish; @@ -104,7 +104,7 @@ void K_UpdateDirectorPositions(void) directorinfo.maxdist = P_ScaleFromMap(players[directorinfo.sortedplayers[0]].distancetofinish, FRACUNIT); } -boolean K_CanSwitchDirector(void) +static boolean K_CanSwitchDirector(void) { INT32 *displayplayerp = &displayplayers[0]; @@ -121,7 +121,7 @@ boolean K_CanSwitchDirector(void) return true; } -void K_DirectorSwitch(INT32 player, boolean force) +static void K_DirectorSwitch(INT32 player, boolean force) { if (P_IsDisplayPlayer(&players[player])) { @@ -142,7 +142,7 @@ void K_DirectorSwitch(INT32 player, boolean force) directorinfo.cooldown = SWITCHTIME; } -void K_DirectorForceSwitch(INT32 player, INT32 time) +static void K_DirectorForceSwitch(INT32 player, INT32 time) { if (players[player].exiting) { From f45385e844aa1d93d14bba0c428fddc98696524e Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 20 Jan 2022 15:29:13 -0600 Subject: [PATCH 167/379] how is static formed --- src/k_director.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/k_director.c b/src/k_director.c index 291cb3953..169cd8af4 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -19,15 +19,6 @@ #define WALKBACKDIST 600 // how close should a trailing player be before we switch? #define PINCHDIST 30000 // how close should the leader be to be considered "end of race"? -void K_InitDirector(void); -void K_UpdateDirectorPositions(void); -boolean K_CanSwitchDirector(void); -fixed_t K_GetFinishGap(INT32 leader, INT32 follower); -void K_DirectorSwitch(INT32 player, boolean force); -void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source); -void K_UpdateDirector(void); -void K_DirectorForceSwitch(INT32 player, INT32 time); - struct directorinfo directorinfo; void K_InitDirector(void) @@ -62,7 +53,7 @@ static fixed_t K_GetFinishGap(INT32 leader, INT32 follower) } } -void K_UpdateDirectorPositions(void) +static void K_UpdateDirectorPositions(void) { INT32 playernum; INT32 position; From 973d35aced0c85541ed84334f21eef7c2acc374a Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 20 Jan 2022 15:35:26 -0600 Subject: [PATCH 168/379] fix double speed attack transfers --- src/k_director.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/k_director.c b/src/k_director.c index 169cd8af4..34ee9d6b3 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -235,7 +235,6 @@ void K_UpdateDirector(void) if (!(--directorinfo.freeze)) K_DirectorSwitch(directorinfo.attacker, true); - directorinfo.freeze--; return; } From 410a37b47b680060fc9aec7e1b73fa200379a15e Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 20 Jan 2022 15:56:36 -0600 Subject: [PATCH 169/379] fix low-confidence switch behavior --- src/k_director.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/k_director.c b/src/k_director.c index 34ee9d6b3..4603beb31 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -163,7 +163,7 @@ void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source) void K_DrawDirectorDebugger(void) { - INT32 playernum; + INT32 position; INT32 leader; INT32 follower; INT32 ytxt; @@ -180,32 +180,32 @@ void K_DrawDirectorDebugger(void) V_DrawThinString(150, 0, V_70TRANS, va("COOLDOWN: %d", directorinfo.cooldown)); V_DrawThinString(230, 0, V_70TRANS, va("MAXDIST: %d", directorinfo.maxdist)); - for (playernum = 0; playernum < MAXPLAYERS - 1; playernum++) + for (position = 0; position < MAXPLAYERS - 1; position++) { - ytxt = 10 * (playernum + 1); - leader = directorinfo.sortedplayers[playernum]; - follower = directorinfo.sortedplayers[playernum + 1]; + ytxt = 10 * (position + 1); + leader = directorinfo.sortedplayers[position]; + follower = directorinfo.sortedplayers[position + 1]; if (leader == -1 || follower == -1) break; - V_DrawThinString(10, ytxt, V_70TRANS, va("%d", playernum)); - V_DrawThinString(20, ytxt, V_70TRANS, va("%d", playernum + 1)); + V_DrawThinString(10, ytxt, V_70TRANS, va("%d", position)); + V_DrawThinString(20, ytxt, V_70TRANS, va("%d", position + 1)); if (players[leader].positiondelay) { V_DrawThinString(40, ytxt, V_70TRANS, va("NG")); } - V_DrawThinString(80, ytxt, V_70TRANS, va("%d", directorinfo.gap[playernum])); + V_DrawThinString(80, ytxt, V_70TRANS, va("%d", directorinfo.gap[position])); - if (directorinfo.boredom[playernum] >= BOREDOMTIME) + if (directorinfo.boredom[position] >= BOREDOMTIME) { V_DrawThinString(120, ytxt, V_70TRANS, va("BORED")); } else { - V_DrawThinString(120, ytxt, V_70TRANS, va("%d", directorinfo.boredom[playernum])); + V_DrawThinString(120, ytxt, V_70TRANS, va("%d", directorinfo.boredom[position])); } V_DrawThinString(150, ytxt, V_70TRANS, va("%s", player_names[leader])); @@ -282,7 +282,7 @@ void K_UpdateDirector(void) } // even if we're not certain, if we're certain we're watching the WRONG player, try to switch - if (players[*displayplayerp].position != targetposition && !players[target].positiondelay) + if (players[*displayplayerp].position != targetposition+1 && !players[target].positiondelay) { K_DirectorSwitch(target, false); } From 3e6d1d4c60d513f1993d03da04b10bccdc939282 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 20 Jan 2022 16:00:51 -0600 Subject: [PATCH 170/379] clarify potential footgunnery in K_UpdateDirector --- src/k_director.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/k_director.c b/src/k_director.c index 4603beb31..947e73cc3 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -239,6 +239,8 @@ void K_UpdateDirector(void) } // aaight, time to walk through the standings to find the first interesting pair + // NB: targetposition/sortedplayers is 0-indexed, aiming at the "back half" of a given pair by default. + // we adjust for this when comparing to player->position or when looking at the leading player, Don't Freak Out for (targetposition = 1; targetposition < MAXPLAYERS; targetposition++) { INT32 target; From d71fdc9d0accfd401bc6201c750f52a07ba23991 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Fri, 21 Jan 2022 02:42:16 -0600 Subject: [PATCH 171/379] avoid wasteful player_t copy --- src/k_director.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/k_director.c b/src/k_director.c index 947e73cc3..a35c3da84 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -57,17 +57,17 @@ static void K_UpdateDirectorPositions(void) { INT32 playernum; INT32 position; - player_t target; + player_t* target; memset(directorinfo.sortedplayers, -1, sizeof(directorinfo.sortedplayers)); for (playernum = 0; playernum < MAXPLAYERS; playernum++) { - target = players[playernum]; + target = &players[playernum]; - if (playeringame[playernum] && !target.spectator && target.position > 0) + if (playeringame[playernum] && !target->spectator && target->position > 0) { - directorinfo.sortedplayers[target.position - 1] = playernum; + directorinfo.sortedplayers[target->position - 1] = playernum; } } From cc0786d7805dcb6d14dd34aae25cc0f3a334c16b Mon Sep 17 00:00:00 2001 From: SteelT Date: Thu, 20 Jan 2022 03:20:53 -0500 Subject: [PATCH 172/379] Linedef type 460: Don't award rings while SPB-locked --- src/p_spec.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/p_spec.c b/src/p_spec.c index 61862df96..9f75cf135 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -3620,6 +3620,10 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) INT32 delay = (sides[line->sidenum[0]].rowoffset>>FRACBITS); if (mo && mo->player) { + // Don't award rings while SPB is targetting you + if (mo->player->pflags & PF_RINGLOCK) + return; + if (delay <= 0 || !(leveltime % delay)) P_GivePlayerRings(mo->player, rings); } From 9b2d81273991892946e6f83196d202a82e1d946f Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 20 Jan 2022 19:27:02 +0000 Subject: [PATCH 173/379] The "free" changes from framezero - the crash fixes that have no complexity and therefore no negative consequence to introduce --- src/k_kart.c | 17 ++++++++++++----- src/p_map.c | 3 +++ src/p_mobj.c | 2 ++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index f7fc380a3..892aaef8f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3819,7 +3819,7 @@ static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, I y = source->y + source->momy + FixedMul(finalspeed, FINESINE(an>>ANGLETOFINESHIFT)); z = source->z; // spawn on the ground please - th = P_SpawnMobj(x, y, z, type); + th = P_SpawnMobj(x, y, z, type); // this will never return null because collision isn't processed here K_FlipFromObject(th, source); @@ -3838,7 +3838,10 @@ static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, I { // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn // This should set it for FOFs - P_SetOrigin(th, th->x, th->y, th->z); + P_SetOrigin(th, th->x, th->y, th->z); // however, THIS can fuck up your day. just absolutely ruin you. + if (P_MobjWasRemoved(th)) + return NULL; + // spawn on the ground if the player is on the ground if (P_MobjFlip(source) < 0) { @@ -3906,7 +3909,7 @@ static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, I P_SetTarget(&throwmo->target, source); } - return NULL; + return th; } UINT16 K_DriftSparkColor(player_t *player, INT32 charge) @@ -4713,10 +4716,11 @@ mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, { if (mapthing == MT_BALLHOG) // Messy { + mo = NULL; // can't return multiple projectiles if (dir == -1) { // Shoot backward - mo = K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) - 0x06000000, 0, PROJSPEED/8); + K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) - 0x06000000, 0, PROJSPEED/8); K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) - 0x03000000, 0, PROJSPEED/8); K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/8); K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) + 0x03000000, 0, PROJSPEED/8); @@ -4725,7 +4729,7 @@ mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, else { // Shoot forward - mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle - 0x06000000, 0, PROJSPEED); + K_SpawnKartMissile(player->mo, mapthing, player->mo->angle - 0x06000000, 0, PROJSPEED); K_SpawnKartMissile(player->mo, mapthing, player->mo->angle - 0x03000000, 0, PROJSPEED); K_SpawnKartMissile(player->mo, mapthing, player->mo->angle, 0, PROJSPEED); K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + 0x03000000, 0, PROJSPEED); @@ -5337,6 +5341,9 @@ void K_KillBananaChain(mobj_t *banana, mobj_t *inflictor, mobj_t *source) mobj_t *cachenext; killnext: + if (P_MobjWasRemoved(banana)) + return; + cachenext = banana->hnext; if (banana->health) diff --git a/src/p_map.c b/src/p_map.c index fb79b6677..a0f4e3cfe 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -3496,6 +3496,9 @@ void P_SlideMove(mobj_t *mo) vertex_t v1, v2; // fake vertexes line_t junk; // fake linedef + if (P_MobjWasRemoved(mo)) + return; + if (tmhitthing && mo->z + mo->height > tmhitthing->z && mo->z < tmhitthing->z + tmhitthing->height) { // Don't mess with your momentum if it's a pushable object. Pushables do their own crazy things already. diff --git a/src/p_mobj.c b/src/p_mobj.c index 4a147e065..d7f58eb60 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1689,6 +1689,8 @@ void P_XYMovement(mobj_t *mo) if (mo->flags & MF_SLIDEME) { P_SlideMove(mo); + if (P_MobjWasRemoved(mo)) + return; xmove = ymove = 0; } else From 47dc3b9d3d5b9d15528f8e1751e610ac7b16f3f1 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 22 Jan 2022 03:18:06 -0800 Subject: [PATCH 174/379] Hash name lookup for textures and lumps --- src/doomdef.h | 16 ++++++++++++++++ src/r_textures.c | 12 ++++++++++-- src/r_textures.h | 1 + src/w_wad.c | 19 ++++++++++++++++--- src/w_wad.h | 1 + 5 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index c5ef7839f..34d256930 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -551,6 +551,22 @@ extern boolean capslock; // i_system.c, replace getchar() once the keyboard has been appropriated INT32 I_GetKey(void); +/* http://www.cse.yorku.ca/~oz/hash.html */ +static inline +UINT32 quickncasehash (const char *p, size_t n) +{ + size_t i = 0; + UINT32 x = 5381; + + while (i < n && p[i]) + { + x = (x * 33) ^ tolower(p[i]); + i++; + } + + return x; +} + #ifndef min // Double-Check with WATTCP-32's cdefs.h #define min(x, y) (((x) < (y)) ? (x) : (y)) #endif diff --git a/src/r_textures.c b/src/r_textures.c index 9254cb91b..3b6e561b5 100644 --- a/src/r_textures.c +++ b/src/r_textures.c @@ -60,6 +60,7 @@ INT32 *texturebrightmaps; // Painfully simple texture id cacheing to make maps load faster. :3 static struct { char name[9]; + UINT32 hash; INT32 id; } *tidcache = NULL; static INT32 tidcachelen = 0; @@ -803,6 +804,7 @@ Rloadflats (INT32 i, INT32 w) // Set texture properties. M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); + texture->hash = quickncasehash(texture->name, 8); #ifndef NO_PNG_LUMPS if (Picture_IsLumpPNG(header, lumplength)) @@ -901,6 +903,7 @@ Rloadtextures (INT32 i, INT32 w) // Set texture properties. M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); + texture->hash = quickncasehash(texture->name, 8); #ifndef NO_PNG_LUMPS if (Picture_IsLumpPNG((UINT8 *)&patchlump, lumplength)) @@ -1389,6 +1392,7 @@ static texture_t *R_ParseTexture(boolean actuallyLoadTexture) // Allocate memory for a zero-patch texture. Obviously, we'll be adding patches momentarily. resultTexture = (texture_t *)Z_Calloc(sizeof(texture_t),PU_STATIC,NULL); M_Memcpy(resultTexture->name, newTextureName, 8); + resultTexture->hash = quickncasehash(newTextureName, 8); resultTexture->width = newTextureWidth; resultTexture->height = newTextureHeight; resultTexture->type = TEXTURETYPE_COMPOSITE; @@ -1614,25 +1618,29 @@ void R_ClearTextureNumCache(boolean btell) INT32 R_CheckTextureNumForName(const char *name) { INT32 i; + UINT32 hash; // "NoTexture" marker. if (name[0] == '-') return 0; + hash = quickncasehash(name, 8); + for (i = 0; i < tidcachelen; i++) - if (!strncasecmp(tidcache[i].name, name, 8)) + if (tidcache[i].hash == hash && !strncasecmp(tidcache[i].name, name, 8)) return tidcache[i].id; // Need to parse the list backwards, so textures loaded more recently are used in lieu of ones loaded earlier //for (i = 0; i < numtextures; i++) <- old for (i = (numtextures - 1); i >= 0; i--) // <- new - if (!strncasecmp(textures[i]->name, name, 8)) + if (textures[i]->hash == hash && !strncasecmp(textures[i]->name, name, 8)) { tidcachelen++; Z_Realloc(tidcache, tidcachelen * sizeof(*tidcache), PU_STATIC, &tidcache); strncpy(tidcache[tidcachelen-1].name, name, 8); tidcache[tidcachelen-1].name[8] = '\0'; CONS_Debug(DBG_SETUP, "texture #%s: %s\n", sizeu1(tidcachelen), tidcache[tidcachelen-1].name); + tidcache[tidcachelen-1].hash = hash; tidcache[tidcachelen-1].id = i; return i; } diff --git a/src/r_textures.h b/src/r_textures.h index dada043a6..bd5f6388e 100644 --- a/src/r_textures.h +++ b/src/r_textures.h @@ -54,6 +54,7 @@ typedef struct { // Keep name for switch changing, etc. char name[8]; + UINT32 hash; UINT8 type; // TEXTURETYPE_ INT16 width, height; boolean holes; diff --git a/src/w_wad.c b/src/w_wad.c index c2c4ed231..af1cd0244 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -356,6 +356,7 @@ static lumpinfo_t* ResGetLumpsStandalone (FILE* handle, UINT16* numlumps, const lumpinfo->size = ftell(handle); fseek(handle, 0, SEEK_SET); strcpy(lumpinfo->name, lumpname); + lumpinfo->hash = quickncasehash(lumpname, 8); // Allocate the lump's long name. lumpinfo->longname = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL); @@ -453,6 +454,7 @@ static lumpinfo_t* ResGetLumpsWad (FILE* handle, UINT16* nlmp, const char* filen lump_p->compression = CM_NOCOMPRESSION; memset(lump_p->name, 0x00, 9); strncpy(lump_p->name, fileinfo->name, 8); + lump_p->hash = quickncasehash(lump_p->name, 8); // Allocate the lump's long name. lump_p->longname = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL); @@ -627,6 +629,7 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) memset(lump_p->name, '\0', 9); // Making sure they're initialized to 0. Is it necessary? strncpy(lump_p->name, trimname, min(8, dotpos - trimname)); + lump_p->hash = quickncasehash(lump_p->name, 8); lump_p->longname = Z_Calloc(dotpos - trimname + 1, PU_STATIC, NULL); strlcpy(lump_p->longname, trimname, dotpos - trimname + 1); @@ -978,12 +981,14 @@ UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump) { UINT16 i; static char uname[8 + 1]; + UINT32 hash; if (!TestValidLump(wad,0)) return INT16_MAX; strlcpy(uname, name, sizeof uname); strupr(uname); + hash = quickncasehash(uname, 8); // // scan forward @@ -994,7 +999,7 @@ UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump) { lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++) - if (!strncmp(lump_p->name, uname, sizeof(uname) - 1)) + if (lump_p->hash == hash && !strncmp(lump_p->name, uname, sizeof(uname) - 1)) return i; } @@ -1197,15 +1202,20 @@ lumpnum_t W_CheckNumForLongName(const char *name) // TODO: Make it search through cache first, maybe...? lumpnum_t W_CheckNumForMap(const char *name) { + UINT32 hash = quickncasehash(name, 8); UINT16 lumpNum, end; UINT32 i; + lumpinfo_t *p; for (i = numwadfiles - 1; i < numwadfiles; i--) { if (wadfiles[i]->type == RET_WAD) { for (lumpNum = 0; lumpNum < wadfiles[i]->numlumps; lumpNum++) - if (!strncmp(name, (wadfiles[i]->lumpinfo + lumpNum)->name, 8)) + { + p = wadfiles[i]->lumpinfo + lumpNum; + if (p->hash == hash && !strncmp(name, p->name, 8)) return (i<<16) + lumpNum; + } } else if (wadfiles[i]->type == RET_PK3) { @@ -1216,8 +1226,11 @@ lumpnum_t W_CheckNumForMap(const char *name) continue; // Now look for the specified map. for (; lumpNum < end; lumpNum++) - if (!strnicmp(name, (wadfiles[i]->lumpinfo + lumpNum)->name, 8)) + { + p = wadfiles[i]->lumpinfo + lumpNum; + if (p->hash == hash && !strnicmp(name, p->name, 8)) return (i<<16) + lumpNum; + } } } return LUMPERROR; diff --git a/src/w_wad.h b/src/w_wad.h index b6bf94be8..55e3e0072 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -67,6 +67,7 @@ typedef struct unsigned long position; // filelump_t filepos unsigned long disksize; // filelump_t size char name[9]; // filelump_t name[] e.g. "LongEntr" + UINT32 hash; char *longname; // e.g. "LongEntryName" char *fullname; // e.g. "Folder/Subfolder/LongEntryName.extension" size_t size; // real (uncompressed) size From 38c77205b2d4bf025c6dd7e5ee0331434eb77521 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 9 Jan 2022 00:00:01 -0500 Subject: [PATCH 175/379] More extremely scaled grow & shrink players! - Grow is x2 instead of x1.5. - Shrink is x0.5 instead of x0.75. - Physics were left ALONE!! This is purely a HITBOX / VISUAL change!! Grow isn't any faster, and Shrink isn't any slower!! The only potential worry is low ceilings in maps!! --- src/k_kart.c | 81 ++++++++++++++++++++++++++++++++++++++++--------- src/k_kart.h | 7 +++++ src/k_respawn.c | 4 +-- src/p_mobj.c | 7 +++-- 4 files changed, 81 insertions(+), 18 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 892aaef8f..3d2dfdca6 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2905,6 +2905,30 @@ static void K_GetKartBoostPower(player_t *player) player->numboosts = numboosts; } +fixed_t K_GrowShrinkSpeedMul(player_t *player) +{ + fixed_t scaleDiff = player->mo->scale - mapobjectscale; + fixed_t physicsScale = mapobjectscale; + fixed_t speedMul = FRACUNIT; + + if (scaleDiff > 0) + { + // Grown + // Change x2 speed into x1.5 + physicsScale = FixedMul(GROW_PHYSICS_SCALE, mapobjectscale); + speedMul = FixedDiv(physicsScale, player->mo->scale); + } + else if (scaleDiff < 0) + { + // Shrunk + // Change x0.5 speed into x0.75 + physicsScale = FixedMul(SHRINK_PHYSICS_SCALE, mapobjectscale); + speedMul = FixedDiv(physicsScale, player->mo->scale); + } + + return speedMul; +} + // Returns kart speed from a stat. Boost power and scale are NOT taken into account, no player or object is necessary. fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed) { @@ -2921,9 +2945,8 @@ fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed) fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) { - fixed_t finalspeed; - - finalspeed = K_GetKartSpeedFromStat(player->kartspeed); + const boolean mobjValid = (player->mo != NULL && P_MobjWasRemoved(player->mo) == false); + fixed_t finalspeed = K_GetKartSpeedFromStat(player->kartspeed); if (player->spheres > 0) { @@ -2944,17 +2967,24 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) } } - if (player->mo && !P_MobjWasRemoved(player->mo)) + if (mobjValid == true) + { finalspeed = FixedMul(finalspeed, player->mo->scale); + finalspeed = FixedMul(finalspeed, K_GrowShrinkSpeedMul(player)); + } + else + { + finalspeed = FixedMul(finalspeed, mapobjectscale); + } - if (doboostpower) + if (doboostpower == true) { if (K_PlayerUsesBotMovement(player)) { finalspeed = FixedMul(finalspeed, K_BotTopSpeedRubberband(player)); } - return FixedMul(finalspeed, player->boostpower+player->speedboost); + finalspeed = FixedMul(finalspeed, player->boostpower + player->speedboost); } return finalspeed; @@ -3266,8 +3296,11 @@ static void K_RemoveGrowShrink(player_t *player) player->mo->scalespeed = mapobjectscale/TICRATE; player->mo->destscale = mapobjectscale; + if (cv_kartdebugshrink.value && !modeattacking && !player->bot) - player->mo->destscale = (6*player->mo->destscale)/8; + { + player->mo->destscale = FixedMul(player->mo->destscale, SHRINK_SCALE); + } } player->growshrinktimer = 0; @@ -5209,9 +5242,13 @@ static void K_DoShrink(player_t *user) if (players[i].mo && !P_MobjWasRemoved(players[i].mo)) { players[i].mo->scalespeed = mapobjectscale/TICRATE; - players[i].mo->destscale = (6*mapobjectscale)/8; + players[i].mo->destscale = FixedMul(mapobjectscale, SHRINK_SCALE); + if (cv_kartdebugshrink.value && !modeattacking && !players[i].bot) - players[i].mo->destscale = (6*players[i].mo->destscale)/8; + { + players[i].mo->destscale = FixedMul(players[i].mo->destscale, SHRINK_SCALE); + } + S_StartSound(players[i].mo, sfx_kc59); } } @@ -9051,23 +9088,39 @@ void K_MoveKartPlayer(player_t *player, boolean onground) case KITEM_GROW: if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) { - if (player->growshrinktimer < 0) // If you're shrunk, then "grow" will just make you normal again. + if (player->growshrinktimer < 0) + { + // If you're shrunk, then "grow" will just make you normal again. K_RemoveGrowShrink(player); + } else { K_PlayPowerGloatSound(player->mo); + player->mo->scalespeed = mapobjectscale/TICRATE; - player->mo->destscale = (3*mapobjectscale)/2; + player->mo->destscale = FixedMul(mapobjectscale, GROW_SCALE); + if (cv_kartdebugshrink.value && !modeattacking && !player->bot) - player->mo->destscale = (6*player->mo->destscale)/8; + { + player->mo->destscale = FixedMul(player->mo->destscale, SHRINK_SCALE); + } + player->growshrinktimer = itemtime+(4*TICRATE); // 12 seconds - if (P_IsLocalPlayer(player)) + + if (P_IsLocalPlayer(player) == true) + { S_ChangeMusicSpecial("kgrow"); - if (! P_IsDisplayPlayer(player)) + } + + if (P_IsDisplayPlayer(player) == false) + { S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kgrow)); + } + P_RestoreMusic(player); S_StartSound(player->mo, sfx_kc5a); } + player->itemamount--; } break; diff --git a/src/k_kart.h b/src/k_kart.h index 04d80fd05..c05620b74 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -22,6 +22,12 @@ Make sure this matches the actual number of states #define MAXHITLAGTICS 18 //12 #define HITLAGJITTERS (FRACUNIT / 20) +#define GROW_SCALE (2*FRACUNIT) +#define SHRINK_SCALE (FRACUNIT/2) + +#define GROW_PHYSICS_SCALE (3*FRACUNIT/2) +#define SHRINK_PHYSICS_SCALE (3*FRACUNIT/4) + player_t *K_GetItemBoxPlayer(mobj_t *mobj); angle_t K_ReflectAngle(angle_t angle, angle_t against, fixed_t maxspeed, fixed_t yourspeed); @@ -115,6 +121,7 @@ boolean K_WaterRun(player_t *player); void K_ApplyTripWire(player_t *player, tripwirestate_t state); INT16 K_GetSpindashChargeTime(player_t *player); fixed_t K_GetSpindashChargeSpeed(player_t *player); +fixed_t K_GrowShrinkSpeedMul(player_t *player); fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed); fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower); fixed_t K_GetKartAccel(player_t *player); diff --git a/src/k_respawn.c b/src/k_respawn.c index b79a5ca69..2146b2d48 100644 --- a/src/k_respawn.c +++ b/src/k_respawn.c @@ -672,11 +672,11 @@ static void K_HandleDropDash(player_t *player) if (player->growshrinktimer < 0) { player->mo->scalespeed = mapobjectscale/TICRATE; - player->mo->destscale = (6*mapobjectscale)/8; + player->mo->destscale = FixedMul(mapobjectscale, SHRINK_SCALE); if (cv_kartdebugshrink.value && !modeattacking && !player->bot) { - player->mo->destscale = (6*player->mo->destscale)/8; + player->mo->destscale = FixedMul(player->mo->destscale, SHRINK_SCALE); } } diff --git a/src/p_mobj.c b/src/p_mobj.c index d7f58eb60..47aa07120 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10867,9 +10867,12 @@ void P_SpawnPlayer(INT32 playernum) P_SetTarget(&p->follower, NULL); // cleanse follower from existence - // set the scale to the mobj's destscale so settings get correctly set. if we don't, they sometimes don't. if (cv_kartdebugshrink.value && !modeattacking && !p->bot) - mobj->destscale = 6*mobj->destscale/8; + { + mobj->destscale = FixedMul(mobj->destscale, SHRINK_SCALE); + } + + // set the scale to the mobj's destscale so settings get correctly set. if we don't, they sometimes don't. P_SetScale(mobj, mobj->destscale); P_FlashPal(p, 0, 0); // Resets From 49930adda2cf630bd982e309de15bdbbc0882841 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 9 Jan 2022 00:10:14 -0500 Subject: [PATCH 176/379] Update how scaled speed is calculated This makes it so that the scaled speed shows up on the percentage speedometer. --- src/k_kart.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 3d2dfdca6..8fcd81b30 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2908,22 +2908,20 @@ static void K_GetKartBoostPower(player_t *player) fixed_t K_GrowShrinkSpeedMul(player_t *player) { fixed_t scaleDiff = player->mo->scale - mapobjectscale; - fixed_t physicsScale = mapobjectscale; + fixed_t playerScale = FixedDiv(player->mo->scale, mapobjectscale); fixed_t speedMul = FRACUNIT; if (scaleDiff > 0) { // Grown // Change x2 speed into x1.5 - physicsScale = FixedMul(GROW_PHYSICS_SCALE, mapobjectscale); - speedMul = FixedDiv(physicsScale, player->mo->scale); + speedMul = FixedDiv(FixedMul(playerScale, GROW_PHYSICS_SCALE), GROW_SCALE); } else if (scaleDiff < 0) { // Shrunk // Change x0.5 speed into x0.75 - physicsScale = FixedMul(SHRINK_PHYSICS_SCALE, mapobjectscale); - speedMul = FixedDiv(physicsScale, player->mo->scale); + speedMul = FixedDiv(FixedMul(playerScale, SHRINK_PHYSICS_SCALE), SHRINK_SCALE); } return speedMul; @@ -2967,18 +2965,16 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) } } - if (mobjValid == true) - { - finalspeed = FixedMul(finalspeed, player->mo->scale); - finalspeed = FixedMul(finalspeed, K_GrowShrinkSpeedMul(player)); - } - else - { - finalspeed = FixedMul(finalspeed, mapobjectscale); - } + finalspeed = FixedMul(finalspeed, mapobjectscale); if (doboostpower == true) { + if (mobjValid == true) + { + // Scale with the player. + finalspeed = FixedMul(finalspeed, K_GrowShrinkSpeedMul(player)); + } + if (K_PlayerUsesBotMovement(player)) { finalspeed = FixedMul(finalspeed, K_BotTopSpeedRubberband(player)); From 2683b6d018ef7cd948efbf3acc980f8cb8aa62a6 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 9 Jan 2022 01:16:36 -0500 Subject: [PATCH 177/379] Keep your large items --- src/d_player.h | 5 ++ src/k_kart.c | 151 ++++++++++++++++++++++++++++++++++++++----------- src/k_kart.h | 4 ++ src/p_enemy.c | 2 +- 4 files changed, 129 insertions(+), 33 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 4a4b85981..c09afe13a 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -260,6 +260,10 @@ typedef enum // for kickstartaccel #define ACCEL_KICKSTART 35 +#define ITEMSCALE_NORMAL 0 +#define ITEMSCALE_GROW 1 +#define ITEMSCALE_SHRINK 2 + // player_t struct for all respawn variables typedef struct respawnvars_s { @@ -436,6 +440,7 @@ typedef struct player_s SINT8 itemtype; // KITEM_ constant for item number UINT8 itemamount; // Amount of said item SINT8 throwdir; // Held dir of controls; 1 = forward, 0 = none, -1 = backward (was "player->heldDir") + UINT8 itemscale; // Item scale value, from when an item was taken out. (0 for normal, 1 for grow, 2 for shrink.) UINT8 sadtimer; // How long you've been sad diff --git a/src/k_kart.c b/src/k_kart.c index 8fcd81b30..108501eb0 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3824,24 +3824,56 @@ void K_SpawnMineExplosion(mobj_t *source, UINT8 color) #undef MINEQUAKEDIST +fixed_t K_ItemScaleForPlayer(player_t *player) +{ + switch (player->itemscale) + { + case ITEMSCALE_GROW: + return FixedMul(GROW_SCALE, mapobjectscale); + + case ITEMSCALE_SHRINK: + return FixedMul(SHRINK_SCALE, mapobjectscale); + + default: + return mapobjectscale; + } +} + static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, INT32 flags2, fixed_t speed) { mobj_t *th; fixed_t x, y, z; fixed_t finalspeed = speed; + fixed_t finalscale = mapobjectscale; mobj_t *throwmo; - if (source->player && source->player->speed > K_GetKartSpeed(source->player, false)) + if (source->player != NULL) { - angle_t input = source->angle - an; - boolean invert = (input > ANGLE_180); - if (invert) - input = InvAngle(input); + if (source->player->itemscale == ITEMSCALE_SHRINK) + { + // Nerf the base item speed a bit. + finalspeed = FixedMul(finalspeed, SHRINK_PHYSICS_SCALE); + } - finalspeed = max(speed, FixedMul(speed, FixedMul( - FixedDiv(source->player->speed, K_GetKartSpeed(source->player, false)), // Multiply speed to be proportional to your own, boosted maxspeed. - (((180<player->speed > K_GetKartSpeed(source->player, false)) + { + angle_t input = source->angle - an; + boolean invert = (input > ANGLE_180); + if (invert) + input = InvAngle(input); + + finalspeed = max(speed, FixedMul(speed, FixedMul( + FixedDiv(source->player->speed, K_GetKartSpeed(source->player, false)), // Multiply speed to be proportional to your own, boosted maxspeed. + (((180<player); + } + + if (type == MT_BUBBLESHIELDTRAP) + { + finalscale = source->scale; } x = source->x + source->momx + FixedMul(finalspeed, FINECOSINE(an>>ANGLETOFINESHIFT)); @@ -3860,8 +3892,8 @@ static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, I P_SetTarget(&th->target, source); - P_SetScale(th, source->scale); - th->destscale = source->destscale; + P_SetScale(th, finalscale); + th->destscale = finalscale; if (P_IsObjectOnGround(source)) { @@ -3887,6 +3919,11 @@ static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, I th->momy = FixedMul(finalspeed, FINESINE(an>>ANGLETOFINESHIFT)); th->momz = source->momz; + if (source->player != NULL) + { + th->cusval = source->player->itemscale; + } + switch (type) { case MT_ORBINAUT: @@ -4781,13 +4818,15 @@ mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, } else { + fixed_t finalscale = K_ItemScaleForPlayer(player); + player->bananadrag = 0; // RESET timer, for multiple bananas if (dir > 0) { // Shoot forward mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, mapthing); - //K_FlipFromObject(mo, player->mo); + // These are really weird so let's make it a very specific case to make SURE it works... if (player->mo->eflags & MFE_VERTICALFLIP) { @@ -4814,6 +4853,9 @@ mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, if (mo->eflags & MFE_UNDERWATER) mo->momz = (117 * mo->momz) / 200; + + P_SetScale(mo, finalscale); + mo->destscale = finalscale; } // this is the small graphic effect that plops in you when you throw an item: @@ -4828,6 +4870,9 @@ mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, } throwmo->movecount = 0; // above player + + P_SetScale(throwmo, finalscale); + throwmo->destscale = finalscale; } else { @@ -4865,8 +4910,8 @@ mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, mo->threshold = 10; P_SetTarget(&mo->target, player->mo); - P_SetScale(mo, player->mo->scale); - mo->destscale = player->mo->destscale; + P_SetScale(mo, finalscale); + mo->destscale = finalscale; if (P_IsObjectOnGround(player->mo)) { @@ -4936,6 +4981,10 @@ void K_PuntMine(mobj_t *origMine, mobj_t *punter) mine->floorz = origMine->floorz; mine->ceilingz = origMine->ceilingz; + P_SetScale(mine, origMine->scale); + mine->destscale = origMine->destscale; + mine->scalespeed = origMine->scalespeed; + // Copy interp data mine->old_angle = origMine->old_angle; mine->old_x = origMine->old_x; @@ -4944,8 +4993,7 @@ void K_PuntMine(mobj_t *origMine, mobj_t *punter) // Since we aren't using P_KillMobj, we need to clean up the hnext reference P_SetTarget(&mineOwner->hnext, NULL); - mineOwner->player->bananadrag = 0; - mineOwner->player->pflags &= ~PF_ITEMOUT; + K_UnsetItemOut(mineOwner->player); if (mineOwner->player->itemamount) { @@ -5102,7 +5150,7 @@ static void K_DoHyudoroSteal(player_t *player) player->itemtype = KITEM_KITCHENSINK; player->itemamount = 1; - player->pflags &= ~PF_ITEMOUT; + K_UnsetItemOut(player); return; } else if ((gametype == GT_RACE && player->position == 1) || numplayers == 0) // No-one can be stolen from? Oh well... @@ -5128,11 +5176,11 @@ static void K_DoHyudoroSteal(player_t *player) player->itemtype = players[stealplayer].itemtype; player->itemamount = players[stealplayer].itemamount; - player->pflags &= ~PF_ITEMOUT; + K_UnsetItemOut(player); players[stealplayer].itemtype = KITEM_NONE; players[stealplayer].itemamount = 0; - players[stealplayer].pflags &= ~PF_ITEMOUT; + K_UnsetItemOut(&players[stealplayer]); if (P_IsDisplayPlayer(&players[stealplayer]) && !r_splitscreen) S_StartSound(NULL, sfx_s3k92); @@ -5455,7 +5503,7 @@ void K_DropHnextList(player_t *player, boolean keepshields) player->curshield = KSHIELD_NONE; player->itemtype = KITEM_NONE; player->itemamount = 0; - player->pflags &= ~PF_ITEMOUT; + K_UnsetItemOut(player); } nextwork = work->hnext; @@ -5504,6 +5552,10 @@ void K_DropHnextList(player_t *player, boolean keepshields) dropwork->angle = work->angle; + P_SetScale(dropwork, work->scale); + dropwork->destscale = work->destscale; + dropwork->scalespeed = work->scalespeed; + dropwork->flags |= MF_NOCLIPTHING; dropwork->flags2 = work->flags2; dropwork->eflags = work->eflags; @@ -5593,7 +5645,7 @@ void K_DropHnextList(player_t *player, boolean keepshields) && (dropall || (--player->itemamount <= 0))) { player->itemamount = 0; - player->pflags &= ~PF_ITEMOUT; + K_UnsetItemOut(player); player->itemtype = KITEM_NONE; } } @@ -5968,6 +6020,8 @@ static void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z // Move the hnext chain! static void K_MoveHeldObjects(player_t *player) { + fixed_t finalscale = INT32_MAX; + if (!player->mo) return; @@ -5979,7 +6033,7 @@ static void K_MoveHeldObjects(player_t *player) else if (player->pflags & PF_ITEMOUT) { player->itemamount = 0; - player->pflags &= ~PF_ITEMOUT; + K_UnsetItemOut(player); player->itemtype = KITEM_NONE; } return; @@ -5995,12 +6049,14 @@ static void K_MoveHeldObjects(player_t *player) else if (player->pflags & PF_ITEMOUT) { player->itemamount = 0; - player->pflags &= ~PF_ITEMOUT; + K_UnsetItemOut(player); player->itemtype = KITEM_NONE; } return; } + finalscale = K_ItemScaleForPlayer(player); + switch (player->mo->hnext->type) { case MT_ORBINAUT_SHIELD: // Kart orbit items @@ -6039,7 +6095,7 @@ static void K_MoveHeldObjects(player_t *player) cur->eflags &= ~MFE_VERTICALFLIP; // Shrink your items if the player shrunk too. - P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale))); + P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), finalscale))); if (P_MobjFlip(cur) > 0) z = player->mo->z; @@ -6070,7 +6126,7 @@ static void K_MoveHeldObjects(player_t *player) } // Center it during the scale up animation - z += (FixedMul(mobjinfo[cur->type].height, player->mo->scale - cur->scale)>>1) * P_MobjFlip(cur); + z += (FixedMul(mobjinfo[cur->type].height, finalscale - cur->scale)>>1) * P_MobjFlip(cur); cur->z = z; cur->momx = cur->momy = 0; @@ -6132,7 +6188,7 @@ static void K_MoveHeldObjects(player_t *player) continue; // Shrink your items if the player shrunk too. - P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale))); + P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), finalscale))); ang = targ->angle; targx = targ->x + P_ReturnThrustX(cur, ang + ANGLE_180, dist); @@ -8723,6 +8779,31 @@ static void K_trickPanelTimingVisual(player_t *player, fixed_t momz) #undef RADIUSSCALING #undef MINRADIUS +void K_SetItemOut(player_t *player) +{ + player->pflags |= PF_ITEMOUT; + + if (player->mo->scale >= FixedMul(GROW_PHYSICS_SCALE, mapobjectscale)) + { + player->itemscale = ITEMSCALE_GROW; + } + else if (player->mo->scale <= FixedMul(SHRINK_PHYSICS_SCALE, mapobjectscale)) + { + player->itemscale = ITEMSCALE_SHRINK; + } + else + { + player->itemscale = ITEMSCALE_NORMAL; + } +} + +void K_UnsetItemOut(player_t *player) +{ + player->pflags &= ~PF_ITEMOUT; + player->itemscale = ITEMSCALE_NORMAL; + player->bananadrag = 0; +} + // // K_MoveKartPlayer // @@ -8823,7 +8904,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) } else if (player->itemamount == 0) { - player->pflags &= ~PF_ITEMOUT; + K_UnsetItemOut(player); } else { @@ -8898,7 +8979,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) mobj_t *prev = player->mo; //K_PlayAttackTaunt(player->mo); - player->pflags |= PF_ITEMOUT; + K_SetItemOut(player); S_StartSound(player->mo, sfx_s254); for (moloop = 0; moloop < player->itemamount; moloop++) @@ -8913,6 +8994,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) mo->threshold = 10; mo->movecount = player->itemamount; mo->movedir = moloop+1; + mo->cusval = player->itemscale; P_SetTarget(&mo->target, player->mo); P_SetTarget(&mo->hprev, prev); P_SetTarget(&prev->hnext, mo); @@ -8942,6 +9024,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) mo->threshold = 10; mo->movecount = 1; mo->movedir = 1; + mo->cusval = player->itemscale; P_SetTarget(&mo->target, player->mo); P_SetTarget(&player->mo->hnext, mo); } @@ -8956,7 +9039,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) mobj_t *prev = player->mo; //K_PlayAttackTaunt(player->mo); - player->pflags |= PF_ITEMOUT; + K_SetItemOut(player); S_StartSound(player->mo, sfx_s3k3a); for (moloop = 0; moloop < player->itemamount; moloop++) @@ -8974,6 +9057,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) mo->movecount = player->itemamount; mo->movedir = mo->lastlook = moloop+1; mo->color = player->skincolor; + mo->cusval = player->itemscale; P_SetTarget(&mo->target, player->mo); P_SetTarget(&mo->hprev, prev); P_SetTarget(&prev->hnext, mo); @@ -8997,7 +9081,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) mobj_t *prev = player->mo; //K_PlayAttackTaunt(player->mo); - player->pflags |= PF_ITEMOUT; + K_SetItemOut(player); S_StartSound(player->mo, sfx_s3k3a); for (moloop = 0; moloop < player->itemamount; moloop++) @@ -9014,6 +9098,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) mo->threshold = 10; mo->movecount = player->itemamount; mo->movedir = mo->lastlook = moloop+1; + mo->cusval = player->itemscale; P_SetTarget(&mo->target, player->mo); P_SetTarget(&mo->hprev, prev); P_SetTarget(&prev->hnext, mo); @@ -9035,7 +9120,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) { mobj_t *mo; - player->pflags |= PF_ITEMOUT; + K_SetItemOut(player); S_StartSound(player->mo, sfx_s254); mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SSMINE_SHIELD); if (mo) @@ -9044,6 +9129,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) mo->threshold = 10; mo->movecount = 1; mo->movedir = 1; + mo->cusval = player->itemscale; P_SetTarget(&mo->target, player->mo); P_SetTarget(&player->mo->hnext, mo); } @@ -9293,7 +9379,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) { mobj_t *mo; - player->pflags |= PF_ITEMOUT; + K_SetItemOut(player); S_StartSound(player->mo, sfx_s254); mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SINK_SHIELD); if (mo) @@ -9302,6 +9388,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) mo->threshold = 10; mo->movecount = 1; mo->movedir = 1; + mo->cusval = player->itemscale; P_SetTarget(&mo->target, player->mo); P_SetTarget(&player->mo->hnext, mo); } diff --git a/src/k_kart.h b/src/k_kart.h index c05620b74..a8834397c 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -148,5 +148,9 @@ void K_PlayPainSound(mobj_t *source); void K_PlayHitEmSound(mobj_t *source); void K_PlayPowerGloatSound(mobj_t *source); +fixed_t K_ItemScaleForPlayer(player_t *player); +void K_SetItemOut(player_t *player); +void K_UnsetItemOut(player_t *player); + // ========================================================================= #endif // __K_KART__ diff --git a/src/p_enemy.c b/src/p_enemy.c index d9b1fd840..04e77bd8f 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -14107,7 +14107,7 @@ void A_SSMineExplode(mobj_t *actor) INT32 d; INT32 locvar1 = var1; mobjtype_t type; - explodedist = FixedMul((3*actor->info->painchance)/2, mapobjectscale); + explodedist = FixedMul((3*actor->info->painchance)/2, actor->scale); if (LUA_CallAction(A_SSMINEEXPLODE, actor)) return; From fd7c377043ccdc13be8db714f0a06c713bb812eb Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 9 Jan 2022 04:08:26 -0500 Subject: [PATCH 178/379] Force shrink can be toggled individually online In preparation for a "make yourself small by default" cheat on the char select like SMK. --- src/d_netcmd.c | 30 ++++++++++++++++++++---- src/d_player.h | 11 +++++---- src/deh_tables.c | 4 ++++ src/g_demo.c | 59 +++++++++++++++++++++++++++++++++++++----------- src/g_game.c | 34 ++++++++++++++++------------ src/g_game.h | 9 ++++++++ src/k_kart.c | 35 ++++++++++++++++++++++++---- src/k_kart.h | 2 ++ src/k_respawn.c | 2 +- src/p_mobj.c | 2 +- src/p_setup.c | 1 + 11 files changed, 148 insertions(+), 41 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 2d80f31fd..ac1ca9c69 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -423,7 +423,7 @@ consvar_t cv_kartallowgiveitem = CVAR_INIT ("kartallowgiveitem", #endif CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, CV_YesNo, NULL ); -consvar_t cv_kartdebugshrink = CVAR_INIT ("kartdebugshrink", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, CV_OnOff, NULL); + consvar_t cv_kartdebugdistribution = CVAR_INIT ("kartdebugdistribution", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, CV_OnOff, NULL); consvar_t cv_kartdebughuddrop = CVAR_INIT ("kartdebughuddrop", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, CV_OnOff, NULL); static CV_PossibleValue_t kartdebugwaypoint_cons_t[] = {{0, "Off"}, {1, "Forwards"}, {2, "Backwards"}, {0, NULL}}; @@ -934,6 +934,14 @@ void D_RegisterClientCommands(void) for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { CV_RegisterVar(&cv_kickstartaccel[i]); + CV_RegisterVar(&cv_shrinkme[i]); + CV_RegisterVar(&cv_turnaxis[i]); + CV_RegisterVar(&cv_moveaxis[i]); + CV_RegisterVar(&cv_brakeaxis[i]); + CV_RegisterVar(&cv_aimaxis[i]); + CV_RegisterVar(&cv_lookaxis[i]); + CV_RegisterVar(&cv_fireaxis[i]); + CV_RegisterVar(&cv_driftaxis[i]); CV_RegisterVar(&cv_deadzone[i]); } @@ -1627,10 +1635,13 @@ void SendWeaponPref(UINT8 n) UINT8 buf[1]; buf[0] = 0; - // Player option cvars that need to be synched go HERE + if (cv_kickstartaccel[n].value) buf[0] |= 1; + if (cv_shrinkme[n].value) + buf[0] |= 2; + SendNetXCmdForPlayer(n, XD_WEAPONPREF, buf, 1); } @@ -1638,11 +1649,22 @@ static void Got_WeaponPref(UINT8 **cp,INT32 playernum) { UINT8 prefs = READUINT8(*cp); - // Player option cvars that need to be synched go HERE - players[playernum].pflags &= ~(PF_KICKSTARTACCEL); + players[playernum].pflags &= ~(PF_KICKSTARTACCEL|PF_SHRINKME); + if (prefs & 1) players[playernum].pflags |= PF_KICKSTARTACCEL; + if (prefs & 2) + players[playernum].pflags |= PF_SHRINKME; + + if (leveltime < 2) + { + // BAD HACK: No other place I tried to slot this in + // made it work for the host when they initally host, + // so this will have to do. + K_UpdateShrinkCheat(&players[playernum]); + } + // SEE ALSO g_demo.c demo_extradata[playernum] |= DXD_WEAPONPREF; } diff --git a/src/d_player.h b/src/d_player.h index c09afe13a..5aba0dd0b 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -59,10 +59,10 @@ typedef enum typedef enum { // True if button down last tic. - PF_ATTACKDOWN = 1, - PF_ACCELDOWN = 1<<1, - PF_BRAKEDOWN = 1<<2, - PF_LOOKDOWN = 1<<3, + PF_ATTACKDOWN = 1, + PF_ACCELDOWN = 1<<1, + PF_BRAKEDOWN = 1<<2, + PF_LOOKDOWN = 1<<3, // Accessibility and cheats PF_KICKSTARTACCEL = 1<<4, // Is accelerate in kickstart mode? @@ -99,6 +99,9 @@ typedef enum PF_HITFINISHLINE = 1<<26, // Already hit the finish line this tic PF_WRONGWAY = 1<<27, // Moving the wrong way with respect to waypoints? + PF_SHRINKME = 1<<28, // "Shrink me" cheat preference + PF_SHRINKACTIVE = 1<<29, // "Shrink me" cheat is in effect. (Can't be disabled mid-race) + // up to 1<<31 is free } pflags_t; diff --git a/src/deh_tables.c b/src/deh_tables.c index f2e0dc966..3980e1998 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5912,6 +5912,10 @@ const char *const PLAYERFLAG_LIST[] = { "HITFINISHLINE", // Already hit the finish line this tic "WRONGWAY", // Moving the wrong way with respect to waypoints? + + "SHRINKME", + "SHRINKACTIVE", + NULL // stop loop here. }; diff --git a/src/g_demo.c b/src/g_demo.c index 25404a809..749309828 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -122,8 +122,9 @@ demoghost *ghosts = NULL; #define DF_ENCORE 0x40 #define DF_MULTIPLAYER 0x80 // This demo was recorded in multiplayer mode! -#define DEMO_SPECTATOR 0x40 -#define DEMO_KICKSTART 0x20 +#define DEMO_SPECTATOR 0x01 +#define DEMO_KICKSTART 0x02 +#define DEMO_SHRINKME 0x04 // For demos #define ZT_FWD 0x01 @@ -351,9 +352,20 @@ void G_ReadDemoExtraData(void) if (extradata & DXD_WEAPONPREF) { i = READUINT8(demo_p); - players[p].pflags &= ~(PF_KICKSTARTACCEL); + players[p].pflags &= ~(PF_KICKSTARTACCEL|PF_SHRINKME); if (i & 1) players[p].pflags |= PF_KICKSTARTACCEL; + if (i & 2) + players[p].pflags |= PF_SHRINKME; + + if (leveltime < 2) + { + // BAD HACK: No other place I tried to slot this in + // made it work for the host when they initally host, + // so this will have to do. + K_UpdateShrinkCheat(&players[p]); + } + //CONS_Printf("weaponpref is %d for player %d\n", i, p); } @@ -466,6 +478,8 @@ void G_WriteDemoExtraData(void) UINT8 prefs = 0; if (players[i].pflags & PF_KICKSTARTACCEL) prefs |= 1; + if (players[i].pflags & PF_SHRINKME) + prefs |= 2; WRITEUINT8(demo_p, prefs); } } @@ -2015,12 +2029,15 @@ void G_BeginRecording(void) for (p = 0; p < MAXPLAYERS; p++) { if (playeringame[p]) { player = &players[p]; + WRITEUINT8(demo_p, p); - i = p; - if (player->pflags & PF_KICKSTARTACCEL) - i |= DEMO_KICKSTART; + i = 0; if (player->spectator) i |= DEMO_SPECTATOR; + if (player->pflags & PF_KICKSTARTACCEL) + i |= DEMO_KICKSTART; + if (player->pflags & PF_SHRINKME) + i |= DEMO_SHRINKME; WRITEUINT8(demo_p, i); // Name @@ -2672,7 +2689,7 @@ void G_DoPlayDemo(char *defdemoname) UINT32 randseed; char msg[1024]; - boolean spectator, kickstart; + boolean spectator, kickstart, shrinkme; UINT8 slots[MAXPLAYERS], kartspeed[MAXPLAYERS], kartweight[MAXPLAYERS], numslots = 0; #if defined(SKIPERRORS) && !defined(DEVELOP) @@ -2943,9 +2960,13 @@ void G_DoPlayDemo(char *defdemoname) while (p != 0xFF) { - if ((spectator = !!(p & DEMO_SPECTATOR))) + UINT8 flags = READUINT8(demo_p); + + spectator = kickstart = shrinkme = false; + + if ((spectator = !!(flags & DEMO_SPECTATOR)) == true) { - p &= ~DEMO_SPECTATOR; + flags &= ~DEMO_SPECTATOR; if (modeattacking) { @@ -2960,10 +2981,14 @@ void G_DoPlayDemo(char *defdemoname) } } - if ((kickstart = (p & DEMO_KICKSTART))) - p &= ~DEMO_KICKSTART; + if ((kickstart = !!(flags & DEMO_KICKSTART)) == true) + flags &= ~DEMO_KICKSTART; - slots[numslots] = p; numslots++; + if ((shrinkme = !!(flags & DEMO_SHRINKME)) == true) + flags &= ~DEMO_SHRINKME; + + slots[numslots] = p; + numslots++; if (modeattacking && numslots > 1) { @@ -2982,11 +3007,19 @@ void G_DoPlayDemo(char *defdemoname) playeringame[p] = true; players[p].spectator = spectator; + if (kickstart) players[p].pflags |= PF_KICKSTARTACCEL; else players[p].pflags &= ~PF_KICKSTARTACCEL; + if (shrinkme) + players[p].pflags |= PF_SHRINKME; + else + players[p].pflags &= ~PF_SHRINKME; + + K_UpdateShrinkCheat(&players[p]); + // Name M_Memcpy(player_names[p],demo_p,16); demo_p += 16; @@ -3245,7 +3278,7 @@ void G_AddGhost(char *defdemoname) return; } - if ((READUINT8(p) & ~DEMO_KICKSTART) != 0) + if ((READUINT8(p) & ~(DEMO_KICKSTART|DEMO_SHRINKME)) != 0) { CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot.\n"), pdemoname); Z_Free(pdemoname); diff --git a/src/g_game.c b/src/g_game.c index 6d0620701..0fdf43e43 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -341,10 +341,10 @@ INT16 prevmap, nextmap; static UINT8 *savebuffer; -static void kickstartaccel_OnChange(void); -static void kickstartaccel2_OnChange(void); -static void kickstartaccel3_OnChange(void); -static void kickstartaccel4_OnChange(void); +static void weaponPrefChange(void); +static void weaponPrefChange2(void); +static void weaponPrefChange3(void); +static void weaponPrefChange4(void); // don't mind me putting these here, I was lazy to figure out where else I could put those without blowing up the compiler. @@ -389,10 +389,17 @@ consvar_t cv_resetspecialmusic = CVAR_INIT ("resetspecialmusic", "Yes", CV_SAVE, consvar_t cv_resume = CVAR_INIT ("resume", "Yes", CV_SAVE, CV_YesNo, NULL); consvar_t cv_kickstartaccel[MAXSPLITSCREENPLAYERS] = { - CVAR_INIT ("kickstartaccel", "Off", CV_SAVE|CV_CALL, CV_OnOff, kickstartaccel_OnChange), - CVAR_INIT ("kickstartaccel2", "Off", CV_SAVE|CV_CALL, CV_OnOff, kickstartaccel2_OnChange), - CVAR_INIT ("kickstartaccel3", "Off", CV_SAVE|CV_CALL, CV_OnOff, kickstartaccel3_OnChange), - CVAR_INIT ("kickstartaccel4", "Off", CV_SAVE|CV_CALL, CV_OnOff, kickstartaccel4_OnChange) + CVAR_INIT ("kickstartaccel", "Off", CV_SAVE|CV_CALL, CV_OnOff, weaponPrefChange), + CVAR_INIT ("kickstartaccel2", "Off", CV_SAVE|CV_CALL, CV_OnOff, weaponPrefChange2), + CVAR_INIT ("kickstartaccel3", "Off", CV_SAVE|CV_CALL, CV_OnOff, weaponPrefChange3), + CVAR_INIT ("kickstartaccel4", "Off", CV_SAVE|CV_CALL, CV_OnOff, weaponPrefChange4) +}; + +consvar_t cv_shrinkme[MAXSPLITSCREENPLAYERS] = { + CVAR_INIT ("shrinkme", "Off", CV_CALL, CV_OnOff, weaponPrefChange), + CVAR_INIT ("shrinkme2", "Off", CV_CALL, CV_OnOff, weaponPrefChange2), + CVAR_INIT ("shrinkme3", "Off", CV_CALL, CV_OnOff, weaponPrefChange3), + CVAR_INIT ("shrinkme4", "Off", CV_CALL, CV_OnOff, weaponPrefChange4) }; static CV_PossibleValue_t zerotoone_cons_t[] = {{0, "MIN"}, {FRACUNIT, "MAX"}, {0, NULL}}; @@ -1121,22 +1128,22 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n) return dest; } -static void kickstartaccel_OnChange(void) +static void weaponPrefChange(void) { SendWeaponPref(0); } -static void kickstartaccel2_OnChange(void) +static void weaponPrefChange2(void) { SendWeaponPref(1); } -static void kickstartaccel3_OnChange(void) +static void weaponPrefChange3(void) { SendWeaponPref(2); } -static void kickstartaccel4_OnChange(void) +static void weaponPrefChange4(void) { SendWeaponPref(3); } @@ -2091,7 +2098,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) botdiffincrease = players[player].botvars.diffincrease; botrival = players[player].botvars.rival; - pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_KICKSTARTACCEL)); + pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_KICKSTARTACCEL|PF_SHRINKME|PF_SHRINKACTIVE)); // SRB2kart if (betweenmaps || leveltime < introtime) @@ -2162,7 +2169,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) if (!(netgame || multiplayer)) pflags |= (players[player].pflags & (PF_GODMODE|PF_NOCLIP)); - // Obliterate follower from existence P_SetTarget(&players[player].follower, NULL); diff --git a/src/g_game.h b/src/g_game.h index 0e249cf63..28831b875 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -55,6 +55,15 @@ extern consvar_t cv_pauseifunfocused; extern consvar_t cv_invertmouse; extern consvar_t cv_kickstartaccel[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_shrinkme[MAXSPLITSCREENPLAYERS]; + +extern consvar_t cv_turnaxis[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_moveaxis[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_brakeaxis[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_aimaxis[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_lookaxis[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_fireaxis[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_driftaxis[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_deadzone[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_ghost_besttime, cv_ghost_bestlap, cv_ghost_last, cv_ghost_guest, cv_ghost_staff; diff --git a/src/k_kart.c b/src/k_kart.c index 108501eb0..154be4a91 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -251,7 +251,6 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_kartdebugitem); CV_RegisterVar(&cv_kartdebugamount); - CV_RegisterVar(&cv_kartdebugshrink); CV_RegisterVar(&cv_kartallowgiveitem); CV_RegisterVar(&cv_kartdebugdistribution); CV_RegisterVar(&cv_kartdebughuddrop); @@ -3020,6 +3019,34 @@ UINT16 K_GetKartFlashing(player_t *player) return tics; } +boolean K_PlayerShrinkCheat(player_t *player) +{ + return ( + (player->pflags & PF_SHRINKACTIVE) + && (player->bot == false) + && (modeattacking == false) // Anyone want to make another record attack category? + ); +} + +void K_UpdateShrinkCheat(player_t *player) +{ + const boolean mobjValid = (player->mo != NULL && P_MobjWasRemoved(player->mo) == false); + + if (player->pflags & PF_SHRINKME) + { + player->pflags |= PF_SHRINKACTIVE; + } + else + { + player->pflags &= ~PF_SHRINKACTIVE; + } + + if (mobjValid == true && K_PlayerShrinkCheat(player) == true) + { + player->mo->destscale = FixedMul(mapobjectscale, SHRINK_SCALE); + } +} + boolean K_KartKickstart(player_t *player) { return ((player->pflags & PF_KICKSTARTACCEL) @@ -3293,7 +3320,7 @@ static void K_RemoveGrowShrink(player_t *player) player->mo->scalespeed = mapobjectscale/TICRATE; player->mo->destscale = mapobjectscale; - if (cv_kartdebugshrink.value && !modeattacking && !player->bot) + if (K_PlayerShrinkCheat(player) == true) { player->mo->destscale = FixedMul(player->mo->destscale, SHRINK_SCALE); } @@ -5288,7 +5315,7 @@ static void K_DoShrink(player_t *user) players[i].mo->scalespeed = mapobjectscale/TICRATE; players[i].mo->destscale = FixedMul(mapobjectscale, SHRINK_SCALE); - if (cv_kartdebugshrink.value && !modeattacking && !players[i].bot) + if (K_PlayerShrinkCheat(&players[i]) == true) { players[i].mo->destscale = FixedMul(players[i].mo->destscale, SHRINK_SCALE); } @@ -9182,7 +9209,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->mo->scalespeed = mapobjectscale/TICRATE; player->mo->destscale = FixedMul(mapobjectscale, GROW_SCALE); - if (cv_kartdebugshrink.value && !modeattacking && !player->bot) + if (K_PlayerShrinkCheat(player) == true) { player->mo->destscale = FixedMul(player->mo->destscale, SHRINK_SCALE); } diff --git a/src/k_kart.h b/src/k_kart.h index a8834397c..91024488f 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -126,6 +126,8 @@ fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed); fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower); fixed_t K_GetKartAccel(player_t *player); UINT16 K_GetKartFlashing(player_t *player); +boolean K_PlayerShrinkCheat(player_t *player); +void K_UpdateShrinkCheat(player_t *player); boolean K_KartKickstart(player_t *player); UINT16 K_GetKartButtons(player_t *player); SINT8 K_GetForwardMove(player_t *player); diff --git a/src/k_respawn.c b/src/k_respawn.c index 2146b2d48..1576af681 100644 --- a/src/k_respawn.c +++ b/src/k_respawn.c @@ -674,7 +674,7 @@ static void K_HandleDropDash(player_t *player) player->mo->scalespeed = mapobjectscale/TICRATE; player->mo->destscale = FixedMul(mapobjectscale, SHRINK_SCALE); - if (cv_kartdebugshrink.value && !modeattacking && !player->bot) + if (K_PlayerShrinkCheat(player) == true) { player->mo->destscale = FixedMul(player->mo->destscale, SHRINK_SCALE); } diff --git a/src/p_mobj.c b/src/p_mobj.c index 47aa07120..5f70192c9 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10867,7 +10867,7 @@ void P_SpawnPlayer(INT32 playernum) P_SetTarget(&p->follower, NULL); // cleanse follower from existence - if (cv_kartdebugshrink.value && !modeattacking && !p->bot) + if (K_PlayerShrinkCheat(p) == true) { mobj->destscale = FixedMul(mobj->destscale, SHRINK_SCALE); } diff --git a/src/p_setup.c b/src/p_setup.c index 704bb99ec..15b6c7413 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3466,6 +3466,7 @@ static void P_InitLevelSettings(void) players[i].lives = 3; G_PlayerReborn(i, true); + K_UpdateShrinkCheat(&players[i]); } racecountdown = exitcountdown = exitfadestarted = 0; From fae99a2c676d11408e5df29c4b590b07e31a3f69 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 20 Jan 2022 03:42:27 -0800 Subject: [PATCH 179/379] Fix opengl semibright Adds back 3d models semibright. --- src/hardware/hw_main.c | 38 ++++++++++++++++++++++---------------- src/hardware/hw_main.h | 1 + src/hardware/hw_md2.c | 10 ++++++---- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index e8eea7e11..0ab756c9d 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -181,6 +181,18 @@ boolean gl_shadersavailable = true; // Lighting // ========================================================================== +boolean HWR_OverrideObjectLightLevel(mobj_t *thing, INT32 *lightlevel) +{ + if (R_ThingIsFullBright(thing)) + *lightlevel = 255; + else if (R_ThingIsFullDark(thing)) + *lightlevel = 0; + else + return false; + + return true; +} + void HWR_Lighting(FSurfaceInfo *Surface, INT32 light_level, extracolormap_t *colormap) { RGBA_t poly_color, tint_color, fade_color; @@ -3795,7 +3807,7 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) patch_t *gpatch; FSurfaceInfo Surf; extracolormap_t *colormap = NULL; - FUINT lightlevel; + INT32 lightlevel; boolean lightset = true; FBITFIELD blend = 0; FBITFIELD occlusion; @@ -3931,12 +3943,7 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) i = 0; temp = FLOAT_TO_FIXED(realtop); - if (R_ThingIsFullBright(spr->mobj)) - lightlevel = 255; - else if (R_ThingIsFullDark(spr->mobj)) - lightlevel = 0; - else - lightset = false; + lightset = HWR_OverrideObjectLightLevel(spr->mobj, &lightlevel); for (i = 1; i < sector->numlights; i++) { @@ -3963,7 +3970,13 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) if (!(list[i].flags & FF_NOSHADE) && (list[i].flags & FF_CUTSPRITES)) { if (!lightset) + { lightlevel = *list[i].lightlevel > 255 ? 255 : *list[i].lightlevel; + + if (R_ThingIsSemiBright(spr->mobj)) + lightlevel = 128 + (lightlevel>>1); + } + if (!(spr->mobj->renderflags & RF_NOCOLORMAPS)) colormap = *list[i].extra_colormap; } @@ -4279,17 +4292,10 @@ static void HWR_DrawSprite(gl_vissprite_t *spr) // colormap test { sector_t *sector = spr->mobj->subsector->sector; - UINT8 lightlevel = 0; - boolean lightset = true; + INT32 lightlevel = 0; + boolean lightset = HWR_OverrideObjectLightLevel(spr->mobj, &lightlevel); extracolormap_t *colormap = NULL; - if (R_ThingIsFullBright(spr->mobj)) - lightlevel = 255; - else if (R_ThingIsFullDark(spr->mobj)) - lightlevel = 0; - else - lightset = false; - if (!(spr->mobj->renderflags & RF_NOCOLORMAPS)) colormap = sector->extra_colormap; diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index 4ff46ba6a..33101edb2 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -67,6 +67,7 @@ void HWR_MakeScreenFinalTexture(void); void HWR_DrawScreenFinalTexture(int width, int height); // This stuff is put here so models can use them +boolean HWR_OverrideObjectLightLevel(mobj_t *thing, INT32 *lightlevel); void HWR_Lighting(FSurfaceInfo *Surface, INT32 light_level, extracolormap_t *colormap); UINT8 HWR_FogBlockAlpha(INT32 light, extracolormap_t *colormap); // Let's see if this can work diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index cf9d0b5c8..c2b593f06 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -1324,7 +1324,8 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) if (spr->mobj->subsector) { sector_t *sector = spr->mobj->subsector->sector; - UINT8 lightlevel = 255; + INT32 lightlevel = 255; + boolean lightset = HWR_OverrideObjectLightLevel(spr->mobj, &lightlevel); extracolormap_t *colormap = NULL; if (sector->numlights) @@ -1333,7 +1334,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) light = R_GetPlaneLight(sector, spr->mobj->z + spr->mobj->height, false); // Always use the light at the top instead of whatever I was doing before - if (!R_ThingIsFullBright(spr->mobj)) + if (!lightset) lightlevel = *sector->lightlist[light].lightlevel > 255 ? 255 : *sector->lightlist[light].lightlevel; if (*sector->lightlist[light].extra_colormap) @@ -1341,14 +1342,15 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) } else { - if (!R_ThingIsFullBright(spr->mobj)) + if (!lightset) lightlevel = sector->lightlevel > 255 ? 255 : sector->lightlevel; if (sector->extra_colormap) colormap = sector->extra_colormap; } - //lightlevel = 128 + (lightlevel>>1); + if (R_ThingIsSemiBright(spr->mobj)) + lightlevel = 128 + (lightlevel>>1); HWR_Lighting(&Surf, lightlevel, colormap); } From 23466c2a2c9f8ec1e9a9874724025621e5464516 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 20 Jan 2022 03:45:40 -0800 Subject: [PATCH 180/379] Ignore colormap when overriding thing brightness --- src/hardware/hw_main.c | 36 ++++++++++++++++++++---------------- src/hardware/hw_md2.c | 14 +++++++++----- src/r_things.c | 2 +- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 0ab756c9d..62daf2f4f 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -3945,21 +3945,26 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) lightset = HWR_OverrideObjectLightLevel(spr->mobj, &lightlevel); - for (i = 1; i < sector->numlights; i++) + if (!lightset) { - fixed_t h = P_GetLightZAt(§or->lightlist[i], spr->mobj->x, spr->mobj->y); - if (h <= temp) + for (i = 1; i < sector->numlights; i++) { - if (!lightset) + fixed_t h = P_GetLightZAt(§or->lightlist[i], spr->mobj->x, spr->mobj->y); + if (h <= temp) + { lightlevel = *list[i-1].lightlevel > 255 ? 255 : *list[i-1].lightlevel; - if (!(spr->mobj->renderflags & RF_NOCOLORMAPS)) - colormap = *list[i-1].extra_colormap; - break; + if (!(spr->mobj->renderflags & RF_NOCOLORMAPS)) + colormap = *list[i-1].extra_colormap; + break; + } } } if (R_ThingIsSemiBright(spr->mobj)) + { lightlevel = 128 + (lightlevel>>1); + colormap = NULL; + } for (i = 0; i < sector->numlights; i++) { @@ -3967,17 +3972,13 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) return; // even if we aren't changing colormap or lightlevel, we still need to continue drawing down the sprite - if (!(list[i].flags & FF_NOSHADE) && (list[i].flags & FF_CUTSPRITES)) + if (!lightset && !(list[i].flags & FF_NOSHADE) && (list[i].flags & FF_CUTSPRITES)) { - if (!lightset) - { - lightlevel = *list[i].lightlevel > 255 ? 255 : *list[i].lightlevel; + lightlevel = *list[i].lightlevel > 255 ? 255 : *list[i].lightlevel; - if (R_ThingIsSemiBright(spr->mobj)) - lightlevel = 128 + (lightlevel>>1); - } - - if (!(spr->mobj->renderflags & RF_NOCOLORMAPS)) + if (R_ThingIsSemiBright(spr->mobj)) + lightlevel = 128 + (lightlevel>>1); + else if (!(spr->mobj->renderflags & RF_NOCOLORMAPS)) colormap = *list[i].extra_colormap; } @@ -4313,7 +4314,10 @@ static void HWR_DrawSprite(gl_vissprite_t *spr) lightlevel = sector->lightlevel > 255 ? 255 : sector->lightlevel; if (R_ThingIsSemiBright(spr->mobj)) + { lightlevel = 128 + (lightlevel>>1); + colormap = NULL; + } HWR_Lighting(&Surf, lightlevel, colormap); } diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index c2b593f06..7d54af5b9 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -1335,22 +1335,26 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) light = R_GetPlaneLight(sector, spr->mobj->z + spr->mobj->height, false); // Always use the light at the top instead of whatever I was doing before if (!lightset) + { lightlevel = *sector->lightlist[light].lightlevel > 255 ? 255 : *sector->lightlist[light].lightlevel; - if (*sector->lightlist[light].extra_colormap) - colormap = *sector->lightlist[light].extra_colormap; + if (*sector->lightlist[light].extra_colormap) + colormap = *sector->lightlist[light].extra_colormap; + } } - else + else if (!lightset) { - if (!lightset) - lightlevel = sector->lightlevel > 255 ? 255 : sector->lightlevel; + lightlevel = sector->lightlevel > 255 ? 255 : sector->lightlevel; if (sector->extra_colormap) colormap = sector->extra_colormap; } if (R_ThingIsSemiBright(spr->mobj)) + { lightlevel = 128 + (lightlevel>>1); + colormap = NULL; + } HWR_Lighting(&Surf, lightlevel, colormap); } diff --git a/src/r_things.c b/src/r_things.c index 26e66149b..ea09ac826 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -861,7 +861,7 @@ static void R_DrawVisSprite(vissprite_t *vis) else if (vis->mobj->sprite == SPR_PLAY) // Looks like a player, but doesn't have a color? Get rid of green sonic syndrome. R_SetColumnFunc(COLDRAWFUNC_TRANS, false); - if (vis->extra_colormap && !(vis->renderflags & RF_NOCOLORMAPS)) + if (!(vis->cut & (SC_FULLBRIGHT|SC_SEMIBRIGHT)) && vis->extra_colormap && !(vis->renderflags & RF_NOCOLORMAPS)) { if (!dc_colormap) dc_colormap = vis->extra_colormap->colormap; From 4e02bcbc9b7e1b5edf2394eca15a1a864b66c7c3 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 20 Jan 2022 05:09:39 -0800 Subject: [PATCH 181/379] Only ignore colormap for fullbright --- src/hardware/hw_main.c | 38 +++++++++++++++++--------------------- src/hardware/hw_md2.c | 9 ++------- 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 62daf2f4f..2b4cb9fa3 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -3945,26 +3945,21 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) lightset = HWR_OverrideObjectLightLevel(spr->mobj, &lightlevel); - if (!lightset) + for (i = 1; i < sector->numlights; i++) { - for (i = 1; i < sector->numlights; i++) + fixed_t h = P_GetLightZAt(§or->lightlist[i], spr->mobj->x, spr->mobj->y); + if (h <= temp) { - fixed_t h = P_GetLightZAt(§or->lightlist[i], spr->mobj->x, spr->mobj->y); - if (h <= temp) - { + if (!lightset) lightlevel = *list[i-1].lightlevel > 255 ? 255 : *list[i-1].lightlevel; - if (!(spr->mobj->renderflags & RF_NOCOLORMAPS)) - colormap = *list[i-1].extra_colormap; - break; - } + if (!R_ThingIsFullBright(spr->mobj) && !(spr->mobj->renderflags & RF_NOCOLORMAPS)) + colormap = *list[i-1].extra_colormap; + break; } } if (R_ThingIsSemiBright(spr->mobj)) - { lightlevel = 128 + (lightlevel>>1); - colormap = NULL; - } for (i = 0; i < sector->numlights; i++) { @@ -3972,13 +3967,17 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) return; // even if we aren't changing colormap or lightlevel, we still need to continue drawing down the sprite - if (!lightset && !(list[i].flags & FF_NOSHADE) && (list[i].flags & FF_CUTSPRITES)) + if (!(list[i].flags & FF_NOSHADE) && (list[i].flags & FF_CUTSPRITES)) { - lightlevel = *list[i].lightlevel > 255 ? 255 : *list[i].lightlevel; + if (!lightset) + { + lightlevel = *list[i].lightlevel > 255 ? 255 : *list[i].lightlevel; - if (R_ThingIsSemiBright(spr->mobj)) - lightlevel = 128 + (lightlevel>>1); - else if (!(spr->mobj->renderflags & RF_NOCOLORMAPS)) + if (R_ThingIsSemiBright(spr->mobj)) + lightlevel = 128 + (lightlevel>>1); + } + + if (!R_ThingIsFullBright(spr->mobj) && !(spr->mobj->renderflags & RF_NOCOLORMAPS)) colormap = *list[i].extra_colormap; } @@ -4307,17 +4306,14 @@ static void HWR_DrawSprite(gl_vissprite_t *spr) if (!lightset) lightlevel = *sector->lightlist[light].lightlevel > 255 ? 255 : *sector->lightlist[light].lightlevel; - if (*sector->lightlist[light].extra_colormap && !(spr->mobj->renderflags & RF_NOCOLORMAPS)) + if (!R_ThingIsFullBright(spr->mobj) && *sector->lightlist[light].extra_colormap && !(spr->mobj->renderflags & RF_NOCOLORMAPS)) colormap = *sector->lightlist[light].extra_colormap; } else if (!lightset) lightlevel = sector->lightlevel > 255 ? 255 : sector->lightlevel; if (R_ThingIsSemiBright(spr->mobj)) - { lightlevel = 128 + (lightlevel>>1); - colormap = NULL; - } HWR_Lighting(&Surf, lightlevel, colormap); } diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 7d54af5b9..38b151d91 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -1335,12 +1335,10 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) light = R_GetPlaneLight(sector, spr->mobj->z + spr->mobj->height, false); // Always use the light at the top instead of whatever I was doing before if (!lightset) - { lightlevel = *sector->lightlist[light].lightlevel > 255 ? 255 : *sector->lightlist[light].lightlevel; - if (*sector->lightlist[light].extra_colormap) - colormap = *sector->lightlist[light].extra_colormap; - } + if (!R_ThingIsFullBright(spr->mobj) && *sector->lightlist[light].extra_colormap) + colormap = *sector->lightlist[light].extra_colormap; } else if (!lightset) { @@ -1351,10 +1349,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) } if (R_ThingIsSemiBright(spr->mobj)) - { lightlevel = 128 + (lightlevel>>1); - colormap = NULL; - } HWR_Lighting(&Surf, lightlevel, colormap); } From 8f6cebf7efa6dc9fd1f548691b18a27fcc816e38 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 20 Jan 2022 19:14:44 +0000 Subject: [PATCH 182/379] Semibright has colormapping again --- src/r_things.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/r_things.c b/src/r_things.c index ea09ac826..0ca61910b 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -861,7 +861,7 @@ static void R_DrawVisSprite(vissprite_t *vis) else if (vis->mobj->sprite == SPR_PLAY) // Looks like a player, but doesn't have a color? Get rid of green sonic syndrome. R_SetColumnFunc(COLDRAWFUNC_TRANS, false); - if (!(vis->cut & (SC_FULLBRIGHT|SC_SEMIBRIGHT)) && vis->extra_colormap && !(vis->renderflags & RF_NOCOLORMAPS)) + if (vis->extra_colormap && !(vis->cut & SC_FULLBRIGHT) && !(vis->renderflags & RF_NOCOLORMAPS)) { if (!dc_colormap) dc_colormap = vis->extra_colormap->colormap; From 9528243f89e214f5b425cb0b0de58fb387ef702f Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 20 Jan 2022 23:39:58 -0800 Subject: [PATCH 183/379] Few more instances of fullbright colormap nullification --- src/hardware/hw_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 2b4cb9fa3..3c37ce488 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -3937,7 +3937,7 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) // Start with the lightlevel and colormap from the top of the sprite lightlevel = *list[sector->numlights - 1].lightlevel; - if (!(spr->mobj->renderflags & RF_NOCOLORMAPS)) + if (!R_ThingIsFullBright(spr->mobj) && !(spr->mobj->renderflags & RF_NOCOLORMAPS)) colormap = *list[sector->numlights - 1].extra_colormap; i = 0; @@ -4296,7 +4296,7 @@ static void HWR_DrawSprite(gl_vissprite_t *spr) boolean lightset = HWR_OverrideObjectLightLevel(spr->mobj, &lightlevel); extracolormap_t *colormap = NULL; - if (!(spr->mobj->renderflags & RF_NOCOLORMAPS)) + if (!R_ThingIsFullBright(spr->mobj) && !(spr->mobj->renderflags & RF_NOCOLORMAPS)) colormap = sector->extra_colormap; if (splat && sector->numlights) From 3759f88cb3388d9c444403c13254fa8fc54ac553 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 20 Jan 2022 21:51:47 -0800 Subject: [PATCH 184/379] Check both front and backside for tripwire --- src/p_setup.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 15b6c7413..a899265a2 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1940,6 +1940,30 @@ static void P_LoadTextmap(void) } } +static boolean P_CheckLineSideTripWire(line_t *ld, int p) +{ + INT32 n; + + side_t *sda; + side_t *sdb; + + terrain_t *terrain; + + boolean tripwire; + + n = ld->sidenum[p]; + + if (n == 0xffff) + return false; + + sda = &sides[n]; + + terrain = K_GetTerrainForTextureNum(sda->midtexture); + tripwire = terrain && (terrain->flags & TRF_TRIPWIRE); + + return tripwire; +} + static void P_ProcessLinedefsAfterSidedefs(void) { size_t i = numlines; @@ -1947,16 +1971,13 @@ static void P_ProcessLinedefsAfterSidedefs(void) for (; i--; ld++) { - INT32 midtexture = sides[ld->sidenum[0]].midtexture; - terrain_t *terrain = K_GetTerrainForTextureNum(midtexture); - ld->frontsector = sides[ld->sidenum[0]].sector; //e6y: Can't be -1 here ld->backsector = ld->sidenum[1] != 0xffff ? sides[ld->sidenum[1]].sector : 0; - if (terrain != NULL && (terrain->flags & TRF_TRIPWIRE)) - { - ld->tripwire = true; - } + // Check for tripwire on either side + ld->tripwire = + P_CheckLineSideTripWire(ld, 0) || + P_CheckLineSideTripWire(ld, 1); switch (ld->special) { From 6e08e6798698de9b6d16f72dd29a03b9e39956b6 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 20 Jan 2022 21:52:10 -0800 Subject: [PATCH 185/379] Copy tripwire texture to opposite side and automatically mirror texture alignment --- src/p_setup.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/p_setup.c b/src/p_setup.c index a899265a2..969a6e7f9 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1940,6 +1940,24 @@ static void P_LoadTextmap(void) } } +static fixed_t +P_MirrorTextureOffset +( fixed_t offset, + fixed_t source_width, + fixed_t actual_width) +{ + /* + Adjusting the horizontal alignment is a bit ASS... + Textures on the opposite side of the line will begin + drawing from the opposite end. + + Start with the texture width and subtract the seg + length to account for cropping/wrapping. Subtract the + offset to mirror the alignment. + */ + return source_width - actual_width - offset; +} + static boolean P_CheckLineSideTripWire(line_t *ld, int p) { INT32 n; @@ -1961,6 +1979,28 @@ static boolean P_CheckLineSideTripWire(line_t *ld, int p) terrain = K_GetTerrainForTextureNum(sda->midtexture); tripwire = terrain && (terrain->flags & TRF_TRIPWIRE); + if (tripwire) + { + // copy midtexture to other side + n = ld->sidenum[!p]; + + if (n != 0xffff) + { + fixed_t linelength = FixedHypot(ld->dx, ld->dy); + texture_t *tex = textures[sda->midtexture]; + + sdb = &sides[n]; + + sdb->midtexture = sda->midtexture; + sdb->rowoffset = sda->rowoffset; + + // mirror texture alignment + sdb->textureoffset = P_MirrorTextureOffset( + sda->textureoffset, tex->width * FRACUNIT, + linelength); + } + } + return tripwire; } @@ -1974,7 +2014,8 @@ static void P_ProcessLinedefsAfterSidedefs(void) ld->frontsector = sides[ld->sidenum[0]].sector; //e6y: Can't be -1 here ld->backsector = ld->sidenum[1] != 0xffff ? sides[ld->sidenum[1]].sector : 0; - // Check for tripwire on either side + // Check for tripwire, if either side matches then + // copy that (mid)texture to the other side. ld->tripwire = P_CheckLineSideTripWire(ld, 0) || P_CheckLineSideTripWire(ld, 1); From 8d6efa6fe03cef8438607bf6e08aa52bf2a7b386 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 22 Jan 2022 10:01:24 +0100 Subject: [PATCH 186/379] Add MF_NOHITLAGFORME and DMG_WOMBO --- src/deh_tables.c | 2 ++ src/k_kart.c | 2 +- src/p_inter.c | 14 +++++++++++++- src/p_local.h | 3 ++- src/p_mobj.h | 4 +++- 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 3980e1998..122ce3273 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5809,6 +5809,7 @@ const char *const MOBJFLAG_LIST[] = { "DONTENCOREMAP", "PICKUPFROMBELOW", "NOSQUISH", + "NOHITLAGFORME", NULL }; @@ -6464,6 +6465,7 @@ struct int_const_s const INT_CONST[] = { //// Masks {"DMG_STEAL",DMG_CANTHURTSELF}, {"DMG_CANTHURTSELF",DMG_CANTHURTSELF}, + {"DMG_WOMBO", DMG_WOMBO}, {"DMG_DEATHMASK",DMG_DEATHMASK}, {"DMG_TYPEMASK",DMG_TYPEMASK}, diff --git a/src/k_kart.c b/src/k_kart.c index 154be4a91..945f7f7a5 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3153,7 +3153,7 @@ angle_t K_MomentumAngle(mobj_t *mo) void K_AddHitLag(mobj_t *mo, INT32 tics, boolean fromDamage) { - if (mo == NULL || P_MobjWasRemoved(mo)) + if (mo == NULL || P_MobjWasRemoved(mo) || mo->flags & MF_NOHITLAGFORME) { return; } diff --git a/src/p_inter.c b/src/p_inter.c index 8fb55c0ad..55d33c360 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1929,7 +1929,19 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (combo == false) { - if (player->mo->hitlag == 0 && player->flashing > 0) + // Check if we should allow wombo combos (DMG_WOMBO) + boolean allowcombo; + + // For MISSILE OBJECTS, allow combo BY DEFAULT. If DMG_WOMBO is set, do *NOT* allow it. + if (inflictor && !P_MobjWasRemoved(inflictor) && (inflictor->flags & MF_MISSILE)) + allowcombo = !(damagetype & DMG_WOMBO); + + // OTHERWISE, only allow combos IF DMG_WOMBO *IS* set. + else + allowcombo = (damagetype & DMG_WOMBO); + + + if ((player->mo->hitlag == 0 || !allowcombo) && player->flashing > 0) { // Post-hit invincibility K_DoInstashield(player); diff --git a/src/p_local.h b/src/p_local.h index 2cb291b11..1dd631ecb 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -123,7 +123,7 @@ struct demofreecam_s { camera_t *cam; // this is useful when the game is paused, notably mobj_t *soundmobj; // mobj to play sound from, used in s_sound - + angle_t localangle; // keeps track of the cam angle for cmds angle_t localaiming; // ditto with aiming boolean turnheld; // holding turn button for gradual turn speed @@ -488,6 +488,7 @@ typedef struct BasicFF_s // Masks #define DMG_STEAL 0x20 // Flag - can steal bumpers, will only deal damage to players, and will not deal damage outside Battle Mode. #define DMG_CANTHURTSELF 0x40 // Flag - cannot hurt your self or your team +#define DMG_WOMBO 0x80 // Flag - setting this flag allows objects to damage you if you're already in spinout. The effect is reversed on objects with MF_MISSILE (setting it prevents them from comboing in spinout) #define DMG_DEATHMASK DMG_INSTAKILL // if bit 7 is set, this is a death type instead of a damage type #define DMG_TYPEMASK 0x0F // Get type without any flags diff --git a/src/p_mobj.h b/src/p_mobj.h index 53614c5dd..c7ab1f364 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -161,7 +161,9 @@ typedef enum MF_PICKUPFROMBELOW = 1<<29, // Disable momentum-based squash and stretch. MF_NOSQUISH = 1<<30, - // free: to and including 1<<31 + // Disable hitlag for this object + MF_NOHITLAGFORME = 1<<31, + // no more free slots, next up I suppose we can get rid of shit like MF_BOXICON? } mobjflag_t; typedef enum From 5a78994be055a78826f98512b7d8b71b312f6cff Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 22 Jan 2022 11:41:49 +0100 Subject: [PATCH 187/379] Fix a dumb mistake, add DMG_WOMBO to orbinauts, jawz and banana hits --- src/k_collide.c | 4 ++-- src/p_inter.c | 13 ++++++------- src/p_local.h | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/k_collide.c b/src/k_collide.c index 9b0463bfc..92b6c6e47 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -48,7 +48,7 @@ boolean K_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2) else { // Player Damage - P_DamageMobj(t2, t1, t1->target, 1, DMG_WIPEOUT); + P_DamageMobj(t2, t1, t1->target, 1, DMG_WIPEOUT|DMG_WOMBO); K_KartBouncing(t2, t1); S_StartSound(t2, sfx_s3k7b); } @@ -143,7 +143,7 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2) } else { - P_DamageMobj(t2, t1, t1->target, 1, DMG_NORMAL); + P_DamageMobj(t2, t1, t1->target, 1, DMG_NORMAL|DMG_WOMBO); } damageitem = true; diff --git a/src/p_inter.c b/src/p_inter.c index 55d33c360..0b46bb3ad 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1930,18 +1930,17 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (combo == false) { // Check if we should allow wombo combos (DMG_WOMBO) - boolean allowcombo; + boolean allowcombo = false; // For MISSILE OBJECTS, allow combo BY DEFAULT. If DMG_WOMBO is set, do *NOT* allow it. - if (inflictor && !P_MobjWasRemoved(inflictor) && (inflictor->flags & MF_MISSILE)) - allowcombo = !(damagetype & DMG_WOMBO); + if (inflictor && !P_MobjWasRemoved(inflictor) && (inflictor->flags & MF_MISSILE) && !(damagetype & DMG_WOMBO)) + allowcombo = true; // OTHERWISE, only allow combos IF DMG_WOMBO *IS* set. - else - allowcombo = (damagetype & DMG_WOMBO); + else if (damagetype & DMG_WOMBO) + allowcombo = true; - - if ((player->mo->hitlag == 0 || !allowcombo) && player->flashing > 0) + if ((player->mo->hitlag == 0 || allowcombo == false) && player->flashing > 0) { // Post-hit invincibility K_DoInstashield(player); diff --git a/src/p_local.h b/src/p_local.h index 1dd631ecb..a3d5ecd74 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -486,9 +486,9 @@ typedef struct BasicFF_s #define DMG_SPECTATOR 0x83 #define DMG_TIMEOVER 0x84 // Masks +#define DMG_WOMBO 0x10 // Flag - setting this flag allows objects to damage you if you're already in spinout. The effect is reversed on objects with MF_MISSILE (setting it prevents them from comboing in spinout) #define DMG_STEAL 0x20 // Flag - can steal bumpers, will only deal damage to players, and will not deal damage outside Battle Mode. #define DMG_CANTHURTSELF 0x40 // Flag - cannot hurt your self or your team -#define DMG_WOMBO 0x80 // Flag - setting this flag allows objects to damage you if you're already in spinout. The effect is reversed on objects with MF_MISSILE (setting it prevents them from comboing in spinout) #define DMG_DEATHMASK DMG_INSTAKILL // if bit 7 is set, this is a death type instead of a damage type #define DMG_TYPEMASK 0x0F // Get type without any flags From dfbf4b5ec69a4d6fd2860d88a857269c7f3ac50b Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sun, 23 Jan 2022 11:50:53 +0100 Subject: [PATCH 188/379] Add MF_NOHITLAGFORME to spikes and wallspikes --- src/info.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/info.c b/src/info.c index 062d1fbb2..2cd257839 100644 --- a/src/info.c +++ b/src/info.c @@ -9295,7 +9295,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 4, // mass 0, // damage sfx_None, // activesound - MF_NOBLOCKMAP|MF_SCENERY|MF_NOCLIPHEIGHT, // flags + MF_NOBLOCKMAP|MF_SCENERY|MF_NOCLIPHEIGHT|MF_NOHITLAGFORME, // flags S_NULL // raisestate }, @@ -9322,7 +9322,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 4, // mass 0, // damage sfx_None, // activesound - MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SCENERY|MF_NOCLIPHEIGHT|MF_PAPERCOLLISION, // flags + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SCENERY|MF_NOCLIPHEIGHT|MF_PAPERCOLLISION|MF_NOHITLAGFORME, // flags S_NULL // raisestate }, @@ -9349,7 +9349,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 4, // mass 0, // damage sfx_None, // activesound - MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPTHING, // flags + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOHITLAGFORME, // flags S_NULL // raisestate }, From db3420c766815e2c0cdb18206f7dc95dd65fc051 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sun, 23 Jan 2022 13:57:11 +0100 Subject: [PATCH 189/379] MF_NOHITLAGFORME shouldn't work on MT_PLAYER --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 945f7f7a5..99f2b1cac 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3153,7 +3153,7 @@ angle_t K_MomentumAngle(mobj_t *mo) void K_AddHitLag(mobj_t *mo, INT32 tics, boolean fromDamage) { - if (mo == NULL || P_MobjWasRemoved(mo) || mo->flags & MF_NOHITLAGFORME) + if (mo == NULL || P_MobjWasRemoved(mo) || (mo->flags & MF_NOHITLAGFORME && mo->type != MT_PLAYER)) { return; } From ecb8b2bae9e67aeb84c29403c6ce553d1c77df2a Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sun, 23 Jan 2022 17:52:31 +0100 Subject: [PATCH 190/379] Add DMG_WOMBO to all player contact damage --- src/k_collide.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/k_collide.c b/src/k_collide.c index 92b6c6e47..482b67fa3 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -492,12 +492,12 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) if (t1Condition == true && t2Condition == false) { - P_DamageMobj(t2, t1, t1, 1, DMG_TUMBLE); + P_DamageMobj(t2, t1, t1, 1, DMG_TUMBLE|DMG_WOMBO); return true; } else if (t1Condition == false && t2Condition == true) { - P_DamageMobj(t1, t2, t2, 1, DMG_TUMBLE); + P_DamageMobj(t1, t2, t2, 1, DMG_TUMBLE|DMG_WOMBO); return true; } @@ -507,12 +507,12 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) if (t1Condition == true && t2Condition == false) { - P_DamageMobj(t2, t1, t1, 1, DMG_TUMBLE); + P_DamageMobj(t2, t1, t1, 1, DMG_TUMBLE|DMG_WOMBO); return true; } else if (t1Condition == false && t2Condition == true) { - P_DamageMobj(t1, t2, t2, 1, DMG_TUMBLE); + P_DamageMobj(t1, t2, t2, 1, DMG_TUMBLE|DMG_WOMBO); return true; } @@ -522,12 +522,12 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) if (t1Condition == true && t2Condition == false) { - P_DamageMobj(t2, t1, t1, 1, DMG_WIPEOUT); + P_DamageMobj(t2, t1, t1, 1, DMG_WIPEOUT|DMG_WOMBO); return true; } else if (t1Condition == false && t2Condition == true) { - P_DamageMobj(t1, t2, t2, 1, DMG_WIPEOUT); + P_DamageMobj(t1, t2, t2, 1, DMG_WIPEOUT|DMG_WOMBO); return true; } @@ -540,12 +540,12 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) if (t1Condition == true && t2Condition == false) { - P_DamageMobj(t2, t1, t1, 1, DMG_WIPEOUT|DMG_STEAL); + P_DamageMobj(t2, t1, t1, 1, DMG_WIPEOUT|DMG_STEAL|DMG_WOMBO); return true; } else if (t1Condition == false && t2Condition == true) { - P_DamageMobj(t1, t2, t2, 1, DMG_WIPEOUT|DMG_STEAL); + P_DamageMobj(t1, t2, t2, 1, DMG_WIPEOUT|DMG_STEAL|DMG_WOMBO); return true; } } @@ -558,7 +558,7 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) { if (t2->player->rings <= 0) { - P_DamageMobj(t2, t1, t1, 1, DMG_STING); + P_DamageMobj(t2, t1, t1, 1, DMG_STING|DMG_WOMBO); stungT2 = true; } @@ -569,7 +569,7 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) { if (t1->player->rings <= 0) { - P_DamageMobj(t1, t2, t2, 1, DMG_STING); + P_DamageMobj(t1, t2, t2, 1, DMG_STING|DMG_WOMBO); stungT1 = true; } From 13fb905190642d4c5c972d53382b7aa0fab132e8 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sun, 23 Jan 2022 17:58:08 +0100 Subject: [PATCH 191/379] Let ThunderShield combo players --- src/p_user.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index 70fb23a08..e8e36f2ae 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2464,8 +2464,10 @@ void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius) indirectitemcooldown = 0; } - if (mo->flags & MF_BOSS || mo->type == MT_PLAYER) //don't OHKO bosses nor players! + if (mo->flags & MF_BOSS) //don't OHKO bosses nor players! P_DamageMobj(mo, inflictor, source, 1, DMG_NORMAL|DMG_CANTHURTSELF); + else if (mo->type == MT_PLAYER) // Thunder shield: Combo players. + P_DamageMobj(mo, inflictor, source, 1, DMG_NORMAL|DMG_CANTHURTSELF|DMG_WOMBO); else P_DamageMobj(mo, inflictor, source, 1000, DMG_NORMAL|DMG_CANTHURTSELF); } From 37b17baf52828148550ddbe4df537410baa8e4af Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sun, 23 Jan 2022 18:02:56 +0100 Subject: [PATCH 192/379] Add combo to bubble shield --- src/p_map.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_map.c b/src/p_map.c index a0f4e3cfe..a3902b159 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -782,7 +782,7 @@ static boolean PIT_CheckThing(mobj_t *thing) return true; // Player Damage - P_DamageMobj(tmthing, ((thing->type == MT_BUBBLESHIELD) ? thing->target : thing), thing, 1, DMG_NORMAL); + P_DamageMobj(tmthing, ((thing->type == MT_BUBBLESHIELD) ? thing->target : thing), thing, 1, DMG_NORMAL|DMG_WOMBO); S_StartSound(thing, sfx_s3k44); } else From 0dc85f03d16c1b70427c99349b94b2dadfd4cabb Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 23 Jan 2022 21:35:29 +0000 Subject: [PATCH 193/379] Resolve the replay crash issue :mike: (Also no longer mess around with moving flags into booleans per my comment) --- src/g_demo.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/g_demo.c b/src/g_demo.c index 749309828..45f7a0c83 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -2689,7 +2689,7 @@ void G_DoPlayDemo(char *defdemoname) UINT32 randseed; char msg[1024]; - boolean spectator, kickstart, shrinkme; + boolean spectator; UINT8 slots[MAXPLAYERS], kartspeed[MAXPLAYERS], kartweight[MAXPLAYERS], numslots = 0; #if defined(SKIPERRORS) && !defined(DEVELOP) @@ -2962,12 +2962,10 @@ void G_DoPlayDemo(char *defdemoname) { UINT8 flags = READUINT8(demo_p); - spectator = kickstart = shrinkme = false; + spectator = !!(flags & DEMO_SPECTATOR); - if ((spectator = !!(flags & DEMO_SPECTATOR)) == true) + if (spectator == true) { - flags &= ~DEMO_SPECTATOR; - if (modeattacking) { snprintf(msg, 1024, M_GetText("%s is a Record Attack replay with spectators, and is thus invalid.\n"), pdemoname); @@ -2981,12 +2979,6 @@ void G_DoPlayDemo(char *defdemoname) } } - if ((kickstart = !!(flags & DEMO_KICKSTART)) == true) - flags &= ~DEMO_KICKSTART; - - if ((shrinkme = !!(flags & DEMO_SHRINKME)) == true) - flags &= ~DEMO_SHRINKME; - slots[numslots] = p; numslots++; @@ -3008,12 +3000,12 @@ void G_DoPlayDemo(char *defdemoname) playeringame[p] = true; players[p].spectator = spectator; - if (kickstart) + if (flags & DEMO_KICKSTART) players[p].pflags |= PF_KICKSTARTACCEL; else players[p].pflags &= ~PF_KICKSTARTACCEL; - if (shrinkme) + if (flags & DEMO_SHRINKME) players[p].pflags |= PF_SHRINKME; else players[p].pflags &= ~PF_SHRINKME; @@ -3278,7 +3270,10 @@ void G_AddGhost(char *defdemoname) return; } - if ((READUINT8(p) & ~(DEMO_KICKSTART|DEMO_SHRINKME)) != 0) + p++; // player number - doesn't really need to be checked, TODO maybe support adding multiple players' ghosts at once + + // any invalidating flags? + if ((READUINT8(p) & (DEMO_SPECTATOR)) != 0) { CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot.\n"), pdemoname); Z_Free(pdemoname); From ef21be39b9670e8e45794617c1b018fe1c39fed0 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 20 Jan 2022 20:07:52 -0800 Subject: [PATCH 194/379] Enable percentage speedometer by default; do not save --- src/d_netcmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index ac1ca9c69..fce6e7601 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -382,7 +382,7 @@ consvar_t cv_kartencore = CVAR_INIT ("kartencore", "Auto", CV_NETVAR|CV_CALL|CV_ static CV_PossibleValue_t kartvoterulechanges_cons_t[] = {{0, "Never"}, {1, "Sometimes"}, {2, "Frequent"}, {3, "Always"}, {0, NULL}}; consvar_t cv_kartvoterulechanges = CVAR_INIT ("kartvoterulechanges", "Frequent", CV_NETVAR, kartvoterulechanges_cons_t, NULL); static CV_PossibleValue_t kartspeedometer_cons_t[] = {{0, "Off"}, {1, "Percentage"}, {2, "Kilometers"}, {3, "Miles"}, {4, "Fracunits"}, {0, NULL}}; -consvar_t cv_kartspeedometer = CVAR_INIT ("kartdisplayspeed", "Off", CV_SAVE, kartspeedometer_cons_t, NULL); // use tics in display +consvar_t cv_kartspeedometer = CVAR_INIT ("kartdisplayspeed", "Percentage", 0, kartspeedometer_cons_t, NULL); // use tics in display static CV_PossibleValue_t kartvoices_cons_t[] = {{0, "Never"}, {1, "Tasteful"}, {2, "Meme"}, {0, NULL}}; consvar_t cv_kartvoices = CVAR_INIT ("kartvoices", "Tasteful", CV_SAVE, kartvoices_cons_t, NULL); From f0587ac38930727bb50c2ceb77b576fa2c1d6110 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 23 Jan 2022 20:49:29 +0000 Subject: [PATCH 195/379] Readd CV_SAVE per Oni request in vc (and also my desires) Made using web IDE --- src/d_netcmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index fce6e7601..49cb1f3a2 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -382,7 +382,7 @@ consvar_t cv_kartencore = CVAR_INIT ("kartencore", "Auto", CV_NETVAR|CV_CALL|CV_ static CV_PossibleValue_t kartvoterulechanges_cons_t[] = {{0, "Never"}, {1, "Sometimes"}, {2, "Frequent"}, {3, "Always"}, {0, NULL}}; consvar_t cv_kartvoterulechanges = CVAR_INIT ("kartvoterulechanges", "Frequent", CV_NETVAR, kartvoterulechanges_cons_t, NULL); static CV_PossibleValue_t kartspeedometer_cons_t[] = {{0, "Off"}, {1, "Percentage"}, {2, "Kilometers"}, {3, "Miles"}, {4, "Fracunits"}, {0, NULL}}; -consvar_t cv_kartspeedometer = CVAR_INIT ("kartdisplayspeed", "Percentage", 0, kartspeedometer_cons_t, NULL); // use tics in display +consvar_t cv_kartspeedometer = CVAR_INIT ("kartdisplayspeed", "Percentage", CV_SAVE, kartspeedometer_cons_t, NULL); // use tics in display static CV_PossibleValue_t kartvoices_cons_t[] = {{0, "Never"}, {1, "Tasteful"}, {2, "Meme"}, {0, NULL}}; consvar_t cv_kartvoices = CVAR_INIT ("kartvoices", "Tasteful", CV_SAVE, kartvoices_cons_t, NULL); From 71b6b816dafd4b30ce2ad43fba5acc1b44b5f8c7 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 2 Jan 2022 21:58:18 -0800 Subject: [PATCH 196/379] Disable player admins --- src/d_netcmd.c | 14 -------------- src/d_netcmd.h | 2 +- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 49cb1f3a2..d3ee8e1b9 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3571,20 +3571,6 @@ static void Command_Login_f(void) #endif } -boolean IsPlayerAdmin(INT32 playernum) -{ -#ifdef DEVELOP - return playernum != serverplayer; -#else - INT32 i; - for (i = 0; i < MAXPLAYERS; i++) - if (playernum == adminplayers[i]) - return true; - - return false; -#endif -} - void SetAdminPlayer(INT32 playernum) { INT32 i; diff --git a/src/d_netcmd.h b/src/d_netcmd.h index ba3c68452..600fa6ebe 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -219,7 +219,7 @@ void D_SetupVote(void); void D_ModifyClientVote(UINT8 player, SINT8 voted, UINT8 splitplayer); void D_PickVote(void); void ObjectPlace_OnChange(void); -boolean IsPlayerAdmin(INT32 playernum); +#define IsPlayerAdmin(playernum) (0) void SetAdminPlayer(INT32 playernum); void ClearAdminPlayers(void); void RemoveAdminPlayer(INT32 playernum); From 3e7cde8abd326602024d677f5e11076601a00756 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 2 Jan 2022 22:08:18 -0800 Subject: [PATCH 197/379] Load misc build testers data files --- src/d_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 91d13dd75..c81e15081 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1073,9 +1073,9 @@ static void IdentifyVersion(void) // checking in D_SRB2Main D_AddFile(startupiwads, va(pandf,srb2waddir,"gfx.pk3")); - D_AddFile(startupiwads, va(pandf,srb2waddir,"textures.pk3")); + D_AddFile(startupiwads, va(pandf,srb2waddir,"MISC_TEXTURES.pk3")); D_AddFile(startupiwads, va(pandf,srb2waddir,"chars.pk3")); - D_AddFile(startupiwads, va(pandf,srb2waddir,"maps.pk3")); + D_AddFile(startupiwads, va(pandf,srb2waddir,"MISC_MAPS.pk3")); D_AddFile(startupiwads, va(pandf,srb2waddir,"followers.pk3")); #ifdef USE_PATCH_FILE D_AddFile(startupiwads, va(pandf,srb2waddir,"patch.pk3")); @@ -1097,7 +1097,7 @@ static void IdentifyVersion(void) } MUSICTEST("sounds.pk3") - MUSICTEST("music.pk3") + MUSICTEST("MISC_MUSIC.pk3") #undef MUSICTEST From 1ea1ab8c4df4536918c5e1a522af8846eb629601 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 17 Jan 2022 01:12:30 +0000 Subject: [PATCH 198/379] TESTERS stuff --- src/d_main.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index c81e15081..a67493837 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1072,17 +1072,34 @@ static void IdentifyVersion(void) // if you change the ordering of this or add/remove a file, be sure to update the md5 // checking in D_SRB2Main +#if defined (TESTERS) || defined (HOSTTESTERS) +//// +#define TEXTURESNAME "MISC_TEXTURES.pk3" +#define MAPSNAME "MISC_MAPS.pk3" +#define PATCHNAME "MISC_PATCH.pk3" +//// +#else +//// +#define TEXTURESNAME "textures.pk3" +#define MAPSNAME "maps.pk3" +#define PATCHNAME "patch.pk3" +//// +#endif +//// D_AddFile(startupiwads, va(pandf,srb2waddir,"gfx.pk3")); - D_AddFile(startupiwads, va(pandf,srb2waddir,"MISC_TEXTURES.pk3")); + D_AddFile(startupiwads, va(pandf,srb2waddir,TEXTURESNAME)); D_AddFile(startupiwads, va(pandf,srb2waddir,"chars.pk3")); - D_AddFile(startupiwads, va(pandf,srb2waddir,"MISC_MAPS.pk3")); + D_AddFile(startupiwads, va(pandf,srb2waddir,MAPSNAME)); D_AddFile(startupiwads, va(pandf,srb2waddir,"followers.pk3")); #ifdef USE_PATCH_FILE - D_AddFile(startupiwads, va(pandf,srb2waddir,"patch.pk3")); - + D_AddFile(startupiwads, va(pandf,srb2waddir,PATCHNAME)); // SPECIFIC HACK TO NEW-MENUS SO THAT MY DUMBASS STOPS FORGETTING TO ADD THE FILE (rip :youfuckedup:) - D_AddFile(startupiwads, va(pandf,srb2waddir,"newmenus.pk3")); + D_AddFile(startupiwads, va(pandf,srb2waddir,"newmenus.pk3")); #endif +//// +#undef TEXTURESNAME +#undef MAPSNAME +#undef PATCHNAME #if !defined (HAVE_SDL) || defined (HAVE_MIXER) From 00484b4d7c0da40768372539220aca5888a8ece8 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 17 Jan 2022 01:19:13 +0000 Subject: [PATCH 199/379] Revert "Disable player admins" This reverts commit 4d2793918c5aa8e9aec5529db33b13ddcd153a04. --- src/d_netcmd.c | 14 ++++++++++++++ src/d_netcmd.h | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index d3ee8e1b9..49cb1f3a2 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3571,6 +3571,20 @@ static void Command_Login_f(void) #endif } +boolean IsPlayerAdmin(INT32 playernum) +{ +#ifdef DEVELOP + return playernum != serverplayer; +#else + INT32 i; + for (i = 0; i < MAXPLAYERS; i++) + if (playernum == adminplayers[i]) + return true; + + return false; +#endif +} + void SetAdminPlayer(INT32 playernum) { INT32 i; diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 600fa6ebe..ba3c68452 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -219,7 +219,7 @@ void D_SetupVote(void); void D_ModifyClientVote(UINT8 player, SINT8 voted, UINT8 splitplayer); void D_PickVote(void); void ObjectPlace_OnChange(void); -#define IsPlayerAdmin(playernum) (0) +boolean IsPlayerAdmin(INT32 playernum); void SetAdminPlayer(INT32 playernum); void ClearAdminPlayers(void); void RemoveAdminPlayer(INT32 playernum); From cac833e324567ad6dd0ea8ca11c616790714b8aa Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 17 Jan 2022 01:22:10 +0000 Subject: [PATCH 200/379] Guard IsPlayerAdmin in testers/hosttesters --- src/d_netcmd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 49cb1f3a2..39583cebe 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3573,7 +3573,9 @@ static void Command_Login_f(void) boolean IsPlayerAdmin(INT32 playernum) { -#ifdef DEVELOP +#if defined (TESTERS) || defined (HOSTTESTERS) + return false; +#elif defined (DEVELOP) return playernum != serverplayer; #else INT32 i; From 6518d73d51624fec496a5b9637a80dbb181758e0 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 17 Jan 2022 01:23:14 +0000 Subject: [PATCH 201/379] Music also --- src/d_main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/d_main.c b/src/d_main.c index a67493837..89eb4f7d9 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1077,12 +1077,14 @@ static void IdentifyVersion(void) #define TEXTURESNAME "MISC_TEXTURES.pk3" #define MAPSNAME "MISC_MAPS.pk3" #define PATCHNAME "MISC_PATCH.pk3" +#define MUSICNAME "MISC_MUSIC.PK3" //// #else //// #define TEXTURESNAME "textures.pk3" #define MAPSNAME "maps.pk3" #define PATCHNAME "patch.pk3" +#define MUSICNAME "music.pk3" //// #endif //// @@ -1114,8 +1116,9 @@ static void IdentifyVersion(void) } MUSICTEST("sounds.pk3") - MUSICTEST("MISC_MUSIC.pk3") + MUSICTEST(MUSICNAME) +#undef MUSICNAME #undef MUSICTEST #endif From a684b06f807ae84c24cb6a6178b9bf25ef47a319 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 16 Jan 2022 17:32:58 -0800 Subject: [PATCH 202/379] Do not add gfx.pk3 in testers build MISC_PATCH.pk3 covers it. --- src/d_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/d_main.c b/src/d_main.c index 89eb4f7d9..bd8e0febf 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1088,7 +1088,9 @@ static void IdentifyVersion(void) //// #endif //// +#if !defined (TESTERS) && !defined (HOSTTESTERS) D_AddFile(startupiwads, va(pandf,srb2waddir,"gfx.pk3")); +#endif D_AddFile(startupiwads, va(pandf,srb2waddir,TEXTURESNAME)); D_AddFile(startupiwads, va(pandf,srb2waddir,"chars.pk3")); D_AddFile(startupiwads, va(pandf,srb2waddir,MAPSNAME)); From c50133fb473786113564262f9e2312821c5bf8b3 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 16 Jan 2022 17:34:12 -0800 Subject: [PATCH 203/379] Add HOSTTESTERS make flag --- src/Makefile.d/features.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.d/features.mk b/src/Makefile.d/features.mk index a29a8a153..c9a81fcfa 100644 --- a/src/Makefile.d/features.mk +++ b/src/Makefile.d/features.mk @@ -6,7 +6,7 @@ passthru_opts+=\ NONET NO_IPV6 NOHW NOMD5 NOPOSTPROCESSING\ MOBJCONSISTANCY PACKETDROP ZDEBUG\ HAVE_MINIUPNPC\ - HAVE_DISCORDRPC TESTERS DEVELOP + HAVE_DISCORDRPC TESTERS HOSTTESTERS DEVELOP # build with debugging information ifdef DEBUGMODE From 5d5e71beda0a18a6dd296aaa6f569bfe684678eb Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 16 Jan 2022 17:35:47 -0800 Subject: [PATCH 204/379] Fix compiler warning --- src/d_netcmd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 39583cebe..ce74dec96 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3574,6 +3574,7 @@ static void Command_Login_f(void) boolean IsPlayerAdmin(INT32 playernum) { #if defined (TESTERS) || defined (HOSTTESTERS) + (void)playernum; return false; #elif defined (DEVELOP) return playernum != serverplayer; From 1f40c48bea046636d9c3907b14dae39a7777bae8 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 19 Jan 2022 01:13:09 -0800 Subject: [PATCH 205/379] Do not count gfx.pk3 toward mainwads in testers build --- src/d_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/d_main.c b/src/d_main.c index bd8e0febf..2eb8b100b 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1351,7 +1351,9 @@ void D_SRB2Main(void) mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_PATCH_PK3); // patch.pk3 #endif #else +#if !defined (TESTERS) || !defined (HOSTTESTERS) mainwads++; // gfx.pk3 +#endif mainwads++; // textures.pk3 mainwads++; // chars.pk3 mainwads++; // maps.pk3 From 90eeff00afbf8f0488f863269758e37c5b9b1220 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 19 Jan 2022 01:17:12 -0800 Subject: [PATCH 206/379] JUST SEARCH ALL FUCKING WADS --- src/d_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_main.c b/src/d_main.c index 2eb8b100b..f5e02ad5b 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1369,7 +1369,7 @@ void D_SRB2Main(void) // // search for maps // - for (wadnum = 4; wadnum < 6; wadnum++) // fucking arbitrary numbers + for (wadnum = 0; wadnum <= mainwads; wadnum++) { lumpinfo = wadfiles[wadnum]->lumpinfo; for (i = 0; i < wadfiles[wadnum]->numlumps; i++, lumpinfo++) From 00a471405db6b09b7db0e6be2c0c3d7fa3ec52d9 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 19 Jan 2022 01:17:54 -0800 Subject: [PATCH 207/379] Wrong condition --- src/d_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_main.c b/src/d_main.c index f5e02ad5b..69e4887e5 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1351,7 +1351,7 @@ void D_SRB2Main(void) mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_PATCH_PK3); // patch.pk3 #endif #else -#if !defined (TESTERS) || !defined (HOSTTESTERS) +#if !defined (TESTERS) && !defined (HOSTTESTERS) mainwads++; // gfx.pk3 #endif mainwads++; // textures.pk3 From 1789dd1255805ba0658b98d38075eb71264d0b76 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Mon, 14 Feb 2022 23:14:25 +0100 Subject: [PATCH 208/379] Rudimentary start on profiles --- src/Sourcefile | 1 + src/d_main.c | 4 ++ src/d_main.h | 1 + src/d_netcmd.c | 7 ---- src/k_profiles.c | 90 +++++++++++++++++++++++++++++++++++++++++++++ src/k_profiles.h | 96 ++++++++++++++++++++++++++++++++++++++++++++++++ src/v_video.c | 13 +++++++ src/v_video.h | 3 ++ 8 files changed, 208 insertions(+), 7 deletions(-) create mode 100644 src/k_profiles.c create mode 100644 src/k_profiles.h diff --git a/src/Sourcefile b/src/Sourcefile index 8c3bff8d1..2d554c71c 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -116,3 +116,4 @@ k_menudraw.c k_brightmap.c k_terrain.c k_director.c +k_profiles.c diff --git a/src/d_main.c b/src/d_main.c index 69e4887e5..e80d38b7b 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1479,6 +1479,10 @@ void D_SRB2Main(void) //--------------------------------------------------------- CONFIG.CFG M_FirstLoadConfig(); // WARNING : this do a "COM_BufExecute()" + // Load Profiles now that default controls have been defined + PR_LoadProfiles(); // load control profiles + PR_SaveProfiles(); // Test @TODO: remove this lol + M_Init(); #if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL) diff --git a/src/d_main.h b/src/d_main.h index 81de0634d..f01f9227f 100644 --- a/src/d_main.h +++ b/src/d_main.h @@ -14,6 +14,7 @@ #ifndef __D_MAIN__ #define __D_MAIN__ +#include "k_profiles.h" // PR_LoadProfiles() #include "d_event.h" #include "w_wad.h" // for MAX_WADFILES diff --git a/src/d_netcmd.c b/src/d_netcmd.c index ce74dec96..f79cd050f 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -935,13 +935,6 @@ void D_RegisterClientCommands(void) { CV_RegisterVar(&cv_kickstartaccel[i]); CV_RegisterVar(&cv_shrinkme[i]); - CV_RegisterVar(&cv_turnaxis[i]); - CV_RegisterVar(&cv_moveaxis[i]); - CV_RegisterVar(&cv_brakeaxis[i]); - CV_RegisterVar(&cv_aimaxis[i]); - CV_RegisterVar(&cv_lookaxis[i]); - CV_RegisterVar(&cv_fireaxis[i]); - CV_RegisterVar(&cv_driftaxis[i]); CV_RegisterVar(&cv_deadzone[i]); } diff --git a/src/k_profiles.c b/src/k_profiles.c new file mode 100644 index 000000000..980827765 --- /dev/null +++ b/src/k_profiles.c @@ -0,0 +1,90 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2020 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 k_profiles.c +/// \brief implements methods for profiles etc. + +#include "k_profiles.h" + +// List of all the profiles. +static profile_t profilesList[MAXPROFILES+1]; // +1 because we're gonna add a default "GUEST' profile. +static UINT8 numprofiles = 0; // # of loaded profiles + +profile_t PR_MakeProfile(const char *prname, const char *pname, const UINT16 col, const char *fname, UINT16 fcol, INT32 controlarray[num_gamecontrols][MAXINPUTMAPPING]) +{ + profile_t new; + + new.version = PROFILEVER; + + strcpy(new.profilename, prname); + + strcpy(new.playername, pname); + new.color = col; + + strcpy(new.follower, fname); + new.followercolor = fcol; + + // Copy from gamecontrol directly as we'll be setting controls up directly in the profile. + memcpy(new.controls, controlarray, sizeof(new.controls)); + + return new; +} + +profile_t PR_MakeProfileFromPlayer(const char *prname, const char *pname, const UINT16 col, const char *fname, UINT16 fcol, UINT8 pnum) +{ + // Generate profile using the player's gamecontrol, as we set them directly when making profiles from menus. + profile_t new = PR_MakeProfile(prname, pname, col, fname, fcol, gamecontrol[pnum]); + + // Player bound cvars: + new.kickstartaccel = cv_kickstartaccel[pnum].value; + + return new; +} + +boolean PR_AddProfile(profile_t p) +{ + if (numprofiles < MAXPROFILES) + { + memcpy(&profilesList[numprofiles], &p, sizeof(profile_t)); + numprofiles++; + + CONS_Printf("Profile '%s' added\n", p.profilename); + + return true; + } + else + return false; +} + +void PR_SaveProfiles(void) +{ + FILE *f = NULL; + + f = fopen(PROFILESFILE, "w"); + if (f != NULL) + { + fwrite(profilesList, sizeof(profile_t), MAXPROFILES, f); + fclose(f); + } +} + +void PR_LoadProfiles(void) +{ + //FILE *f = NULL; + profile_t dprofile = PR_MakeProfile(PROFILEDEFAULTNAME, PROFILEDEFAULTPNAME, PROFILEDEFAULTCOLOR, PROFILEDEFAULTFOLLOWER, PROFILEDEFAULTFOLLOWERCOLOR, gamecontroldefault); + PR_AddProfile(dprofile); + + /*f = fopen(PROFILESFILE, "r"); + + if (f != NULL) + { + fread(&profilesList[1], sizeof(profile_t)*(MAXPROFILES), MAXPROFILES, f); + fclose(f); + }*/ +} \ No newline at end of file diff --git a/src/k_profiles.h b/src/k_profiles.h new file mode 100644 index 000000000..ccd8bd2fb --- /dev/null +++ b/src/k_profiles.h @@ -0,0 +1,96 @@ +// 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 k_profiles.h +/// \brief Control profiles definition + +#ifndef __PROFILES_H__ +#define __PROFILES_H__ + +#include "doomdef.h" // MAXPLAYERNAME +//#include "r_skins.h" // SKINNAMESIZE // This cuases stupid issues. +#include "g_input.h" // Input related stuff +#include "string.h" // strcpy etc +#include "g_game.h" // game CVs + +// We have to redefine this because somehow including r_skins.h causes a redefinition of node_t since that's used for both net nodes and BSP nodes too...... +// And honestly I don't wanna refactor that. +#define SKINNAMESIZE 16 + +#define PROFILENAMELEN 6 +#define PROFILEVER 0 +#define MAXPROFILES 16 +#define PROFILESFILE "kartprofiles.cfg" + +#define PROFILEDEFAULTNAME "GUEST" +#define PROFILEDEFAULTPNAME "Player" +#define PROFILEDEFAULTSKIN "sonic" +#define PROFILEDEFAULTCOLOR SKINCOLOR_SAPPHIRE +#define PROFILEDEFAULTFOLLOWER "none" +#define PROFILEDEFAULTFOLLOWERCOLOR 255 + +// Man I wish I had more than 16 friends!! + +// profile_t definition (WIP) +typedef struct profile_s +{ + + // Versionning + UINT8 version; // Version of the profile, this can be useful for backwards compatibility reading if we ever update the profile structure/format after release. + + // Profile header + char profilename[PROFILENAMELEN+1]; // Profile name (not to be confused with player name) + + // Player data + char playername[MAXPLAYERNAME+1]; // Player name + UINT16 color; // Default player coloUr. ...But for consistency we'll name it color. + char follower[SKINNAMESIZE+1]; // Follower + UINT16 followercolor; // Follower color + + // Player-specific consvars. + // @TODO: List all of those + boolean kickstartaccel; // cv_kickstartaccel + + // Finally, control data itself + INT32 controls[num_gamecontrols][MAXINPUTMAPPING]; // Lists of all the controls, defined the same way as default inputs in g_input.c +} profile_t; + + +// Functions + +// PR_MakeProfile +// Makes a profile from the supplied profile name, player name, colour, follower, followercolour and controls. +// The consvar values are left untouched. +profile_t PR_MakeProfile(const char *prname, const char *pname, const UINT16 col, const char *fname, UINT16 fcol, INT32 controlarray[num_gamecontrols][MAXINPUTMAPPING]); + +// PR_MakeProfileFromPlayer +// Makes a profile_t from the supplied profile name, player name, colour, follower and followercolour. +// The last argument is a player number to read cvars from; as for convenience, cvars will be set directly when making a profile (since loading another one will overwrite them, this will be inconsequential) +profile_t PR_MakeProfileFromPlayer(const char *prname, const char *pname, const UINT16 col, const char *fname, UINT16 fcol, UINT8 pnum); + +// PR_AddProfile(profile_t p) +// Adds a profile to profilesList and increments numprofiles. +// Returns true if succesful, false if not. +boolean PR_AddProfile(profile_t p); + +// PR_SaveProfiles(void) +// Saves all the profiles in profiles.cfg +// This does not save profilesList[0] since that's always going to be the default profile. +void PR_SaveProfiles(void); + +// PR_LoadProfiles(void) +// Loads all the profiles saved in profiles.cfg. +// This also loads +void PR_LoadProfiles(void); + + + +#endif \ No newline at end of file diff --git a/src/v_video.c b/src/v_video.c index bb06228e0..d41f316cd 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -2626,6 +2626,19 @@ void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits) } while (--digits); } +void V_DrawCenteredThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string) +{ + x -= (V_ThinStringWidth(string, option) / 2) * FRACUNIT; + V_DrawThinStringAtFixed(x, y, option, string); +} + +void V_DrawRightAlignedThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string) +{ + x -= V_ThinStringWidth(string, option) * FRACUNIT; + V_DrawThinStringAtFixed(x, y, option, string); +} + + // Draws a number using the PING font thingy. // TODO: Merge number drawing functions into one with "font name" selection. diff --git a/src/v_video.h b/src/v_video.h index d0da576db..b16ac235a 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -291,6 +291,9 @@ void V_DrawRightAlignedThinString(INT32 x, INT32 y, INT32 option, const char *st #define V_DrawThinStringAtFixed( x,y,option,string ) \ V__DrawOneScaleString (x,y,FRACUNIT,option,NULL,TINY_FONT,string) +void V_DrawCenteredThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string); +void V_DrawRightAlignedThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string); + // Draws a titlecard font string. // timer: when the letters start appearing (leave to 0 to disable) From 8c53108d174f7150c406a33f5893e71f2c0dc777 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Tue, 15 Feb 2022 01:28:57 +0100 Subject: [PATCH 209/379] Start on profiles menu (does nothing and softlocks) --- src/k_menu.h | 9 ++++++++- src/k_menudef.c | 26 ++++++++++++++++++++++++-- src/k_menudraw.c | 34 ++++++++++++++++++++++++++++++++++ src/k_menufunc.c | 4 ++++ src/k_profiles.c | 38 +++++++++++++++++++++++--------------- src/k_profiles.h | 19 ++++++++++++------- 6 files changed, 105 insertions(+), 25 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 9fb09a450..b4a69c861 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -19,6 +19,7 @@ #include "command.h" #include "doomstat.h" // MAXSPLITSCREENPLAYERS #include "g_demo.h" //menudemo_t +#include "k_profiles.h" // profile data & functions // flags for items in the menu // menu handle (what we do when key is pressed @@ -219,6 +220,9 @@ typedef enum mopt_manual, } mopt_e; +extern menuitem_t OPTIONS_Profiles[]; +extern menu_t OPTIONS_ProfilesDef; + extern menuitem_t OPTIONS_Video[]; extern menu_t OPTIONS_VideoDef; @@ -619,8 +623,10 @@ void M_OptionsChangeBGColour(INT16 newcolour); // changes the background colour void M_HandleItemToggles(INT32 choice); // For item toggling void M_EraseData(INT32 choice); // For data erasing -// video modes menu (resolution) +// profile selection menu +void M_HandleProfileSelect(INT32 ch); +// video modes menu (resolution) void M_VideoModeMenu(INT32 choice); void M_HandleVideoModes(INT32 ch); @@ -758,6 +764,7 @@ void M_DrawPlaybackMenu(void); void M_DrawOptionsMovingButton(void); // for sick transitions... void M_DrawOptions(void); void M_DrawGenericOptions(void); +void M_DrawProfileSelect(void); void M_DrawVideoModes(void); void M_DrawItemToggles(void); diff --git a/src/k_menudef.c b/src/k_menudef.c index aeee90865..c75354eb3 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -367,8 +367,8 @@ menu_t PLAY_MP_RoomSelectDef = { menuitem_t OPTIONS_Main[] = { - {IT_STRING | IT_TRANSTEXT, "Profile Setup", "Remap keys & buttons to your likings.", - NULL, NULL, 0, 0}, + {IT_STRING | IT_SUBMENU, "Profile Setup", "Remap keys & buttons to your likings.", + NULL, &OPTIONS_ProfilesDef, 0, 0}, {IT_STRING | IT_SUBMENU, "Video Options", "Change video settings such as the resolution.", NULL, &OPTIONS_VideoDef, 0, 0}, @@ -408,6 +408,28 @@ menu_t OPTIONS_MainDef = { M_OptionsInputs }; +// profiles menu +// profile select +menuitem_t OPTIONS_Profiles[] = { + {IT_KEYHANDLER | IT_NOTHING, NULL, "Select a Profile.", + NULL, M_HandleProfileSelect, 0, 0}, // dummy menuitem for the control func +}; + +menu_t OPTIONS_ProfilesDef = { + sizeof (OPTIONS_Profiles) / sizeof (menuitem_t), + &OPTIONS_MainDef, + 0, + OPTIONS_Profiles, + 32, 80, + SKINCOLOR_ULTRAMARINE, 0, + 2, 10, + M_DrawProfileSelect, + M_OptionsTick, + NULL, + NULL, + NULL, +}; + // video options menu... // options menu menuitem_t OPTIONS_Video[] = diff --git a/src/k_menudraw.c b/src/k_menudraw.c index eb91a2c7b..29cc158ee 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2011,6 +2011,40 @@ void M_DrawGenericOptions(void) } } +// Draws profile selection +void M_DrawProfileSelect(void) +{ + INT32 i; + patch_t *card = W_CachePatchName("PR_CARD", PU_CACHE); + + INT32 x = 160; + INT32 y = 75 + menutransition.tics*16; + + M_DrawOptionsCogs(); + M_DrawMenuTooltips(); + M_DrawOptionsMovingButton(); + + for (i=0; i < MAXPROFILES; i++) + { + profile_t *p = PR_GetProfile(i); + UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_BLACK, GTC_CACHE); + char pname[PROFILENAMELEN+1] = "EMPTY"; + + if (p != NULL) + { + colormap = R_GetTranslationColormap(TC_DEFAULT, p->color, GTC_CACHE); + strcpy(pname, p->profilename); + } + + V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, card, colormap); + V_DrawCenteredGamemodeString(x, y+18, 0, 0, pname); + + CONS_Printf("pname %s\n", pname); + + x += 96; + } +} + // Draw the video modes list, a-la-Quake void M_DrawVideoModes(void) { diff --git a/src/k_menufunc.c b/src/k_menufunc.c index f422aad5d..05b980146 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3264,6 +3264,10 @@ void M_VideoModeMenu(INT32 choice) M_SetupNextMenu(&OPTIONS_VideoModesDef, false); } +void M_HandleProfileSelect(INT32 ch) +{ + (void) ch; +} // special menuitem key handler for video mode list void M_HandleVideoModes(INT32 ch) diff --git a/src/k_profiles.c b/src/k_profiles.c index 980827765..979c7da83 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -19,20 +19,20 @@ static UINT8 numprofiles = 0; // # of loaded profiles profile_t PR_MakeProfile(const char *prname, const char *pname, const UINT16 col, const char *fname, UINT16 fcol, INT32 controlarray[num_gamecontrols][MAXINPUTMAPPING]) { profile_t new; - + new.version = PROFILEVER; - + strcpy(new.profilename, prname); - + strcpy(new.playername, pname); new.color = col; - + strcpy(new.follower, fname); new.followercolor = fcol; - + // Copy from gamecontrol directly as we'll be setting controls up directly in the profile. - memcpy(new.controls, controlarray, sizeof(new.controls)); - + memcpy(new.controls, controlarray, sizeof(new.controls)); + return new; } @@ -40,10 +40,10 @@ profile_t PR_MakeProfileFromPlayer(const char *prname, const char *pname, const { // Generate profile using the player's gamecontrol, as we set them directly when making profiles from menus. profile_t new = PR_MakeProfile(prname, pname, col, fname, fcol, gamecontrol[pnum]); - + // Player bound cvars: new.kickstartaccel = cv_kickstartaccel[pnum].value; - + return new; } @@ -53,35 +53,43 @@ boolean PR_AddProfile(profile_t p) { memcpy(&profilesList[numprofiles], &p, sizeof(profile_t)); numprofiles++; - + CONS_Printf("Profile '%s' added\n", p.profilename); - + return true; } else return false; } +profile_t* PR_GetProfile(INT32 num) +{ + if (num < numprofiles) + return &profilesList[num]; + else + return NULL; +} + void PR_SaveProfiles(void) { FILE *f = NULL; - + f = fopen(PROFILESFILE, "w"); if (f != NULL) { fwrite(profilesList, sizeof(profile_t), MAXPROFILES, f); fclose(f); } -} +} void PR_LoadProfiles(void) { //FILE *f = NULL; profile_t dprofile = PR_MakeProfile(PROFILEDEFAULTNAME, PROFILEDEFAULTPNAME, PROFILEDEFAULTCOLOR, PROFILEDEFAULTFOLLOWER, PROFILEDEFAULTFOLLOWERCOLOR, gamecontroldefault); PR_AddProfile(dprofile); - + /*f = fopen(PROFILESFILE, "r"); - + if (f != NULL) { fread(&profilesList[1], sizeof(profile_t)*(MAXPROFILES), MAXPROFILES, f); diff --git a/src/k_profiles.h b/src/k_profiles.h index ccd8bd2fb..7611fa43f 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -26,7 +26,7 @@ #define SKINNAMESIZE 16 #define PROFILENAMELEN 6 -#define PROFILEVER 0 +#define PROFILEVER 1 #define MAXPROFILES 16 #define PROFILESFILE "kartprofiles.cfg" @@ -40,25 +40,26 @@ // Man I wish I had more than 16 friends!! // profile_t definition (WIP) -typedef struct profile_s +typedef struct profile_s { - + // Versionning UINT8 version; // Version of the profile, this can be useful for backwards compatibility reading if we ever update the profile structure/format after release. + // A version of 0 can easily be checked to identify an unitialized profile. // Profile header char profilename[PROFILENAMELEN+1]; // Profile name (not to be confused with player name) - + // Player data char playername[MAXPLAYERNAME+1]; // Player name UINT16 color; // Default player coloUr. ...But for consistency we'll name it color. char follower[SKINNAMESIZE+1]; // Follower UINT16 followercolor; // Follower color - + // Player-specific consvars. // @TODO: List all of those boolean kickstartaccel; // cv_kickstartaccel - + // Finally, control data itself INT32 controls[num_gamecontrols][MAXINPUTMAPPING]; // Lists of all the controls, defined the same way as default inputs in g_input.c } profile_t; @@ -81,6 +82,10 @@ profile_t PR_MakeProfileFromPlayer(const char *prname, const char *pname, const // Returns true if succesful, false if not. boolean PR_AddProfile(profile_t p); +// PR_GetProfile(INT32 num) +// Returns a pointer to the profile you're asking for or NULL if the profile is uninitialized. +profile_t* PR_GetProfile(INT32 num); + // PR_SaveProfiles(void) // Saves all the profiles in profiles.cfg // This does not save profilesList[0] since that's always going to be the default profile. @@ -88,7 +93,7 @@ void PR_SaveProfiles(void); // PR_LoadProfiles(void) // Loads all the profiles saved in profiles.cfg. -// This also loads +// This also loads void PR_LoadProfiles(void); From aea066b70f91eacdca1f8b399402549f7c51ccac Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Tue, 15 Feb 2022 23:30:51 +0100 Subject: [PATCH 210/379] Update menus to use the new input system all over... --- src/k_menu.h | 5 + src/k_menudef.c | 4 +- src/k_menudraw.c | 96 +++---- src/k_menufunc.c | 632 ++++++++++++++++++++++++++--------------------- src/k_profiles.c | 11 +- src/k_profiles.h | 7 +- 6 files changed, 420 insertions(+), 335 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index b4a69c861..97035862d 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -597,6 +597,10 @@ extern struct optionsmenu_s { INT16 toptx; INT16 topty; + // profile garbage + boolean profilemenu; // In profile menu. (Used to know when to get the "PROFILE SETUP" button away.... + profile_t *profile; // Pointer to the profile we're editing + // for video mode testing: INT32 vidm_testingmode; INT32 vidm_previousmode; @@ -624,6 +628,7 @@ void M_HandleItemToggles(INT32 choice); // For item toggling void M_EraseData(INT32 choice); // For data erasing // profile selection menu +void M_ProfileSelectInit(INT32 choice); void M_HandleProfileSelect(INT32 ch); // video modes menu (resolution) diff --git a/src/k_menudef.c b/src/k_menudef.c index c75354eb3..91e0eceb1 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -367,8 +367,8 @@ menu_t PLAY_MP_RoomSelectDef = { menuitem_t OPTIONS_Main[] = { - {IT_STRING | IT_SUBMENU, "Profile Setup", "Remap keys & buttons to your likings.", - NULL, &OPTIONS_ProfilesDef, 0, 0}, + {IT_STRING | IT_CALL, "Profile Setup", "Remap keys & buttons to your likings.", + NULL, M_ProfileSelectInit, 0, 0}, {IT_STRING | IT_SUBMENU, "Video Options", "Change video settings such as the resolution.", NULL, &OPTIONS_VideoDef, 0, 0}, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 29cc158ee..ff289835a 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -754,6 +754,43 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) } } +static void M_DrawCharacterSprite(INT16 x, INT16 y, SINT8 skin, INT32 addflags, UINT8 *colormap) +{ + UINT8 spr; + spritedef_t *sprdef; + spriteframe_t *sprframe; + patch_t *sprpatch; + + UINT32 flags = 0; + UINT32 frame; + + spr = P_GetSkinSprite2(&skins[skin], SPR2_FSTN, NULL); + sprdef = &skins[skin].sprites[spr]; + + if (!sprdef->numframes) // No frames ?? + return; // Can't render! + + frame = states[S_KART_FAST].frame & FF_FRAMEMASK; + if (frame >= sprdef->numframes) // Walking animation missing + frame = 0; // Try to use standing frame + + sprframe = &sprdef->spriteframes[frame]; + sprpatch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE); + + if (sprframe->flip & 1) // Only for first sprite + flags |= V_FLIP; // This sprite is left/right flipped! + + if (skins[skin].flags & SF_HIRES) + { + V_DrawFixedPatch(x<gridx][p->gridy].skinlist[p->clonenum]; UINT8 color = p->color; UINT8 *colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); + INT32 flags = 0; - if (skin != -1) - { - UINT8 spr; - spritedef_t *sprdef; - spriteframe_t *sprframe; - patch_t *sprpatch; + // Flip for left-side players + if (!(num & 1)) + flags ^= V_FLIP; - UINT32 flags = 0; - UINT32 frame; - - spr = P_GetSkinSprite2(&skins[skin], SPR2_FSTN, NULL); - sprdef = &skins[skin].sprites[spr]; - - if (!sprdef->numframes) // No frames ?? - return; // Can't render! - - frame = states[S_KART_FAST].frame & FF_FRAMEMASK; - if (frame >= sprdef->numframes) // Walking animation missing - frame = 0; // Try to use standing frame - - sprframe = &sprdef->spriteframes[frame]; - sprpatch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE); - - if (sprframe->flip & 1) // Only for first sprite - flags |= V_FLIP; // This sprite is left/right flipped! - - // Flip for left-side players - if (!(num & 1)) - flags ^= V_FLIP; - - if (skins[skin].flags & SF_HIRES) - { - V_DrawFixedPatch(x<color, GTC_CACHE); strcpy(pname, p->profilename); + skinnum = R_SkinAvailable(p->skinname); } + // Card V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, card, colormap); - V_DrawCenteredGamemodeString(x, y+18, 0, 0, pname); - CONS_Printf("pname %s\n", pname); + if (skinnum > -1) + M_DrawCharacterSprite(x, y+104, skinnum, 0, colormap); + + V_DrawCenteredGamemodeString(x, y+16, V_ALLOWLOWERCASE, 0, pname); + + // Card bottom to overlay the skin preview + V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, cardbot, colormap); x += 96; } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 05b980146..f32b192aa 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1146,6 +1146,11 @@ static void M_HandleMenuInput(void) return; } + if (menucmd[pid].delay > 0) + { + return; + } + // Handle menu-specific input handling. If this returns true, we skip regular input handling. if (currentMenu->inputroutine) { @@ -1154,12 +1159,6 @@ static void M_HandleMenuInput(void) return; } } - - if (menucmd[pid].delay > 0) - { - return; - } - routine = currentMenu->menuitems[itemOn].itemaction; // Handle menuitems which need a specific key handling @@ -3101,6 +3100,9 @@ boolean M_OptionsQuit(void) optionsmenu.toptx = 140-1; optionsmenu.topty = 70+1; + optionsmenu.profilemenu = false; + optionsmenu.profile = NULL; + return true; // Always allow quitting, duh. } @@ -3125,6 +3127,8 @@ void M_OptionsTick(void) } else { + // I don't like this, it looks like shit but it needs to be done.......... + optionsmenu.toptx = 160; optionsmenu.topty = 50; } @@ -3142,47 +3146,56 @@ void M_OptionsTick(void) boolean M_OptionsInputs(INT32 ch) { - switch (ch) + const UINT8 pid = 0; + (void)ch; + + if (menucmd[pid].dpad_ud > 0) { - case KEY_DOWNARROW: - { - optionsmenu.offset += 48; - M_NextOpt(); - S_StartSound(NULL, sfx_menu1); + M_SetMenuDelay(pid); + optionsmenu.offset += 48; + M_NextOpt(); + S_StartSound(NULL, sfx_menu1); - if (itemOn == 0) - optionsmenu.offset -= currentMenu->numitems*48; + if (itemOn == 0) + optionsmenu.offset -= currentMenu->numitems*48; - return true; - } - case KEY_UPARROW: - { - optionsmenu.offset -= 48; - M_PrevOpt(); - S_StartSound(NULL, sfx_menu1); - - if (itemOn == currentMenu->numitems-1) - optionsmenu.offset += currentMenu->numitems*48; - - return true; - } - case KEY_ENTER: - { - - if (currentMenu->menuitems[itemOn].status & IT_TRANSTEXT) - return true; // No. - - optionsmenu.optx = 140; - optionsmenu.opty = 70; // Default position for the currently selected option. - - return false; // Don't eat. - } + return true; } + else if (menucmd[pid].dpad_ud < 0) + { + M_SetMenuDelay(pid); + optionsmenu.offset -= 48; + M_PrevOpt(); + S_StartSound(NULL, sfx_menu1); + if (itemOn == currentMenu->numitems-1) + optionsmenu.offset += currentMenu->numitems*48; + + + return true; + } + else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + { + + if (currentMenu->menuitems[itemOn].status & IT_TRANSTEXT) + return true; // No. + + optionsmenu.optx = 140; + optionsmenu.opty = 70; // Default position for the currently selected option. + return false; // Don't eat. + } return false; } +void M_ProfileSelectInit(INT32 choice) +{ + (void)choice; + optionsmenu.profilemenu = true; + + M_SetupNextMenu(&OPTIONS_ProfilesDef, false); +} + // setup video mode menu void M_VideoModeMenu(INT32 choice) { @@ -3272,52 +3285,72 @@ void M_HandleProfileSelect(INT32 ch) // special menuitem key handler for video mode list void M_HandleVideoModes(INT32 ch) { - if (optionsmenu.vidm_testingmode > 0) switch (ch) + + const UINT8 pid = 0; + (void)ch; + + if (optionsmenu.vidm_testingmode > 0) { // change back to the previous mode quickly - case KEY_ESCAPE: + if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + { setmodeneeded = optionsmenu.vidm_previousmode + 1; optionsmenu.vidm_testingmode = 0; - break; - - case KEY_ENTER: + } + else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + { S_StartSound(NULL, sfx_menu1); optionsmenu.vidm_testingmode = 0; // stop testing + } } - else switch (ch) + else { - case KEY_DOWNARROW: + if (menucmd[pid].dpad_ud < 0) + { S_StartSound(NULL, sfx_menu1); if (++optionsmenu.vidm_selected >= optionsmenu.vidm_nummodes) optionsmenu.vidm_selected = 0; - break; - case KEY_UPARROW: + M_SetMenuDelay(pid); + } + + else if (menucmd[pid].dpad_ud > 0) + { S_StartSound(NULL, sfx_menu1); if (--optionsmenu.vidm_selected < 0) optionsmenu.vidm_selected = optionsmenu.vidm_nummodes - 1; - break; - case KEY_LEFTARROW: + M_SetMenuDelay(pid); + } + + else if (menucmd[pid].dpad_lr < 0) + { S_StartSound(NULL, sfx_menu1); optionsmenu.vidm_selected -= optionsmenu.vidm_column_size; if (optionsmenu.vidm_selected < 0) optionsmenu.vidm_selected = (optionsmenu.vidm_column_size*3) + optionsmenu.vidm_selected; if (optionsmenu.vidm_selected >= optionsmenu.vidm_nummodes) optionsmenu.vidm_selected = optionsmenu.vidm_nummodes - 1; - break; - case KEY_RIGHTARROW: + M_SetMenuDelay(pid); + } + + else if (menucmd[pid].dpad_lr > 0) + { S_StartSound(NULL, sfx_menu1); optionsmenu.vidm_selected += optionsmenu.vidm_column_size; if (optionsmenu.vidm_selected >= (optionsmenu.vidm_column_size*3)) optionsmenu.vidm_selected %= optionsmenu.vidm_column_size; if (optionsmenu.vidm_selected >= optionsmenu.vidm_nummodes) optionsmenu.vidm_selected = optionsmenu.vidm_nummodes - 1; - break; - case KEY_ENTER: + M_SetMenuDelay(pid); + } + + else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + { + M_SetMenuDelay(pid); S_StartSound(NULL, sfx_menu1); if (vid.modenum == optionsmenu.modedescs[optionsmenu.vidm_selected].modenum) SCR_SetDefaultMode(); @@ -3328,17 +3361,16 @@ void M_HandleVideoModes(INT32 ch) if (!setmodeneeded) // in case the previous setmode was not finished setmodeneeded = optionsmenu.modedescs[optionsmenu.vidm_selected].modenum + 1; } - break; + } - case KEY_ESCAPE: // this one same as M_Responder + else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + { + M_SetMenuDelay(pid); if (currentMenu->prevMenu) M_SetupNextMenu(currentMenu->prevMenu, false); else M_ClearMenus(true); - break; - - default: - break; + } } } @@ -3349,86 +3381,103 @@ void M_HandleItemToggles(INT32 choice) INT16 next; UINT8 i; boolean exitmenu = false; + const UINT8 pid = 0; - switch (choice) + (void) choice; + + + if (menucmd[pid].dpad_lr > 0) { - 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; + S_StartSound(NULL, sfx_menu1); + column++; + if (((column*height)+row) >= currentMenu->numitems) + column = 0; + next = min(((column*height)+row), currentMenu->numitems-1); + itemOn = next; - case KEY_LEFTARROW: - S_StartSound(NULL, sfx_menu1); + M_SetMenuDelay(pid); + } + + else if (menucmd[pid].dpad_lr < 0) + { + S_StartSound(NULL, sfx_menu1); + column--; + if (column < 0) + column = width-1; + if (((column*height)+row) >= currentMenu->numitems) 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; + next = max(((column*height)+row), 0); + if (next >= currentMenu->numitems) + next = currentMenu->numitems-1; + itemOn = next; - 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; + M_SetMenuDelay(pid); + } - 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; + else if (menucmd[pid].dpad_ud > 0) + { + 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; - case KEY_ENTER: + M_SetMenuDelay(pid); + } + + else if (menucmd[pid].dpad_ud < 0) + { + 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; + + M_SetMenuDelay(pid); + } + + else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + { + M_SetMenuDelay(pid); #ifdef ITEMTOGGLEBOTTOMRIGHT - if (currentMenu->menuitems[itemOn].mvar1 == 255) + if (currentMenu->menuitems[itemOn].mvar1 == 255) + { + //S_StartSound(NULL, sfx_s26d); + if (!shitsfree) { - //S_StartSound(NULL, sfx_s26d); - if (!shitsfree) - { - shitsfree = TICRATE; - S_StartSound(NULL, sfx_itfree); - } + shitsfree = TICRATE; + S_StartSound(NULL, sfx_itfree); } - else + } + else #endif - if (currentMenu->menuitems[itemOn].mvar1 == 0) + if (currentMenu->menuitems[itemOn].mvar1 == 0) + { + INT32 v = cv_sneaker.value; + S_StartSound(NULL, sfx_s1b4); + for (i = 0; i < NUMKARTRESULTS-1; i++) { - INT32 v = cv_sneaker.value; - S_StartSound(NULL, sfx_s1b4); - for (i = 0; i < NUMKARTRESULTS-1; i++) - { - if (KartItemCVars[i]->value == v) - CV_AddValue(KartItemCVars[i], 1); - } + if (KartItemCVars[i]->value == v) + CV_AddValue(KartItemCVars[i], 1); } - else - { - S_StartSound(NULL, sfx_s1ba); - CV_AddValue(KartItemCVars[currentMenu->menuitems[itemOn].mvar1-1], 1); - } - break; + } + else + { + S_StartSound(NULL, sfx_s1ba); + CV_AddValue(KartItemCVars[currentMenu->menuitems[itemOn].mvar1-1], 1); + } + } - case KEY_ESCAPE: - exitmenu = true; - break; + else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + { + M_SetMenuDelay(pid); + exitmenu = true; } if (exitmenu) @@ -3499,41 +3548,46 @@ void M_ExtrasTick(void) boolean M_ExtrasInputs(INT32 ch) { - switch (ch) + const UINT8 pid = 0; + (void) ch; + + if (menucmd[pid].dpad_ud > 0) { - case KEY_DOWNARROW: - { - extrasmenu.offset += 48; - M_NextOpt(); - S_StartSound(NULL, sfx_menu1); + extrasmenu.offset += 48; + M_NextOpt(); + S_StartSound(NULL, sfx_menu1); - if (itemOn == 0) - extrasmenu.offset -= currentMenu->numitems*48; + if (itemOn == 0) + extrasmenu.offset -= currentMenu->numitems*48; - return true; - } - case KEY_UPARROW: - { - extrasmenu.offset -= 48; - M_PrevOpt(); - S_StartSound(NULL, sfx_menu1); + M_SetMenuDelay(pid); + return true; + } - if (itemOn == currentMenu->numitems-1) - extrasmenu.offset += currentMenu->numitems*48; + else if (menucmd[pid].dpad_ud < 0) + { + extrasmenu.offset -= 48; + M_PrevOpt(); + S_StartSound(NULL, sfx_menu1); - return true; - } - case KEY_ENTER: - { + if (itemOn == currentMenu->numitems-1) + extrasmenu.offset += currentMenu->numitems*48; - if (currentMenu->menuitems[itemOn].status & IT_TRANSTEXT) - return true; // No. + M_SetMenuDelay(pid); + return true; + } - extrasmenu.extx = 140; - extrasmenu.exty = 70; // Default position for the currently selected option. + else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + { - return false; // Don't eat. - } + if (currentMenu->menuitems[itemOn].status & IT_TRANSTEXT) + return true; // No. + + extrasmenu.extx = 140; + extrasmenu.exty = 70; // Default position for the currently selected option. + + M_SetMenuDelay(pid); + return false; // Don't eat. } return false; } @@ -3634,36 +3688,35 @@ void M_PauseTick(void) boolean M_PauseInputs(INT32 ch) { + const UINT8 pid = 0; + (void) ch; + if (pausemenu.closing) return true; // Don't allow inputs. - switch (ch) + if (menucmd[pid].dpad_ud < 0) { - - case KEY_UPARROW: - { - pausemenu.offset -= 50; // Each item is spaced by 50 px - S_StartSound(NULL, sfx_menu1); - M_PrevOpt(); - return true; - } - - case KEY_DOWNARROW: - { - pausemenu.offset += 50; // Each item is spaced by 50 px - S_StartSound(NULL, sfx_menu1); - M_NextOpt(); - return true; - } - - case KEY_ESCAPE: - { - M_QuitPauseMenu(); - return true; - } - + M_SetMenuDelay(pid); + pausemenu.offset -= 50; // Each item is spaced by 50 px + S_StartSound(NULL, sfx_menu1); + M_PrevOpt(); + return true; } + else if (menucmd[pid].dpad_ud > 0) + { + pausemenu.offset += 50; // Each item is spaced by 50 px + S_StartSound(NULL, sfx_menu1); + M_NextOpt(); + M_SetMenuDelay(pid); + return true; + } + + else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + { + M_QuitPauseMenu(); + return true; + } return false; } @@ -3914,9 +3967,12 @@ void M_ReplayHut(INT32 choice) // key handler void M_HandleReplayHutList(INT32 choice) { - switch (choice) + + const UINT8 pid = 0; + (void) choice; + + if (menucmd[pid].dpad_ud > 0) { - case KEY_UPARROW: if (dir_on[menudepthleft]) dir_on[menudepthleft]--; else @@ -3925,9 +3981,10 @@ void M_HandleReplayHutList(INT32 choice) S_StartSound(NULL, sfx_menu1); extrasmenu.replayScrollTitle = 0; extrasmenu.replayScrollDelay = TICRATE; extrasmenu.replayScrollDir = 1; - break; + } - case KEY_DOWNARROW: + else if (menucmd[pid].dpad_ud < 0) + { if (dir_on[menudepthleft] < sizedirmenu-1) dir_on[menudepthleft]++; else @@ -3936,13 +3993,15 @@ void M_HandleReplayHutList(INT32 choice) S_StartSound(NULL, sfx_menu1); extrasmenu.replayScrollTitle = 0; extrasmenu.replayScrollDelay = TICRATE; extrasmenu.replayScrollDir = 1; - break; + } - case KEY_ESCAPE: + else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + { M_QuitReplayHut(); - break; + } - case KEY_ENTER: + else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + { switch (dirmenu[dir_on[menudepthleft]][DIR_TYPE]) { case EXT_FOLDER: @@ -4028,8 +4087,6 @@ void M_HandleReplayHutList(INT32 choice) break; } } - - break; } } @@ -4252,8 +4309,11 @@ static boolean M_ChangeStringAddons(INT32 choice) void M_HandleAddons(INT32 choice) { + const UINT8 pid = 0; boolean exitmenu = false; // exit to previous menu + (void) choice; + if (M_ChangeStringAddons(choice)) { char *tempname = NULL; @@ -4270,116 +4330,122 @@ void M_HandleAddons(INT32 choice) #endif } - switch (choice) + if (menucmd[pid].dpad_ud < 0) { - 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: + if (dir_on[menudepthleft] < sizedirmenu-1) + dir_on[menudepthleft]++; + S_StartSound(NULL, sfx_menu1); + } + else if (menucmd[pid].dpad_ud > 0) + { + if (dir_on[menudepthleft]) + dir_on[menudepthleft]--; + S_StartSound(NULL, sfx_menu1); + } + + else if (M_MenuButtonPressed(pid, MBT_L)) + { + UINT8 i; + for (i = numaddonsshown; i && (dir_on[menudepthleft] < sizedirmenu-1); i--) + dir_on[menudepthleft]++; + + S_StartSound(NULL, sfx_menu1); + } + + else if (M_MenuButtonPressed(pid, MBT_R)) + { + UINT8 i; + for (i = numaddonsshown; i && (dir_on[menudepthleft]); i--) + dir_on[menudepthleft]--; + + S_StartSound(NULL, sfx_menu1); + } + + else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + { + boolean refresh = true; + if (!dirmenu[dir_on[menudepthleft]]) + S_StartSound(NULL, sfx_s26d); + else + { + switch (dirmenu[dir_on[menudepthleft]][DIR_TYPE]) { - 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) { - case EXT_FOLDER: - strcpy(&menupath[menupathindex[menudepthleft]],dirmenu[dir_on[menudepthleft]]+DIR_STRING); - if (menudepthleft) - { - menupathindex[--menudepthleft] = strlen(menupath); - menupath[menupathindex[menudepthleft]] = 0; + 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); + 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(false, false)) + + if (!preparefilemenu(true, 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: - 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); + } + else + { + S_StartSound(NULL, sfx_menu1); + dir_on[menudepthleft] = 1; + } + refresh = false; } - } - if (refresh) - refreshdirmenu |= REFRESHDIR_NORMAL; + 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: + 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); } - break; - case KEY_ESCAPE: - exitmenu = true; - break; - - default: - break; + if (refresh) + refreshdirmenu |= REFRESHDIR_NORMAL; + } } + else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + { + exitmenu = true; + } + + if (exitmenu) { closefilemenu(true); diff --git a/src/k_profiles.c b/src/k_profiles.c index 979c7da83..678766a7d 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -16,14 +16,15 @@ static profile_t profilesList[MAXPROFILES+1]; // +1 because we're gonna add a default "GUEST' profile. static UINT8 numprofiles = 0; // # of loaded profiles -profile_t PR_MakeProfile(const char *prname, const char *pname, const UINT16 col, const char *fname, UINT16 fcol, INT32 controlarray[num_gamecontrols][MAXINPUTMAPPING]) +profile_t PR_MakeProfile(const char *prname, const char *pname, const char *sname, const UINT16 col, const char *fname, UINT16 fcol, INT32 controlarray[num_gamecontrols][MAXINPUTMAPPING]) { profile_t new; new.version = PROFILEVER; strcpy(new.profilename, prname); - + + strcpy(new.skinname, sname); strcpy(new.playername, pname); new.color = col; @@ -36,10 +37,10 @@ profile_t PR_MakeProfile(const char *prname, const char *pname, const UINT16 col return new; } -profile_t PR_MakeProfileFromPlayer(const char *prname, const char *pname, const UINT16 col, const char *fname, UINT16 fcol, UINT8 pnum) +profile_t PR_MakeProfileFromPlayer(const char *prname, const char *pname, const char *sname, const UINT16 col, const char *fname, UINT16 fcol, UINT8 pnum) { // Generate profile using the player's gamecontrol, as we set them directly when making profiles from menus. - profile_t new = PR_MakeProfile(prname, pname, col, fname, fcol, gamecontrol[pnum]); + profile_t new = PR_MakeProfile(prname, pname, sname, col, fname, fcol, gamecontrol[pnum]); // Player bound cvars: new.kickstartaccel = cv_kickstartaccel[pnum].value; @@ -85,7 +86,7 @@ void PR_SaveProfiles(void) void PR_LoadProfiles(void) { //FILE *f = NULL; - profile_t dprofile = PR_MakeProfile(PROFILEDEFAULTNAME, PROFILEDEFAULTPNAME, PROFILEDEFAULTCOLOR, PROFILEDEFAULTFOLLOWER, PROFILEDEFAULTFOLLOWERCOLOR, gamecontroldefault); + profile_t dprofile = PR_MakeProfile(PROFILEDEFAULTNAME, PROFILEDEFAULTPNAME, PROFILEDEFAULTSKIN, PROFILEDEFAULTCOLOR, PROFILEDEFAULTFOLLOWER, PROFILEDEFAULTFOLLOWERCOLOR, gamecontroldefault); PR_AddProfile(dprofile); /*f = fopen(PROFILESFILE, "r"); diff --git a/src/k_profiles.h b/src/k_profiles.h index 7611fa43f..fb68d3f85 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -30,7 +30,7 @@ #define MAXPROFILES 16 #define PROFILESFILE "kartprofiles.cfg" -#define PROFILEDEFAULTNAME "GUEST" +#define PROFILEDEFAULTNAME "guest" #define PROFILEDEFAULTPNAME "Player" #define PROFILEDEFAULTSKIN "sonic" #define PROFILEDEFAULTCOLOR SKINCOLOR_SAPPHIRE @@ -52,6 +52,7 @@ typedef struct profile_s // Player data char playername[MAXPLAYERNAME+1]; // Player name + char skinname[SKINNAMESIZE+1]; // Default Skin UINT16 color; // Default player coloUr. ...But for consistency we'll name it color. char follower[SKINNAMESIZE+1]; // Follower UINT16 followercolor; // Follower color @@ -70,12 +71,12 @@ typedef struct profile_s // PR_MakeProfile // Makes a profile from the supplied profile name, player name, colour, follower, followercolour and controls. // The consvar values are left untouched. -profile_t PR_MakeProfile(const char *prname, const char *pname, const UINT16 col, const char *fname, UINT16 fcol, INT32 controlarray[num_gamecontrols][MAXINPUTMAPPING]); +profile_t PR_MakeProfile(const char *prname, const char *pname, const char *sname, const UINT16 col, const char *fname, UINT16 fcol, INT32 controlarray[num_gamecontrols][MAXINPUTMAPPING]); // PR_MakeProfileFromPlayer // Makes a profile_t from the supplied profile name, player name, colour, follower and followercolour. // The last argument is a player number to read cvars from; as for convenience, cvars will be set directly when making a profile (since loading another one will overwrite them, this will be inconsequential) -profile_t PR_MakeProfileFromPlayer(const char *prname, const char *pname, const UINT16 col, const char *fname, UINT16 fcol, UINT8 pnum); +profile_t PR_MakeProfileFromPlayer(const char *prname, const char *pname, const char *sname, const UINT16 col, const char *fname, UINT16 fcol, UINT8 pnum); // PR_AddProfile(profile_t p) // Adds a profile to profilesList and increments numprofiles. From c44300d39f570a1c9a77e3c424c009da42416671 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Wed, 16 Feb 2022 10:29:01 +0100 Subject: [PATCH 211/379] Use mockup design for cards, allow moving around and getting out of the profile menu. Still can't make/edit profiles --- src/k_menu.h | 2 ++ src/k_menudraw.c | 28 +++++++++++++++++------ src/k_menufunc.c | 59 ++++++++++++++++++++++++++++++++++++++++++++---- src/v_video.c | 12 ++++++++++ src/v_video.h | 6 +++++ 5 files changed, 95 insertions(+), 12 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 97035862d..5035ea200 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -599,6 +599,8 @@ extern struct optionsmenu_s { // profile garbage boolean profilemenu; // In profile menu. (Used to know when to get the "PROFILE SETUP" button away.... + boolean resetprofilemenu; // Reset button behaviour when exiting + SINT8 profilen; // # of the selected profile. profile_t *profile; // Pointer to the profile we're editing // for video mode testing: diff --git a/src/k_menudraw.c b/src/k_menudraw.c index ff289835a..4aeae945a 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2020,19 +2020,21 @@ void M_DrawProfileSelect(void) INT32 i; patch_t *card = W_CachePatchName("PR_CARD", PU_CACHE); patch_t *cardbot = W_CachePatchName("PR_CARDB", PU_CACHE); + patch_t *pwrlv = W_CachePatchName("PR_PWR", PU_CACHE); - INT32 x = 160; - INT32 y = 75 + menutransition.tics*16; + INT32 x = 160 - optionsmenu.profilen*(128 + 128/8) + optionsmenu.offset; + INT32 y = 35 + menutransition.tics*16; M_DrawOptionsCogs(); M_DrawMenuTooltips(); M_DrawOptionsMovingButton(); - for (i=0; i < MAXPROFILES; i++) + for (i=0; i < MAXPROFILES+1; i++) // +1 because the default profile does not count { profile_t *p = PR_GetProfile(i); UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_BLACK, GTC_CACHE); INT32 skinnum = -1; + INT32 powerlevel = -1; char pname[PROFILENAMELEN+1] = "empty"; if (p != NULL) @@ -2040,20 +2042,32 @@ void M_DrawProfileSelect(void) colormap = R_GetTranslationColormap(TC_DEFAULT, p->color, GTC_CACHE); strcpy(pname, p->profilename); skinnum = R_SkinAvailable(p->skinname); + powerlevel = 1000; // Test } // Card V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, card, colormap); - if (skinnum > -1) - M_DrawCharacterSprite(x, y+104, skinnum, 0, colormap); + // Draw pwlv if we can + if (powerlevel > -1) + { + V_DrawFixedPatch((x+30)*FRACUNIT, (y+84)*FRACUNIT, FRACUNIT, 0, pwrlv, colormap); + V_DrawCenteredKartString(x+30, y+87, 0, va("%d\n", powerlevel)); + } - V_DrawCenteredGamemodeString(x, y+16, V_ALLOWLOWERCASE, 0, pname); + + if (skinnum > -1) + { + M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap); + V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap); + } + + V_DrawCenteredGamemodeString(x, y+24, 0, 0, pname); // Card bottom to overlay the skin preview V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, cardbot, colormap); - x += 96; + x += 128 + 128/8; } } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index f32b192aa..4f64d4dec 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3100,8 +3100,13 @@ boolean M_OptionsQuit(void) optionsmenu.toptx = 140-1; optionsmenu.topty = 70+1; - optionsmenu.profilemenu = false; - optionsmenu.profile = NULL; + // Reset button behaviour because profile menu is different, since of course it is. + if (optionsmenu.resetprofilemenu) + { + optionsmenu.profilemenu = false; + optionsmenu.profile = NULL; + optionsmenu.resetprofilemenu = false; + } return true; // Always allow quitting, duh. } @@ -3128,9 +3133,16 @@ void M_OptionsTick(void) else { // I don't like this, it looks like shit but it needs to be done.......... - - optionsmenu.toptx = 160; - optionsmenu.topty = 50; + if (optionsmenu.profilemenu) + { + optionsmenu.toptx = 420; + optionsmenu.topty = 70+1; + } + else + { + optionsmenu.toptx = 160; + optionsmenu.topty = 50; + } } // Handle the background stuff: @@ -3279,7 +3291,44 @@ void M_VideoModeMenu(INT32 choice) void M_HandleProfileSelect(INT32 ch) { + const UINT8 pid = 0; (void) ch; + + if (menucmd[pid].dpad_lr > 0) + { + optionsmenu.profilen++; + optionsmenu.offset += (128 + 128/8); + + if (optionsmenu.profilen > MAXPROFILES) + { + optionsmenu.profilen = 0; + optionsmenu.offset -= (128 + 128/8)*(MAXPROFILES+1); + } + + S_StartSound(NULL, sfx_menu1); + M_SetMenuDelay(pid); + + } + else if (menucmd[pid].dpad_lr < 0) + { + optionsmenu.profilen--; + optionsmenu.offset -= (128 + 128/8); + + if (optionsmenu.profilen < 0) + { + optionsmenu.profilen = MAXPROFILES; + optionsmenu.offset += (128 + 128/8)*(MAXPROFILES+1); + } + + S_StartSound(NULL, sfx_menu1); + M_SetMenuDelay(pid); + } + else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + { + optionsmenu.resetprofilemenu = true; + M_GoBack(0); + } + } // special menuitem key handler for video mode list diff --git a/src/v_video.c b/src/v_video.c index 85e8efa00..832a945b6 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -2544,6 +2544,18 @@ void V_DrawRightAlignedThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, con V_DrawThinStringAtFixed(x, y, option, string); } +void V_DrawCenteredKartString(INT32 x, INT32 y, INT32 option, const char *string) +{ + x -= V_KartStringWidth(string, option)/2; + V_DrawKartString(x, y, option, string); +} + +void V_DrawRightAlignedKartString(INT32 x, INT32 y, INT32 option, const char *string) +{ + x -= V_KartStringWidth(string, option); + V_DrawKartString(x, y, option, string); +} + void V_DrawCenteredGamemodeString(INT32 x, INT32 y, INT32 option, const UINT8 *colormap, const char *string) { x -= V_GamemodeStringWidth(string, option)/2; diff --git a/src/v_video.h b/src/v_video.h index ab0d2d4ce..7319a58b0 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -326,6 +326,12 @@ void V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *colorm #define V_DrawKartString( x,y,option,string ) \ V__DrawDupxString (x,y,FRACUNIT,option,NULL,KART_FONT,string) +#define V_KartStringWidth( string,option ) \ + V__IntegerStringWidth ( FRACUNIT,option,KART_FONT,string ) + +void V_DrawCenteredKartString(INT32 x, INT32 y, INT32 option, const char *string); +void V_DrawRightAlignedKartString(INT32 x, INT32 y, INT32 option, const char *string); + #define V_DrawGamemodeString( x,y,option,cm,string ) \ V__DrawDupxString (x,y,FRACUNIT,option,cm,GM_FONT,string) From 8efba24df72b2377447ef21e0e41445ae2c25d06 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Wed, 16 Feb 2022 23:57:06 +0100 Subject: [PATCH 212/379] profiles: start on edit menu (it does nothing) --- src/k_menu.h | 12 ++++ src/k_menudef.c | 30 ++++++++++ src/k_menudraw.c | 142 ++++++++++++++++++++++++++++++++++------------- src/k_menufunc.c | 44 ++++++++++++++- src/k_profiles.c | 2 +- 5 files changed, 190 insertions(+), 40 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 5035ea200..a7acece63 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -223,6 +223,9 @@ typedef enum extern menuitem_t OPTIONS_Profiles[]; extern menu_t OPTIONS_ProfilesDef; +extern menuitem_t OPTIONS_EditProfile[]; +extern menu_t OPTIONS_EditProfileDef; + extern menuitem_t OPTIONS_Video[]; extern menu_t OPTIONS_VideoDef; @@ -592,6 +595,8 @@ extern struct optionsmenu_s { // For moving the button when we get into a submenu. it's smooth and cool! (normal x/y and target x/y.) // this is only used during menu transitions. + + // For profiles specifically, this moves the card around since we don't have the rest of the menu displayed in that case. INT16 optx; INT16 opty; INT16 toptx; @@ -601,6 +606,8 @@ extern struct optionsmenu_s { boolean profilemenu; // In profile menu. (Used to know when to get the "PROFILE SETUP" button away.... boolean resetprofilemenu; // Reset button behaviour when exiting SINT8 profilen; // # of the selected profile. + + boolean resetprofile; // After going back from the edit menu, this tells the profile select menu to kill the profile data after the transition. profile_t *profile; // Pointer to the profile we're editing // for video mode testing: @@ -620,6 +627,9 @@ extern struct optionsmenu_s { tic_t fade; } optionsmenu; +extern consvar_t cv_dummyprofilename; +extern consvar_t cv_dummyprofileplayername; + void M_InitOptions(INT32 choice); // necessary for multiplayer since there's some options we won't want to access void M_OptionsTick(void); boolean M_OptionsInputs(INT32 ch); @@ -632,6 +642,7 @@ void M_EraseData(INT32 choice); // For data erasing // profile selection menu void M_ProfileSelectInit(INT32 choice); void M_HandleProfileSelect(INT32 ch); +boolean M_ProfileEditInputs(INT32 ch); // video modes menu (resolution) void M_VideoModeMenu(INT32 choice); @@ -772,6 +783,7 @@ void M_DrawOptionsMovingButton(void); // for sick transitions... void M_DrawOptions(void); void M_DrawGenericOptions(void); void M_DrawProfileSelect(void); +void M_DrawEditProfile(void); void M_DrawVideoModes(void); void M_DrawItemToggles(void); diff --git a/src/k_menudef.c b/src/k_menudef.c index 91e0eceb1..0d0353a42 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -430,6 +430,36 @@ menu_t OPTIONS_ProfilesDef = { NULL, }; + +menuitem_t OPTIONS_EditProfile[] = { + {IT_STRING | IT_CVAR | IT_CV_STRING, "Profile Name", "6-character long name to identify this Profile.", + NULL, &cv_dummyprofilename, 0, 0}, + + {IT_STRING | IT_CVAR | IT_CV_STRING, "Player Name", "Name displayed online when using this Profile.", + NULL, &cv_dummyprofileplayername, 0, 0}, + + {IT_STRING | IT_SUBMENU, "Character", "Default character and color for this Profile.", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_SUBMENU, "Controls", "Select the button mappings for this Profile.", + NULL, NULL, 0, 0}, +}; + +menu_t OPTIONS_EditProfileDef = { + sizeof (OPTIONS_EditProfile) / sizeof (menuitem_t), + &OPTIONS_ProfilesDef, + 0, + OPTIONS_EditProfile, + 32, 80, + SKINCOLOR_ULTRAMARINE, 0, + 2, 10, + M_DrawEditProfile, + M_OptionsTick, + NULL, + NULL, + M_ProfileEditInputs, +}; + // video options menu... // options menu menuitem_t OPTIONS_Video[] = diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 4aeae945a..562f8e9e3 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2014,61 +2014,127 @@ void M_DrawGenericOptions(void) } } +static void M_DrawProfileCard(INT32 x, INT32 y, profile_t *p) +{ + + patch_t *card = W_CachePatchName("PR_CARD", PU_CACHE); + patch_t *cardbot = W_CachePatchName("PR_CARDB", PU_CACHE); + patch_t *pwrlv = W_CachePatchName("PR_PWR", PU_CACHE); + UINT8 *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_BLACK, GTC_CACHE); + INT32 skinnum = -1; + INT32 powerlevel = -1; + + char pname[PROFILENAMELEN+1] = "empty"; + + if (p != NULL && p->version) + { + colormap = R_GetTranslationColormap(TC_DEFAULT, p->color, GTC_CACHE); + strcpy(pname, p->profilename); + skinnum = R_SkinAvailable(p->skinname); + powerlevel = 1000; // Test + } + + // Card + V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, card, colormap); + + // Draw pwlv if we can + if (powerlevel > -1) + { + V_DrawFixedPatch((x+30)*FRACUNIT, (y+84)*FRACUNIT, FRACUNIT, 0, pwrlv, colormap); + V_DrawCenteredKartString(x+30, y+87, 0, va("%d\n", powerlevel)); + } + + + if (skinnum > -1) + { + M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap); + V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap); + } + + V_DrawCenteredGamemodeString(x, y+24, 0, 0, pname); + + // Card bottom to overlay the skin preview + V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, cardbot, colormap); +} + // Draws profile selection void M_DrawProfileSelect(void) { INT32 i; - patch_t *card = W_CachePatchName("PR_CARD", PU_CACHE); - patch_t *cardbot = W_CachePatchName("PR_CARDB", PU_CACHE); - patch_t *pwrlv = W_CachePatchName("PR_PWR", PU_CACHE); - INT32 x = 160 - optionsmenu.profilen*(128 + 128/8) + optionsmenu.offset; INT32 y = 35 + menutransition.tics*16; M_DrawOptionsCogs(); M_DrawMenuTooltips(); - M_DrawOptionsMovingButton(); + + // This shouldn't be drawn when a profile is selected as optx/opty are used to move the card. + if (optionsmenu.profile == NULL && menutransition.tics) + M_DrawOptionsMovingButton(); for (i=0; i < MAXPROFILES+1; i++) // +1 because the default profile does not count { profile_t *p = PR_GetProfile(i); - UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_BLACK, GTC_CACHE); - INT32 skinnum = -1; - INT32 powerlevel = -1; - char pname[PROFILENAMELEN+1] = "empty"; - if (p != NULL) - { - colormap = R_GetTranslationColormap(TC_DEFAULT, p->color, GTC_CACHE); - strcpy(pname, p->profilename); - skinnum = R_SkinAvailable(p->skinname); - powerlevel = 1000; // Test - } - - // Card - V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, card, colormap); - - // Draw pwlv if we can - if (powerlevel > -1) - { - V_DrawFixedPatch((x+30)*FRACUNIT, (y+84)*FRACUNIT, FRACUNIT, 0, pwrlv, colormap); - V_DrawCenteredKartString(x+30, y+87, 0, va("%d\n", powerlevel)); - } - - - if (skinnum > -1) - { - M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap); - V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap); - } - - V_DrawCenteredGamemodeString(x, y+24, 0, 0, pname); - - // Card bottom to overlay the skin preview - V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, cardbot, colormap); + // don't draw the card in this specific scenario + if (!(menutransition.tics && optionsmenu.profile != NULL && optionsmenu.profilen == i)) + M_DrawProfileCard(x, y, p); x += 128 + 128/8; } + + // needs to be drawn since it happens on the transition + if (optionsmenu.profile != NULL) + M_DrawProfileCard(optionsmenu.optx, optionsmenu.opty, optionsmenu.profile); + + +} + +// Profile edition menu +void M_DrawEditProfile(void) +{ + + INT32 y = 40; + INT32 x = 145; + INT32 i; + + M_DrawOptionsCogs(); + + // Tooltip + // The text is slightly shifted hence why we don't just use M_DrawMenuTooltips() + V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL); + if (currentMenu->menuitems[itemOn].tooltip != NULL) + { + V_DrawCenteredThinString(BASEVIDWIDTH*2/3, 12, V_ALLOWLOWERCASE|V_6WIDTHSPACE, currentMenu->menuitems[itemOn].tooltip); + } + + // Draw the menu options... + for (i = 0; i < currentMenu->numitems; i++) + { + switch (currentMenu->menuitems[i].status & IT_DISPLAY) + { + case IT_STRING: + + UINT8 *colormap = NULL; + if (i == itemOn) + colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE); + + // Background + V_DrawFill(0, y, 400 - (menutransition.tics*32), 24, itemOn == i ? 169 : 30); // 169 is the plague colourization + // Text + V_DrawGamemodeString(x + (menutransition.tics*32), y - 6, V_ALLOWLOWERCASE, colormap, currentMenu->menuitems[i].text); + + + y += 42; + + break; + } + } + + // Finally, draw the card ontop + if (optionsmenu.profile != NULL) + { + M_DrawProfileCard(optionsmenu.optx, optionsmenu.opty, optionsmenu.profile); + } } // Draw the video modes list, a-la-Quake diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 4f64d4dec..119c4bcf1 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -182,6 +182,9 @@ consvar_t cv_dummyip = CVAR_INIT ("dummyip", "", CV_HIDDEN, NULL, NULL); consvar_t cv_dummymenuplayer = CVAR_INIT ("dummymenuplayer", "P1", CV_HIDDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange); consvar_t cv_dummyspectate = CVAR_INIT ("dummyspectate", "Spectator", CV_HIDDEN, dummyspectate_cons_t, NULL); +consvar_t cv_dummyprofilename = CVAR_INIT ("dummyprofilename", "", CV_HIDDEN, NULL, NULL); +consvar_t cv_dummyprofileplayername = CVAR_INIT ("dummyprofileplayername", "", CV_HIDDEN, NULL, NULL); + consvar_t cv_dummygpdifficulty = CVAR_INIT ("dummygpdifficulty", "Normal", CV_HIDDEN, dummygpdifficulty_cons_t, NULL); consvar_t cv_dummykartspeed = CVAR_INIT ("dummykartspeed", "Auto", CV_HIDDEN, dummykartspeed_cons_t, NULL); consvar_t cv_dummygpencore = CVAR_INIT ("dummygpdifficulty", "No", CV_HIDDEN, CV_YesNo, NULL); @@ -3130,7 +3133,7 @@ void M_OptionsTick(void) { M_OptionsQuit(); // ...So now this is used here. } - else + else if (optionsmenu.profile == NULL) // Not currently editing a profile (otherwise we're using these variables for other purposes....) { // I don't like this, it looks like shit but it needs to be done.......... if (optionsmenu.profilemenu) @@ -3323,12 +3326,51 @@ void M_HandleProfileSelect(INT32 ch) S_StartSound(NULL, sfx_menu1); M_SetMenuDelay(pid); } + + else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + { + S_StartSound(NULL, sfx_menu1); + optionsmenu.profile = PR_GetProfile(optionsmenu.profilen); + + // This is now used to move the card we've selected. + optionsmenu.optx = 160; + optionsmenu.opty = 35; + optionsmenu.toptx = 130/2; + optionsmenu.topty = 0; + + M_SetupNextMenu(&OPTIONS_EditProfileDef, false); + } + else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) { optionsmenu.resetprofilemenu = true; M_GoBack(0); } + if (menutransition.tics == 0 && optionsmenu.resetprofile) + { + optionsmenu.profile = NULL; // Make sure to reset that when transitions are done.' + optionsmenu.resetprofile = false; + } +} + +// For profile edit, just make sure going back resets the card to its position, the rest is taken care of automatically. +boolean M_ProfileEditInputs(INT32 ch) +{ + const UINT8 pid = 0; + (void) ch; + + if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + { + optionsmenu.toptx = 160; + optionsmenu.topty = 35; + optionsmenu.resetprofile = true; // Reset profile after the transition is done. + + M_GoBack(0); + return true; + } + + return false; } // special menuitem key handler for video mode list diff --git a/src/k_profiles.c b/src/k_profiles.c index 678766a7d..d87bb79da 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -65,7 +65,7 @@ boolean PR_AddProfile(profile_t p) profile_t* PR_GetProfile(INT32 num) { - if (num < numprofiles) + if (num < MAXPROFILES+1) return &profilesList[num]; else return NULL; From 0ac0efda701f5c261d6b953aa0533f214e4a730a Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Thu, 17 Feb 2022 15:05:22 +0100 Subject: [PATCH 213/379] Add typing menu overlay for controller parity --- src/k_menu.h | 16 ++++ src/k_menudraw.c | 105 +++++++++++++++++++++- src/k_menufunc.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 338 insertions(+), 4 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index a7acece63..a7b7a86d6 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -357,6 +357,22 @@ extern INT16 skullAnimCounter; // skull animation counter extern INT32 menuKey; // keyboard key pressed for menu +extern boolean menutyping; // Typing sub-menu +extern boolean menutypingclose; // if true, we're closing the menu. +extern boolean keyboardtyping; // If true, all keystrokes are treated as typing (ignores MBT_A etc). This is unset if you try moving the cursor on the virtual keyboard or use your controller +extern SINT8 menutypingfade; // Fade-in and out for typing sub-menu. (in 10 tics) +// While typing, we'll have a fade strongly darken the screen to overlay the typing menu instead + +extern INT16 virtualKeyboard[5][13]; +extern INT16 shift_virtualKeyboard[5][13]; + +// cursor position on the virtual keyboard grid +extern SINT8 keyboardx; +extern SINT8 keyboardy; +extern boolean keyboardcapslock; +extern boolean keyboardshift; + + #define MENUDELAYTIME 7 #define MENUMINDELAY 2 diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 562f8e9e3..602738929 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -223,6 +223,81 @@ void M_DrawMenuForeground(void) } } +// Draws the typing submenu +static void M_DrawMenuTyping(void) +{ + + INT32 i, j; + + INT32 x = 60; + INT32 y = 100 + (9-menutypingfade)*8; + INT32 tflag = (9 - menutypingfade)<menuitems[itemOn].itemaction; + + char buf[8]; // We write there to use drawstring for convenience. + + V_DrawFadeScreen(31, menutypingfade); + + // Draw the string we're editing at the top. + V_DrawString(x, y-48 + 12, V_ALLOWLOWERCASE|tflag, cv->string); + if (skullAnimCounter < 4) + V_DrawCharacter(x + V_StringWidth(cv->string, 0), y - 35, '_' | 0x80, false); + + // Some contextual stuff + if (keyboardtyping) + V_DrawThinString(10, 175, V_ALLOWLOWERCASE|tflag|V_GRAYMAP, "Type using your keyboard. Press Enter to confirm & exit.\nUse your controller or any directional input to use the Virtual Keyboard.\n"); + else + V_DrawThinString(10, 175, V_ALLOWLOWERCASE|tflag|V_GRAYMAP, "Type using the Virtual Keyboard. Use the \'OK\' button to confirm & exit.\nPress any keyboard key not bound to a control to use it."); + + + // Now the keyboard itself + for (i=0; i < 5; i++) + { + for (j=0; j < 13; j++) + { + INT32 mflag = 0; + INT16 c = virtualKeyboard[i][j]; + if (keyboardshift ^ keyboardcapslock) + c = shift_virtualKeyboard[i][j]; + + + if (c == KEY_BACKSPACE) + strcpy(buf, "DEL"); + + else if (c == KEY_RSHIFT) + strcpy(buf, "SHIFT"); + + else if (c == KEY_CAPSLOCK) + strcpy(buf, "CAPS"); + + else if (c == KEY_ENTER) + strcpy(buf, "OK"); + + else if (c == KEY_SPACE) + strcpy(buf, "SPACE"); + + else + { + buf[0] = c; + buf[1] = '\0'; + } + + // highlight: + if (keyboardx == j && keyboardy == i && !keyboardtyping) + mflag |= highlightflags; + else if (keyboardtyping) + mflag |= V_TRANSLUCENT; // grey it out if we can't use it. + + V_DrawString(x, y, V_ALLOWLOWERCASE|tflag|mflag, buf); + + x += V_StringWidth(buf, 0)+8; + } + x = 60; + y += 12; + } +} + // // M_Drawer // Called after the view has been rendered, @@ -274,7 +349,12 @@ void M_Drawer(void) V_DrawThinString(vid.dupx, vid.height - 10*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, va("%s", VERSIONSTRING)); #endif } + + } + // Draw typing overlay when needed, above all other menu elements. + if (menutyping) + M_DrawMenuTyping(); } if (menuwipe) @@ -2093,7 +2173,7 @@ void M_DrawProfileSelect(void) void M_DrawEditProfile(void) { - INT32 y = 40; + INT32 y = 34; INT32 x = 145; INT32 i; @@ -2119,12 +2199,31 @@ void M_DrawEditProfile(void) colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE); // Background - V_DrawFill(0, y, 400 - (menutransition.tics*32), 24, itemOn == i ? 169 : 30); // 169 is the plague colourization + V_DrawFill(0, y, 400 - (menutransition.tics*64), 24, itemOn == i ? 169 : 30); // 169 is the plague colourization // Text V_DrawGamemodeString(x + (menutransition.tics*32), y - 6, V_ALLOWLOWERCASE, colormap, 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_STRING: + V_DrawFill(0, y+24, 400 - (menutransition.tics*64), 16, itemOn == i ? 169 : 30); // 169 is the plague colourization + V_DrawString(x + 8, y + 29, V_ALLOWLOWERCASE, cv->string); + if (skullAnimCounter < 4 && i == itemOn) + V_DrawCharacter(x + 8 + V_StringWidth(cv->string, 0), y + 29, + '_' | 0x80, false); + y += 16; + break; + } + } + } - y += 42; + y += 34; break; } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 119c4bcf1..ecc5af6a9 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -96,6 +96,35 @@ struct menutransition_s menutransition; // Menu transition properties INT32 menuKey = -1; // keyboard key pressed for menu menucmd_t menucmd[MAXSPLITSCREENPLAYERS]; +// Typing "sub"-menu +boolean menutyping = false; +boolean menutypingclose = false; +SINT8 menutypingfade = 0; +boolean keyboardtyping = false; + +SINT8 keyboardx = 0; +SINT8 keyboardy = 0; +boolean keyboardcapslock = false; +boolean keyboardshift = false; + +INT16 virtualKeyboard[5][13] = { + + {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0}, + {'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 0}, + {'a', 's', 'd', 'f', 'g', 'h', 'i', 'j', 'k', 'l', ';', '\'', '\\'}, + {'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, 0, 0}, + {KEY_SPACE, KEY_RSHIFT, KEY_BACKSPACE, KEY_CAPSLOCK, KEY_ENTER, 0, 0, 0, 0, 0, 0, 0, 0} +}; + +INT16 shift_virtualKeyboard[5][13] = { + + {'!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0}, + {'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', 0}, + {'A', 'S', 'D', 'F', 'G', 'H', 'I', 'J', 'K', 'L', ':', '\"', '|'}, + {'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, 0, 0}, + {KEY_SPACE, KEY_RSHIFT, KEY_BACKSPACE, KEY_CAPSLOCK, KEY_ENTER, 0, 0, 0, 0, 0, 0, 0, 0} +}; + // finish wipes between screens boolean menuwipe = false; @@ -1130,6 +1159,176 @@ boolean M_MenuButtonPressed(UINT8 pid, UINT32 bt) return (menucmd[pid].buttons & bt); } +// Updates the x coordinate of the keybord so prevent it from going in weird places +static void M_UpdateKeyboardX(void) +{ + // 0s are only at the rightmost edges of the keyboard table, so just go backwards until we get something. + while (!virtualKeyboard[keyboardy][keyboardx]) + keyboardx--; +} + +// Hack... +// This is used to prevent keyboard inputs from being processed when they shouldn't. +// We need to find why this is needed so that I can remove this disgusting thing +static INT32 lastkey = 0; +static tic_t lastkeyheldfor = 0; + +static void M_MenuTypingInput(INT32 key) +{ + + const UINT8 pid = 0; + + // Fade-in + + if (menutypingclose) // closing + { + menutypingfade--; + if (!menutypingfade) + menutyping = false; + + return; // prevent inputs while closing the menu. + } + else // opening + { + menutypingfade++; + if (menutypingfade > 9) // Don't fade all the way, but have it VERY strong to be readable + menutypingfade = 9; + else if (menutypingfade < 9) + return; // Don't allow typing until it's fully opened. + } + + // Now handle the inputs. + // HACK: TODO: REMOVE THIS + // This prevents the same key from being processed multiple times. ev_keydown should account for this but it seems like it doesn't. + if (lastkey != key) + { + lastkey = key; + lastkeyheldfor = 0; + } + else + { + lastkeyheldfor++; + if (lastkeyheldfor > 0 && lastkeyheldfor < TICRATE/2) + key = -1; + } + // END OF HACK. + + // Determine when to check for keyboard inputs or controller inputs using menuKey, which is the key passed here as argument. + if (!keyboardtyping) // controller inputs + { + // we pressed a keyboard input that's not any of our buttons + if (key != -1 && menucmd[pid].dpad_lr == 0 && menucmd[pid].dpad_ud == 0 + && !M_MenuButtonPressed(pid, MBT_A) + && !M_MenuButtonPressed(pid, MBT_B) + && !M_MenuButtonPressed(pid, MBT_C) + && !M_MenuButtonPressed(pid, MBT_X) + && !M_MenuButtonPressed(pid, MBT_Y) + && !M_MenuButtonPressed(pid, MBT_Z)) + { + keyboardtyping = true; + } + } + else // Keyboard inputs. + { + // On the flipside, if we're pressing any keyboard input, switch to controller inputs. + if (key == -1 && ( + M_MenuButtonPressed(pid, MBT_A) + || M_MenuButtonPressed(pid, MBT_B) + || M_MenuButtonPressed(pid, MBT_C) + || M_MenuButtonPressed(pid, MBT_X) + || M_MenuButtonPressed(pid, MBT_Y) + || M_MenuButtonPressed(pid, MBT_Z) + || menucmd[pid].dpad_lr != 0 + || menucmd[pid].dpad_ud != 0 + )) + { + keyboardtyping = false; + return; + } + + // OTHERWISE, process keyboard inputs for typing! + if (key == KEY_ENTER) + { + menutypingclose = true; // close menu. + return; + } + else // process everything else as input for typing + { + M_ChangeStringCvar(key); + } + + } + + if (menucmd[pid].delay == 0 && !keyboardtyping) // We must check for this here because we bypass the normal delay check to allow for normal keyboard inputs + { + if (menucmd[pid].dpad_ud > 0) // down + { + keyboardy++; + if (keyboardy > 4) + keyboardy = 0; + + M_UpdateKeyboardX(); + M_SetMenuDelay(pid); + S_StartSound(NULL, sfx_menu1); + } + else if (menucmd[pid].dpad_ud < 0) // up + { + keyboardy--; + if (keyboardy < 0) + keyboardy = 4; + + M_UpdateKeyboardX(); + M_SetMenuDelay(pid); + S_StartSound(NULL, sfx_menu1); + } + else if (menucmd[pid].dpad_lr > 0) // right + { + keyboardx++; + if (!virtualKeyboard[keyboardy][keyboardx]) + keyboardx = 0; + + M_SetMenuDelay(pid); + S_StartSound(NULL, sfx_menu1); + } + else if (menucmd[pid].dpad_lr < 0) // left + { + keyboardx--; + if (keyboardx < 0) + { + keyboardx = 13; + M_UpdateKeyboardX(); + } + M_SetMenuDelay(pid); + S_StartSound(NULL, sfx_menu1); + } + else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + { + // Add the character. First though, check what we're pressing.... + INT16 c = virtualKeyboard[keyboardy][keyboardx]; + if (keyboardshift ^ keyboardcapslock) + c = shift_virtualKeyboard[keyboardy][keyboardx]; + + if (c == KEY_RSHIFT) + keyboardshift = !keyboardshift; + else if (c == KEY_CAPSLOCK) + keyboardcapslock = !keyboardcapslock; + else if (c == KEY_ENTER) + { + menutypingclose = true; // close menu. + return; + } + else + { + M_ChangeStringCvar((INT32)c); // Write! + keyboardshift = false; // undo shift if it had been pressed + } + + M_SetMenuDelay(pid); + S_StartSound(NULL, sfx_menu1); + } + } +} + static void M_HandleMenuInput(void) { void (*routine)(INT32 choice); // for some casting problem @@ -1149,6 +1348,13 @@ static void M_HandleMenuInput(void) return; } + // Typing for CV_IT_STRING + if (menutyping) + { + M_MenuTypingInput(menuKey); + return; + } + if (menucmd[pid].delay > 0) { return; @@ -1210,8 +1416,18 @@ static void M_HandleMenuInput(void) { if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_STRING) { - // As mentioned earlier, we need a typing submenu. + // Routine is null either way routine = NULL; + + // If we're hovering over a IT_CV_STRING option, pressing A/X opens the typing submenu + if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + { + keyboardtyping = menuKey != 0 ? true : false; // If we entered this menu by pressing a menu Key, default to keyboard typing, otherwise use controller. + menutyping = true; + menutypingclose = false; + return; + } + } else { @@ -1467,6 +1683,9 @@ void M_Init(void) CV_RegisterVar(&cv_dummygametype); CV_RegisterVar(&cv_dummyip); + CV_RegisterVar(&cv_dummyprofilename); + CV_RegisterVar(&cv_dummyprofileplayername); + CV_RegisterVar(&cv_dummygpdifficulty); CV_RegisterVar(&cv_dummykartspeed); CV_RegisterVar(&cv_dummygpencore); From 5601fa4c22a5a84cfedabbdb0eca63e0a90fea11 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 18 Feb 2022 13:46:32 +0100 Subject: [PATCH 214/379] profiles: character/colour select --- src/k_menu.h | 2 + src/k_menudef.c | 6 +-- src/k_menudraw.c | 136 +++++++++++++++++++++++++++++------------------ src/k_menufunc.c | 78 +++++++++++++++++++++++---- 4 files changed, 158 insertions(+), 64 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index a7b7a86d6..6bf48a642 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -658,6 +658,8 @@ void M_EraseData(INT32 choice); // For data erasing // profile selection menu void M_ProfileSelectInit(INT32 choice); void M_HandleProfileSelect(INT32 ch); + +void M_HandleProfileEdit(void); boolean M_ProfileEditInputs(INT32 ch); // video modes menu (resolution) diff --git a/src/k_menudef.c b/src/k_menudef.c index 0d0353a42..b82410734 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -438,8 +438,8 @@ menuitem_t OPTIONS_EditProfile[] = { {IT_STRING | IT_CVAR | IT_CV_STRING, "Player Name", "Name displayed online when using this Profile.", NULL, &cv_dummyprofileplayername, 0, 0}, - {IT_STRING | IT_SUBMENU, "Character", "Default character and color for this Profile.", - NULL, NULL, 0, 0}, + {IT_STRING | IT_CALL, "Character", "Default character and color for this Profile.", + NULL, M_CharacterSelect, 0, 0}, {IT_STRING | IT_SUBMENU, "Controls", "Select the button mappings for this Profile.", NULL, NULL, 0, 0}, @@ -454,7 +454,7 @@ menu_t OPTIONS_EditProfileDef = { SKINCOLOR_ULTRAMARINE, 0, 2, 10, M_DrawEditProfile, - M_OptionsTick, + M_HandleProfileEdit, NULL, NULL, M_ProfileEditInputs, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 602738929..9e89c8cd6 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -834,7 +834,8 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) } } -static void M_DrawCharacterSprite(INT16 x, INT16 y, SINT8 skin, INT32 addflags, UINT8 *colormap) +// returns false if the character couldn't be rendered +static boolean M_DrawCharacterSprite(INT16 x, INT16 y, SINT8 skin, INT32 addflags, UINT8 *colormap) { UINT8 spr; spritedef_t *sprdef; @@ -848,7 +849,7 @@ static void M_DrawCharacterSprite(INT16 x, INT16 y, SINT8 skin, INT32 addflags, sprdef = &skins[skin].sprites[spr]; if (!sprdef->numframes) // No frames ?? - return; // Can't render! + return false; // Can't render! frame = states[S_KART_FAST].frame & FF_FRAMEMASK; if (frame >= sprdef->numframes) // Walking animation missing @@ -869,6 +870,8 @@ static void M_DrawCharacterSprite(INT16 x, INT16 y, SINT8 skin, INT32 addflags, } else V_DrawMappedPatch(x, y, addflags|flags, sprpatch, colormap); + + return true; } static void M_DrawCharSelectSprite(UINT8 num, INT16 x, INT16 y) @@ -987,8 +990,12 @@ static void M_DrawCharSelectCursor(UINT8 num) quadx = 4 * (p->gridx / 3); quady = 4 * (p->gridy / 3); - x = 82 + (p->gridx*16) + quadx - 13, - y = 22 + (p->gridy*16) + quady - 12, + x = 82 + (p->gridx*16) + quadx - 13; + y = 22 + (p->gridy*16) + quady - 12; + + // profiles skew the graphic to the right slightly + if (optionsmenu.profile) + x += 64; colormap = R_GetTranslationColormap(TC_DEFAULT, (p->color != SKINCOLOR_NONE ? p->color : SKINCOLOR_GREY), GTC_MENUCACHE); @@ -1015,12 +1022,77 @@ static void M_DrawCharSelectCursor(UINT8 num) #undef IDLELEN #undef SELECTLEN +// Draw character profile card. +// Moved here because in the case of profile edition this is drawn in the charsel menu. +static void M_DrawProfileCard(INT32 x, INT32 y, profile_t *p) +{ + + setup_player_t *sp = &setup_player[0]; // When editing profile character, we'll always be checking for what P1 is doing. + patch_t *card = W_CachePatchName("PR_CARD", PU_CACHE); + patch_t *cardbot = W_CachePatchName("PR_CARDB", PU_CACHE); + patch_t *pwrlv = W_CachePatchName("PR_PWR", PU_CACHE); + UINT8 *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_BLACK, GTC_CACHE); + INT32 skinnum = -1; + INT32 powerlevel = -1; + + char pname[PROFILENAMELEN+1] = "empty"; + + if (p != NULL && p->version) + { + colormap = R_GetTranslationColormap(TC_DEFAULT, p->color, GTC_CACHE); + strcpy(pname, p->profilename); + skinnum = R_SkinAvailable(p->skinname); + powerlevel = 1000; // Test + } + + // check setup_player for colormap for the card. + // we'll need to check again for drawing afterwards unfortunately. + if (sp->mdepth >= CSSTEP_CHARS) + colormap = R_GetTranslationColormap(skinnum, sp->color, GTC_MENUCACHE); + + // Card + V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, card, colormap); + + // Draw pwlv if we can + if (powerlevel > -1) + { + V_DrawFixedPatch((x+30)*FRACUNIT, (y+84)*FRACUNIT, FRACUNIT, 0, pwrlv, colormap); + V_DrawCenteredKartString(x+30, y+87, 0, va("%d\n", powerlevel)); + } + + // check what setup_player is doing in priority. + if (sp->mdepth >= CSSTEP_CHARS) + { + skinnum = setup_chargrid[sp->gridx][sp->gridy].skinlist[sp->clonenum]; + + + if (M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap)) + V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap); + + if (sp->mdepth == CSSTEP_ALTS || sp->mdepth == CSSTEP_COLORS) + { + M_DrawCharSelectCircle(sp, x-22, y+104); + } + } + else if (skinnum > -1) // otherwise, read from profile. + { + if (M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap)) + V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap); + } + + V_DrawCenteredGamemodeString(x, y+24, 0, 0, pname); + + // Card bottom to overlay the skin preview + V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, cardbot, colormap); +} + void M_DrawCharacterSelect(void) { UINT8 i, j, k; UINT8 priority = 0; INT16 quadx, quady; SINT8 skin; + INT32 basex = optionsmenu.profile != NULL ? 64 : 0; if (setup_numplayers > 0) { @@ -1047,12 +1119,12 @@ void M_DrawCharacterSelect(void) } if (skin != -1) - V_DrawScaledPatch(82 + (i*16) + quadx + 1, 22 + (j*16) + quady + 1, 0, W_CachePatchName("ICONBACK", PU_CACHE)); + V_DrawScaledPatch(basex+ 82 + (i*16) + quadx + 1, 22 + (j*16) + quady + 1, 0, W_CachePatchName("ICONBACK", PU_CACHE)); } } // Draw this inbetween. These drop shadows should be covered by the stat graph, but the icons shouldn't. - V_DrawScaledPatch(3, 2, 0, W_CachePatchName("STATGRPH", PU_CACHE)); + V_DrawScaledPatch(basex+ 3, 2, 0, W_CachePatchName((optionsmenu.profile ? "PR_STGRPH" : "STATGRPH"), PU_CACHE)); // Draw the icons now for (i = 0; i < 9; i++) @@ -1078,10 +1150,10 @@ void M_DrawCharacterSelect(void) else colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE); - V_DrawMappedPatch(82 + (i*16) + quadx, 22 + (j*16) + quady, 0, faceprefix[skin][FACE_RANK], colormap); + V_DrawMappedPatch(basex + 82 + (i*16) + quadx, 22 + (j*16) + quady, 0, faceprefix[skin][FACE_RANK], colormap); if (setup_chargrid[i][j].numskins > 1) - V_DrawScaledPatch(82 + (i*16) + quadx, 22 + (j*16) + quady + 11, 0, W_CachePatchName("ALTSDOT", PU_CACHE)); + V_DrawScaledPatch(basex + 82 + (i*16) + quadx, 22 + (j*16) + quady + 11, 0, W_CachePatchName("ALTSDOT", PU_CACHE)); } } } @@ -1092,7 +1164,10 @@ void M_DrawCharacterSelect(void) for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { // Draw a preview for each player - M_DrawCharSelectPreview(i); + if (optionsmenu.profile == NULL) + M_DrawCharSelectPreview(i); + else if (i == 0) + M_DrawProfileCard(optionsmenu.optx, optionsmenu.opty, optionsmenu.profile); if (i >= setup_numplayers) continue; @@ -2094,49 +2169,6 @@ void M_DrawGenericOptions(void) } } -static void M_DrawProfileCard(INT32 x, INT32 y, profile_t *p) -{ - - patch_t *card = W_CachePatchName("PR_CARD", PU_CACHE); - patch_t *cardbot = W_CachePatchName("PR_CARDB", PU_CACHE); - patch_t *pwrlv = W_CachePatchName("PR_PWR", PU_CACHE); - UINT8 *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_BLACK, GTC_CACHE); - INT32 skinnum = -1; - INT32 powerlevel = -1; - - char pname[PROFILENAMELEN+1] = "empty"; - - if (p != NULL && p->version) - { - colormap = R_GetTranslationColormap(TC_DEFAULT, p->color, GTC_CACHE); - strcpy(pname, p->profilename); - skinnum = R_SkinAvailable(p->skinname); - powerlevel = 1000; // Test - } - - // Card - V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, card, colormap); - - // Draw pwlv if we can - if (powerlevel > -1) - { - V_DrawFixedPatch((x+30)*FRACUNIT, (y+84)*FRACUNIT, FRACUNIT, 0, pwrlv, colormap); - V_DrawCenteredKartString(x+30, y+87, 0, va("%d\n", powerlevel)); - } - - - if (skinnum > -1) - { - M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap); - V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap); - } - - V_DrawCenteredGamemodeString(x, y+24, 0, 0, pname); - - // Card bottom to overlay the skin preview - V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, cardbot, colormap); -} - // Draws profile selection void M_DrawProfileSelect(void) { diff --git a/src/k_menufunc.c b/src/k_menufunc.c index ecc5af6a9..36dd9fa89 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1990,10 +1990,13 @@ void M_CharacterSelectInit(void) { UINT8 i, j; + // While we're editing profiles, don't unset the devices for p1 for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { // Un-set devices for other players. - CV_SetValue(&cv_usejoystick[i], -1); + if (i != 0 || optionsmenu.profile) + CV_SetValue(&cv_usejoystick[i], -1); + CONS_Printf("Device for %d set to %d\n", i, -1); } CONS_Printf("========\n"); @@ -2031,6 +2034,11 @@ void M_CharacterSelectInit(void) setup_player[j].gridx = x; setup_player[j].gridy = y; setup_player[j].color = skins[i].prefcolor; + + // If we're on prpfile select, skip straight to CSSTEP_CHARS + if (optionsmenu.profile && j == 0) + setup_player[j].mdepth = CSSTEP_CHARS; + } } } @@ -2243,8 +2251,18 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) { if (num == setup_numplayers-1) { - p->mdepth = CSSTEP_NONE; - S_StartSound(NULL, sfx_s3k5b); + // for profiles, exit out of the menu instantly, + // we don't want to go to the input detection menu. + if (optionsmenu.profile) + { + M_GoBack(0); + return true; + } + else // for the actual player select, go back to device detection. + { + p->mdepth = CSSTEP_NONE; + S_StartSound(NULL, sfx_s3k5b); + } // Prevent quick presses for multiple players for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) @@ -2490,14 +2508,29 @@ void M_CharacterSelectTick(void) // Selecting from the menu if (gamestate == GS_MENU) { - for (i = 0; i < setup_numplayers; i++) + // in a profile; update the selected profile and then go back to the profile menu. + if (optionsmenu.profile) { - CV_StealthSet(&cv_skin[i], skins[setup_player[i].skin].name); - CV_StealthSetValue(&cv_playercolor[i], setup_player[i].color); - } + strcpy(optionsmenu.profile->skinname, skins[setup_player[0].skin].name); + optionsmenu.profile->color = setup_player[0].color; - CV_StealthSetValue(&cv_splitplayers, setup_numplayers); - M_SetupNextMenu(&PLAY_MainDef, false); + // reset setup_player + memset(setup_player, 0, sizeof(setup_player)); + + M_GoBack(0); + return; + } + else // in a normal menu, stealthset the cvars and then go to the play menu. + { + for (i = 0; i < setup_numplayers; i++) + { + CV_StealthSet(&cv_skin[i], skins[setup_player[i].skin].name); + CV_StealthSetValue(&cv_playercolor[i], setup_player[i].color); + } + + CV_StealthSetValue(&cv_splitplayers, setup_numplayers); + M_SetupNextMenu(&PLAY_MainDef, false); + } } else // In a game { @@ -3557,6 +3590,18 @@ void M_HandleProfileSelect(INT32 ch) optionsmenu.toptx = 130/2; optionsmenu.topty = 0; + // setup cvars + if (optionsmenu.profile->version) + { + CV_StealthSet(&cv_dummyprofilename, optionsmenu.profile->profilename); + CV_StealthSet(&cv_dummyprofileplayername, optionsmenu.profile->playername); + } + else + { + CV_StealthSet(&cv_dummyprofilename, ""); + CV_StealthSet(&cv_dummyprofileplayername, ""); + } + M_SetupNextMenu(&OPTIONS_EditProfileDef, false); } @@ -3592,6 +3637,21 @@ boolean M_ProfileEditInputs(INT32 ch) return false; } +// Handle some actions in profile editing +void M_HandleProfileEdit(void) +{ + M_OptionsTick(); // Keep running that ticker normally. + + // Always copy the profile name and player name in the profile. + + // Copy the first 6 chars for profile name + if (strlen(cv_dummyprofilename.string)) + strncpy(optionsmenu.profile->profilename, cv_dummyprofilename.string, PROFILENAMELEN); + + if (strlen(cv_dummyprofileplayername.string)) + strcpy(optionsmenu.profile->playername, cv_dummyprofileplayername.string); +} + // special menuitem key handler for video mode list void M_HandleVideoModes(INT32 ch) { From ebc2156b7e4f7e7808ca2626ac972d8817fd04be Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 19 Feb 2022 13:26:02 +0100 Subject: [PATCH 215/379] Profiles: control setup (wip but does what it needs to for now) --- src/k_menu.h | 26 +++++++++ src/k_menudef.c | 94 ++++++++++++++++++++++++++++++- src/k_menudraw.c | 120 +++++++++++++++++++++++++++++++++++++++ src/k_menufunc.c | 142 +++++++++++++++++++++++++++++++++++++++++++++-- src/k_profiles.c | 2 +- 5 files changed, 377 insertions(+), 7 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 6bf48a642..6e7b1589e 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -20,6 +20,7 @@ #include "doomstat.h" // MAXSPLITSCREENPLAYERS #include "g_demo.h" //menudemo_t #include "k_profiles.h" // profile data & functions +#include "g_input.h" // gc_ // flags for items in the menu // menu handle (what we do when key is pressed @@ -226,6 +227,9 @@ extern menu_t OPTIONS_ProfilesDef; extern menuitem_t OPTIONS_EditProfile[]; extern menu_t OPTIONS_EditProfileDef; +extern menuitem_t OPTIONS_ProfileControls[]; +extern menu_t OPTIONS_ProfileControlsDef; + extern menuitem_t OPTIONS_Video[]; extern menu_t OPTIONS_VideoDef; @@ -626,6 +630,17 @@ extern struct optionsmenu_s { boolean resetprofile; // After going back from the edit menu, this tells the profile select menu to kill the profile data after the transition. profile_t *profile; // Pointer to the profile we're editing + INT16 controlscroll; // scrolling for the control menu.... + UINT8 bindcontrol; // 0: not binding, 1: binding control #1, 2: binding control #2 + INT16 bindtimer; // Timer until binding is cancelled (5s) + + // controller coords... + // Works the same as (t)opt + INT16 contx; + INT16 conty; + INT16 tcontx; + INT16 tconty; + // for video mode testing: INT32 vidm_testingmode; INT32 vidm_previousmode; @@ -643,6 +658,8 @@ extern struct optionsmenu_s { tic_t fade; } optionsmenu; +extern INT16 controlleroffsets[][2]; + extern consvar_t cv_dummyprofilename; extern consvar_t cv_dummyprofileplayername; @@ -659,9 +676,17 @@ void M_EraseData(INT32 choice); // For data erasing void M_ProfileSelectInit(INT32 choice); void M_HandleProfileSelect(INT32 ch); +// profile edition void M_HandleProfileEdit(void); +void M_ProfileDeviceSelect(INT32 choice); boolean M_ProfileEditInputs(INT32 ch); +void M_HandleProfileControls(void); +boolean M_ProfileControlsInputs(INT32 ch); +void M_ProfileSetControl(INT32 ch); + +void M_MapProfileControl(event_t *ev); + // video modes menu (resolution) void M_VideoModeMenu(INT32 choice); void M_HandleVideoModes(INT32 ch); @@ -802,6 +827,7 @@ void M_DrawOptions(void); void M_DrawGenericOptions(void); void M_DrawProfileSelect(void); void M_DrawEditProfile(void); +void M_DrawProfileControls(void); void M_DrawVideoModes(void); void M_DrawItemToggles(void); diff --git a/src/k_menudef.c b/src/k_menudef.c index b82410734..aa68b816d 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -441,8 +441,8 @@ menuitem_t OPTIONS_EditProfile[] = { {IT_STRING | IT_CALL, "Character", "Default character and color for this Profile.", NULL, M_CharacterSelect, 0, 0}, - {IT_STRING | IT_SUBMENU, "Controls", "Select the button mappings for this Profile.", - NULL, NULL, 0, 0}, + {IT_STRING | IT_CALL, "Controls", "Select the button mappings for this Profile.", + NULL, M_ProfileDeviceSelect, 0, 0}, }; menu_t OPTIONS_EditProfileDef = { @@ -460,6 +460,96 @@ menu_t OPTIONS_EditProfileDef = { M_ProfileEditInputs, }; +menuitem_t OPTIONS_ProfileControls[] = { + + {IT_HEADER, "MAIN CONTROLS", "That's the stuff on the controller!!", + NULL, NULL, 0, 0}, + + {IT_CONTROL, "A", "Accelerate / Confirm", + "PR_BTA", M_ProfileSetControl, gc_a, 0}, + + {IT_CONTROL, "B", "Look backwards / Back", + "PR_BTB", M_ProfileSetControl, gc_b, 0}, + + {IT_CONTROL, "C", "Spindash", + "PR_BTC", M_ProfileSetControl, gc_c, 0}, + + {IT_CONTROL, "X", "Brake", + "PR_BTX", M_ProfileSetControl, gc_x, 0}, + + // @TODO What does this do??? + {IT_CONTROL, "Y", "We just don't know", + "PR_BTY", M_ProfileSetControl, gc_y, 0}, + + {IT_CONTROL, "Z", "We just don't know", + "PR_BTZ", M_ProfileSetControl, gc_z, 0}, + + {IT_CONTROL, "L", "Use item", + "PR_BTL", M_ProfileSetControl, gc_l, 0}, + + {IT_CONTROL, "R", "Drift", + "PR_BTR", M_ProfileSetControl, gc_r, 0}, + + {IT_CONTROL, "Turn Left", "Turn left", + "PR_PADL", M_ProfileSetControl, gc_left, 0}, + + {IT_CONTROL, "Turn Right", "Turn right", + "PR_PADR", M_ProfileSetControl, gc_right, 0}, + + {IT_CONTROL, "Aim Forward", "Aim forwards", + "PR_PADU", M_ProfileSetControl, gc_up, 0}, + + {IT_CONTROL, "Aim Backwards", "Aim backwards", + "PR_PADD", M_ProfileSetControl, gc_down, 0}, + + {IT_CONTROL, "Start", "Open pause menu", + "PR_BTS", M_ProfileSetControl, gc_start, 0}, + + {IT_HEADER, "OPTIONAL CONTROLS", "Take a screenshot, chat...", + NULL, NULL, 0, 0}, + + {IT_CONTROL, "SCREENSHOT", "Also usable with F8 on Keyboard.", + NULL, M_ProfileSetControl, gc_screenshot, 0}, + + {IT_CONTROL, "GIF CAPTURE", "Also usable with F9 on Keyboard.", + NULL, M_ProfileSetControl, gc_recordgif, 0}, + + {IT_CONTROL, "OPEN CHAT", "Opens chatbox in online games.", + NULL, M_ProfileSetControl, gc_talk, 0}, + + {IT_CONTROL, "OPEN TEAM CHAT", "Do we even have team gamemodes?", + NULL, M_ProfileSetControl, gc_teamtalk, 0}, + + {IT_CONTROL, "OPEN CONSOLE", "Also usable with ` on Keyboard.", + NULL, M_ProfileSetControl, gc_console, 0}, + + {IT_CONTROL, "LUA/A", "May be used by add-ons.", + NULL, M_ProfileSetControl, gc_luaa, 0}, + + {IT_CONTROL, "LUA/B", "May be used by add-ons.", + NULL, M_ProfileSetControl, gc_luab, 0}, + + {IT_CONTROL, "LUA/C", "May be used by add-ons.", + NULL, M_ProfileSetControl, gc_luac, 0}, +}; + + + +menu_t OPTIONS_ProfileControlsDef = { + sizeof (OPTIONS_ProfileControls) / sizeof (menuitem_t), + &OPTIONS_EditProfileDef, + 0, + OPTIONS_ProfileControls, + 32, 80, + SKINCOLOR_ULTRAMARINE, 0, + 3, 10, + M_DrawProfileControls, + M_HandleProfileControls, + NULL, + NULL, + M_ProfileControlsInputs, +}; + // video options menu... // options menu menuitem_t OPTIONS_Video[] = diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 9e89c8cd6..a3a989040 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2268,6 +2268,126 @@ void M_DrawEditProfile(void) } } +// Controller offsets to center on each button. +INT16 controlleroffsets[][2] = { + {0, 0}, // gc_none + {70, 112}, // gc_up + {70, 112}, // gc_down + {70, 112}, // gc_left + {70, 112}, // gc_right + {208, 200}, // gc_a + {237, 181}, // gc_b + {267, 166}, // gc_c + {191, 164}, // gc_x + {215, 149}, // gc_y + {242, 137}, // gc_z + {55, 102}, // gc_l + {253, 102}, // gc_r + {149, 187}, // gc_start +}; + +// the control stuff. +// Dear god. +void M_DrawProfileControls(void) +{ + const UINT8 spacing = 34; + INT32 y = 16 - (optionsmenu.controlscroll*spacing); + INT32 x = 8; + INT32 i, j; + + M_DrawOptionsCogs(); + + // @TODO: have it move around and shit. + V_DrawScaledPatch(BASEVIDWIDTH*2/3 - optionsmenu.contx, BASEVIDHEIGHT/2 -optionsmenu.conty, 0, W_CachePatchName("PR_CONT", PU_CACHE)); + + // Tooltip + // The text is slightly shifted hence why we don't just use M_DrawMenuTooltips() + V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL); + if (currentMenu->menuitems[itemOn].tooltip != NULL) + { + V_DrawCenteredThinString(BASEVIDWIDTH*2/3, 12, V_ALLOWLOWERCASE|V_6WIDTHSPACE, currentMenu->menuitems[itemOn].tooltip); + } + + V_DrawFill(0, 0, 138, 200, 31); // Black border + + // Draw the menu options... + for (i = 0; i < currentMenu->numitems; i++) + { + char buf[256]; + INT32 keys[2]; + + // cursor + if (i == itemOn) + { + for (j=0; j < 24; j++) + V_DrawFill(0, (y)+j, 128+j, 1, 73); + } + + switch (currentMenu->menuitems[i].status & IT_DISPLAY) + { + case IT_HEADERTEXT: + V_DrawFill(0, y+17, 124, 1, 0); // underline + V_DrawString(x, y+8, 0, currentMenu->menuitems[i].text); + y += spacing; + break; + + case IT_STRING2: + + if (currentMenu->menuitems[i].patch) + V_DrawScaledPatch(x+12, y+12, 0, W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE)); + else + V_DrawString(x, y+1, (i == itemOn ? highlightflags : 0), currentMenu->menuitems[i].text); + + if (currentMenu->menuitems[i].status & IT_CONTROL) + { + // Draw what the controls are mapped to + keys[0] = optionsmenu.profile->controls[currentMenu->menuitems[i].mvar1][0]; + keys[1] = optionsmenu.profile->controls[currentMenu->menuitems[i].mvar1][1]; + + buf[0] = '\0'; + + if (keys[0] == KEY_NULL && keys[1] == KEY_NULL) + strcpy(buf, "\x85NOT BOUND"); + else + { + if (keys[0] != KEY_NULL) + strcat (buf, G_KeynumToString (keys[0])); + + if (keys[0] != KEY_NULL && keys[1] != KEY_NULL) + strcat(buf," / "); + + if (keys[1] != KEY_NULL) + strcat (buf, G_KeynumToString (keys[1])); + } + + V_DrawThinString(x+32, y+12, V_6WIDTHSPACE, buf); + + // controller dest coords: + if (itemOn == i && currentMenu->menuitems[i].mvar1 && currentMenu->menuitems[i].mvar1 <= gc_start) + { + optionsmenu.tcontx = controlleroffsets[currentMenu->menuitems[i].mvar1][0]; + optionsmenu.tconty = controlleroffsets[currentMenu->menuitems[i].mvar1][1]; + } + } + + y += spacing; + break; + } + } + + // Overlay for control binding + if (optionsmenu.bindcontrol) + { + V_DrawFadeScreen(31, 8); + + M_DrawTextBox((BASEVIDWIDTH/2) - (120), (BASEVIDHEIGHT/2) - (16), 30, 4); + + V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2) - (4), 0, va("Press key #%d for control", optionsmenu.bindcontrol)); + V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2) - (4) +10, 0, va("\"%s\"", currentMenu->menuitems[itemOn].text)); + V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2) - (4) +20, highlightflags, va("(WAIT %d SECONDS TO SKIP)", optionsmenu.bindtimer/TICRATE)); + } +} + // Draw the video modes list, a-la-Quake void M_DrawVideoModes(void) { diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 36dd9fa89..22b16206c 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -831,6 +831,14 @@ boolean M_Responder(event_t *ev) // update keys current state G_MapEventsToControls(ev); + // Profiles: Control mapping. + // We take the WHOLE EVENT for convenience. + if (optionsmenu.bindcontrol) + { + M_MapProfileControl(ev); + return true; // eat events. + } + // Handle menu handling in-game. if (menuactive == false) { @@ -1399,13 +1407,14 @@ static void M_HandleMenuInput(void) } else { -#if 0 // this shit is crazy +//#if 0 // this shit is crazy if (routine) { - void (*otherroutine)(event_t *sev) = currentMenu->menuitems[itemOn].itemaction; - otherroutine(ev); //Alam: what a hack + //void (*otherroutine)(event_t *sev) = currentMenu->menuitems[itemOn].itemaction; + //otherroutine(menuKey); //Alam: what a hack + routine(menuKey); } -#endif +//#endif return; } @@ -3744,6 +3753,131 @@ void M_HandleVideoModes(INT32 ch) } } +static void M_ProfileDeviceSelectResponse(INT32 key) +{ + UINT8 i; + (void) key; + + for (i=0; i < MAXDEVICES; i++) + { + if (deviceResponding[i]) + { + CV_SetValue(&cv_usejoystick[0], i); // Force-set this joystick as the current joystick we're using for P1 (which is the only one controlling menus) + CONS_Printf("Using device %d for mappings\n", i); + M_SetupNextMenu(&OPTIONS_ProfileControlsDef, false); // with that set, send us to the control def + return; + } + } +} + +// Prompt a device selection window (just tap any button on the device you want) +void M_ProfileDeviceSelect(INT32 choice) +{ + (void)choice; + + // While we're here, setup the incoming controls menu to reset the scroll & bind status: + optionsmenu.controlscroll = 0; + optionsmenu.bindcontrol = 0; + optionsmenu.bindtimer = 0; + + optionsmenu.contx = optionsmenu.tcontx = controlleroffsets[gc_a][0]; + optionsmenu.conty = optionsmenu.tconty = controlleroffsets[gc_a][1]; + + M_StartMessage(M_GetText("Press any key on the device\nyou would like to use"), M_ProfileDeviceSelectResponse, MM_EVENTHANDLER); +} + +void M_HandleProfileControls(void) +{ + UINT8 maxscroll = currentMenu->numitems - 5; + M_OptionsTick(); + + optionsmenu.contx += (optionsmenu.tcontx - optionsmenu.contx)/2; + optionsmenu.conty += (optionsmenu.tconty - optionsmenu.conty)/2; + + if (abs(optionsmenu.contx - optionsmenu.tcontx) < 2 && abs(optionsmenu.conty - optionsmenu.tconty) < 2) + { + optionsmenu.contx = optionsmenu.tcontx; + optionsmenu.conty = optionsmenu.tconty; // Avoid awkward 1 px errors. + } + + optionsmenu.controlscroll = itemOn - 3; // very barebones scrolling, but it works just fine for our purpose. + if (optionsmenu.controlscroll > maxscroll) + optionsmenu.controlscroll = maxscroll; + + if (optionsmenu.controlscroll < 0) + optionsmenu.controlscroll = 0; + + // bindings, cancel if timer is depleted. + if (optionsmenu.bindcontrol) + { + optionsmenu.bindtimer--; + if (!optionsmenu.bindtimer) + { + optionsmenu.bindcontrol++; + if (optionsmenu.bindcontrol > 2) + optionsmenu.bindcontrol = 0; // we've gone past the max, just stop. + else + optionsmenu.bindtimer = TICRATE*5; // skip control + } + + } +} + +boolean M_ProfileControlsInputs(INT32 ch) +{ + (void)ch; + + // By default, accept all inputs. + if (optionsmenu.bindcontrol) + return true; // Eat all inputs there. We'll use a stupid hack in M_Responder instead. + + return false; +} + +void M_ProfileSetControl(INT32 ch) +{ + (void) ch; + + optionsmenu.bindcontrol = 1; + optionsmenu.bindtimer = TICRATE*5; +} + +// Map the event to the profile. +void M_MapProfileControl(event_t *ev) +{ + INT32 c = ev->data1; + UINT8 n = optionsmenu.bindcontrol-1; // # of input to bind + INT32 controln = currentMenu->menuitems[itemOn].mvar1; // gc_ + UINT8 where = n; // By default, we'll save the bind where we're supposed to map. + + // Only consider keydown events to make sure we ignore ev_mouse and ev_joystick as well + if (ev->type != ev_keydown) + return; + + // Check if this control is already assigned, it'd look silly to assign the same key twice on the same thing. + if (n == 0 && optionsmenu.profile->controls[controln][1] == c) + { + optionsmenu.profile->controls[controln][1] = KEY_NULL; // unbind + where = 0; // save control in slot 0 + } + else if (n == 1 && optionsmenu.profile->controls[controln][0] == c) + { + // Do nothing and exit this menu. + optionsmenu.bindcontrol = 0; + return; + } + + optionsmenu.profile->controls[controln][where] = c; + + optionsmenu.bindcontrol++; + optionsmenu.bindtimer = TICRATE*5; + if (optionsmenu.bindcontrol > 2) + { + optionsmenu.bindtimer = 0; + optionsmenu.bindcontrol = 0; + } +} + void M_HandleItemToggles(INT32 choice) { const INT32 width = 9, height = 3; diff --git a/src/k_profiles.c b/src/k_profiles.c index d87bb79da..a07c7b803 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -23,7 +23,7 @@ profile_t PR_MakeProfile(const char *prname, const char *pname, const char *snam new.version = PROFILEVER; strcpy(new.profilename, prname); - + strcpy(new.skinname, sname); strcpy(new.playername, pname); new.color = col; From 2e4a58095add9d18cdfe158107d62bdbd562cad5 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 19 Feb 2022 13:31:34 +0100 Subject: [PATCH 216/379] Allow ev_joystick for button mappings, oops --- src/k_menufunc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 22b16206c..03d450e8b 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3850,8 +3850,8 @@ void M_MapProfileControl(event_t *ev) INT32 controln = currentMenu->menuitems[itemOn].mvar1; // gc_ UINT8 where = n; // By default, we'll save the bind where we're supposed to map. - // Only consider keydown events to make sure we ignore ev_mouse and ev_joystick as well - if (ev->type != ev_keydown) + // Only consider keydown and joystick events to make sure we ignore ev_mouse and other events + if (ev->type != ev_keydown && ev->type != ev_joystick) return; // Check if this control is already assigned, it'd look silly to assign the same key twice on the same thing. From debc93ff5c02582d38bb600c492ba3efeddbe0d0 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 19 Feb 2022 13:43:21 +0100 Subject: [PATCH 217/379] Make sure to reset setup_player when backing out to not interfere with profile display --- src/k_menufunc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 03d450e8b..4c40c071d 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2142,6 +2142,7 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num) if (num == 0) { // We're done here. + memset(setup_player, 0, sizeof(setup_player)); // Reset this to avoid funky things with profile display. M_GoBack(0); return true; } @@ -2264,6 +2265,7 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) // we don't want to go to the input detection menu. if (optionsmenu.profile) { + memset(setup_player, 0, sizeof(setup_player)); // Reset setup_player otherwise it does some VERY funky things. M_GoBack(0); return true; } From 3aadf1bff04835382a476b75decc273b9f77868e Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 19 Feb 2022 16:08:50 +0100 Subject: [PATCH 218/379] Allow creating new profiles --- src/k_menudraw.c | 17 ++++++++++------- src/k_menufunc.c | 13 +++++++++---- src/k_profiles.c | 16 ++++++++++++++++ src/k_profiles.h | 7 +++++++ 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index a3a989040..d4e8d653b 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1024,9 +1024,8 @@ static void M_DrawCharSelectCursor(UINT8 num) // Draw character profile card. // Moved here because in the case of profile edition this is drawn in the charsel menu. -static void M_DrawProfileCard(INT32 x, INT32 y, profile_t *p) +static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) { - setup_player_t *sp = &setup_player[0]; // When editing profile character, we'll always be checking for what P1 is doing. patch_t *card = W_CachePatchName("PR_CARD", PU_CACHE); patch_t *cardbot = W_CachePatchName("PR_CARDB", PU_CACHE); @@ -1051,7 +1050,10 @@ static void M_DrawProfileCard(INT32 x, INT32 y, profile_t *p) colormap = R_GetTranslationColormap(skinnum, sp->color, GTC_MENUCACHE); // Card - V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, card, colormap); + V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, greyedout ? V_TRANSLUCENT : 0, card, colormap); + + if (greyedout) + return; // only used for profiles we can't select. // Draw pwlv if we can if (powerlevel > -1) @@ -1167,7 +1169,7 @@ void M_DrawCharacterSelect(void) if (optionsmenu.profile == NULL) M_DrawCharSelectPreview(i); else if (i == 0) - M_DrawProfileCard(optionsmenu.optx, optionsmenu.opty, optionsmenu.profile); + M_DrawProfileCard(optionsmenu.optx, optionsmenu.opty, false, optionsmenu.profile); if (i >= setup_numplayers) continue; @@ -2173,6 +2175,7 @@ void M_DrawGenericOptions(void) void M_DrawProfileSelect(void) { INT32 i; + const INT32 maxp = PR_GetNumProfiles(); INT32 x = 160 - optionsmenu.profilen*(128 + 128/8) + optionsmenu.offset; INT32 y = 35 + menutransition.tics*16; @@ -2189,14 +2192,14 @@ void M_DrawProfileSelect(void) // don't draw the card in this specific scenario if (!(menutransition.tics && optionsmenu.profile != NULL && optionsmenu.profilen == i)) - M_DrawProfileCard(x, y, p); + M_DrawProfileCard(x, y, i > maxp, p); x += 128 + 128/8; } // needs to be drawn since it happens on the transition if (optionsmenu.profile != NULL) - M_DrawProfileCard(optionsmenu.optx, optionsmenu.opty, optionsmenu.profile); + M_DrawProfileCard(optionsmenu.optx, optionsmenu.opty, false, optionsmenu.profile); } @@ -2264,7 +2267,7 @@ void M_DrawEditProfile(void) // Finally, draw the card ontop if (optionsmenu.profile != NULL) { - M_DrawProfileCard(optionsmenu.optx, optionsmenu.opty, optionsmenu.profile); + M_DrawProfileCard(optionsmenu.optx, optionsmenu.opty, false, optionsmenu.profile); } } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 4c40c071d..3e034bada 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3558,6 +3558,7 @@ void M_VideoModeMenu(INT32 choice) void M_HandleProfileSelect(INT32 ch) { const UINT8 pid = 0; + const INT32 maxp = PR_GetNumProfiles(); (void) ch; if (menucmd[pid].dpad_lr > 0) @@ -3565,10 +3566,10 @@ void M_HandleProfileSelect(INT32 ch) optionsmenu.profilen++; optionsmenu.offset += (128 + 128/8); - if (optionsmenu.profilen > MAXPROFILES) + if (optionsmenu.profilen > maxp) { optionsmenu.profilen = 0; - optionsmenu.offset -= (128 + 128/8)*(MAXPROFILES+1); + optionsmenu.offset -= (128 + 128/8)*(maxp+1); } S_StartSound(NULL, sfx_menu1); @@ -3582,8 +3583,8 @@ void M_HandleProfileSelect(INT32 ch) if (optionsmenu.profilen < 0) { - optionsmenu.profilen = MAXPROFILES; - optionsmenu.offset += (128 + 128/8)*(MAXPROFILES+1); + optionsmenu.profilen = maxp; + optionsmenu.offset += (128 + 128/8)*(maxp+1); } S_StartSound(NULL, sfx_menu1); @@ -3593,6 +3594,10 @@ void M_HandleProfileSelect(INT32 ch) else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) { S_StartSound(NULL, sfx_menu1); + + if (optionsmenu.profilen == maxp) + PR_InitNewProfile(); // initialize the new profile. + optionsmenu.profile = PR_GetProfile(optionsmenu.profilen); // This is now used to move the card we've selected. diff --git a/src/k_profiles.c b/src/k_profiles.c index a07c7b803..7766629b3 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -16,6 +16,11 @@ static profile_t profilesList[MAXPROFILES+1]; // +1 because we're gonna add a default "GUEST' profile. static UINT8 numprofiles = 0; // # of loaded profiles +INT32 PR_GetNumProfiles(void) +{ + return numprofiles; +} + profile_t PR_MakeProfile(const char *prname, const char *pname, const char *sname, const UINT16 col, const char *fname, UINT16 fcol, INT32 controlarray[num_gamecontrols][MAXINPUTMAPPING]) { profile_t new; @@ -71,6 +76,17 @@ profile_t* PR_GetProfile(INT32 num) return NULL; } +void PR_InitNewProfile(void) +{ + char pname[PROFILENAMELEN+1] = "PRF"; + profile_t dprofile; + + strcpy(pname, va("PRF%c", 'A'+numprofiles-1)); + + dprofile = PR_MakeProfile(pname, PROFILEDEFAULTPNAME, PROFILEDEFAULTSKIN, PROFILEDEFAULTCOLOR, PROFILEDEFAULTFOLLOWER, PROFILEDEFAULTFOLLOWERCOLOR, gamecontroldefault); + PR_AddProfile(dprofile); +} + void PR_SaveProfiles(void) { FILE *f = NULL; diff --git a/src/k_profiles.h b/src/k_profiles.h index fb68d3f85..686831605 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -68,6 +68,9 @@ typedef struct profile_s // Functions +// returns how many profiles there are +INT32 PR_GetNumProfiles(void); + // PR_MakeProfile // Makes a profile from the supplied profile name, player name, colour, follower, followercolour and controls. // The consvar values are left untouched. @@ -87,6 +90,10 @@ boolean PR_AddProfile(profile_t p); // Returns a pointer to the profile you're asking for or NULL if the profile is uninitialized. profile_t* PR_GetProfile(INT32 num); +// PR_InitNewProfile(void) +// Initializes the first new profile +void PR_InitNewProfile(void); + // PR_SaveProfiles(void) // Saves all the profiles in profiles.cfg // This does not save profilesList[0] since that's always going to be the default profile. From 2b74b7188fcb4600adac5105997ba4543376b174 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 19 Feb 2022 18:57:17 +0100 Subject: [PATCH 219/379] profiles: they work(?) now --- src/k_menu.h | 2 + src/k_menudraw.c | 35 +++++++++++++++ src/k_menufunc.c | 111 ++++++++++++++++++++++++++++++++++++++++++++--- src/k_profiles.c | 48 +++++++++++++++++--- src/k_profiles.h | 4 ++ 5 files changed, 188 insertions(+), 12 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 6e7b1589e..07f24df9b 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -461,6 +461,7 @@ extern struct setup_chargrid_s { typedef enum { CSSTEP_NONE = 0, + CSSTEP_PROFILE, CSSTEP_CHARS, CSSTEP_ALTS, CSSTEP_COLORS, @@ -470,6 +471,7 @@ typedef enum typedef struct setup_player_s { SINT8 gridx, gridy; + UINT8 profilen; SINT8 skin; SINT8 clonenum; SINT8 rotate; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index d4e8d653b..89f831b7a 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -929,6 +929,41 @@ static void M_DrawCharSelectPreview(UINT8 num) V_DrawScaledPatch(x+9, y+2, 0, W_CachePatchName("FILEBACK", PU_CACHE)); V_DrawScaledPatch(x, y+2, 0, W_CachePatchName(va("CHARSEL%c", letter), PU_CACHE)); V_DrawFileString(x+16, y+2, 0, "PLAYER"); + + // Profile selection + if (p->mdepth == CSSTEP_PROFILE) + { + UINT8 i = 0; + INT16 px = x+12; + INT16 py = y+48 - p->profilen*12; + UINT8 maxp = MAXPROFILES+1; + + for (i=0; i < maxp; i++) + { + profile_t *pr = PR_GetProfile(i); + INT16 dist = abs(p->profilen - i); + + if (dist > 2) + { + py += 12; + continue; + } + else if (dist == 2) + { + V_DrawCenteredFileString(px+26, py, 0, pr->version ? pr->profilename : "EMPTY"); + V_DrawScaledPatch(px, py, V_TRANSLUCENT, W_CachePatchName("FILEBACK", PU_CACHE)); + } + else + { + V_DrawScaledPatch(px, py, 0, W_CachePatchName("FILEBACK", PU_CACHE)); + + if (i != p->profilen || ((setup_animcounter/10) & 1)) + V_DrawCenteredFileString(px+26, py, 0, pr->version ? pr->profilename : "EMPTY"); + } + py += 12; + } + + } } static void M_DrawCharSelectExplosions(void) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 3e034bada..e019109d9 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2134,6 +2134,9 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num) { INT32 i, j; + if (optionsmenu.profile) + return false; // Don't allow for the possibility of SOMEHOW another player joining in. + // Detect B press first ... this means P1 can actually exit out of the menu. if (M_MenuButtonPressed(num, MBT_B) || M_MenuButtonPressed(num, MBT_Y)) { @@ -2182,7 +2185,7 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num) CONS_Printf("========\n"); //setup_numplayers++; - p->mdepth = CSSTEP_CHARS; + p->mdepth = CSSTEP_PROFILE; S_StartSound(NULL, sfx_s3k65); // Prevent quick presses for multiple players @@ -2201,6 +2204,94 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num) return false; } +// sets up the grid pos for the skin used by the profile. +static void M_SetupProfileGridPos(setup_player_t *p) +{ + profile_t *pr = PR_GetProfile(p->profilen); + INT32 i; + + for (i = 0; i < numskins; i++) + { + if (!(strcmp(pr->skinname, skins[i].name))) + { + INT32 alt = 0; // Hey it's my character's name! + p->gridx = skins[i].kartspeed-1; + p->gridy = skins[i].kartweight-1; + + // Now this put our cursor on the good alt + while (setup_chargrid[p->gridx][p->gridy].skinlist[alt] != i) + alt++; + + p->clonenum = alt; + p->color = pr->color; + return; // we're done here + } + } +} + +static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num) +{ + const UINT8 maxp = MAXPROFILES; + UINT8 i; + + if (menucmd[num].dpad_ud > 0) + { + p->profilen++; + if (p->profilen > maxp) + p->profilen = 0; + + S_StartSound(NULL, sfx_menu1); + M_SetMenuDelay(num); + } + else if (menucmd[num].dpad_ud < 0) + { + if (p->profilen == 0) + p->profilen = maxp; + else + p->profilen--; + + S_StartSound(NULL, sfx_menu1); + M_SetMenuDelay(num); + } + else if (M_MenuButtonPressed(num, MBT_B) || M_MenuButtonPressed(num, MBT_Y)) + { + if (num == setup_numplayers-1) + { + + p->mdepth = CSSTEP_NONE; + S_StartSound(NULL, sfx_s3k5b); + + // Prevent quick presses for multiple players + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + setup_player[i].delay = MENUDELAYTIME; + M_SetMenuDelay(i); + menucmd[i].buttonsHeld |= (MBT_B|MBT_Y); + } + + return true; + } + else + { + S_StartSound(NULL, sfx_s3kb2); + } + + M_SetMenuDelay(num); + } + else if (M_MenuButtonPressed(num, MBT_A) || M_MenuButtonPressed(num, MBT_X)) + { + // Apply the profile. + PR_ApplyProfile(p->profilen, num); + M_SetupProfileGridPos(p); + + p->mdepth = CSSTEP_CHARS; + S_StartSound(NULL, sfx_s3k63); + } + + return false; + +} + static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) { INT32 i; @@ -2271,7 +2362,7 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) } else // for the actual player select, go back to device detection. { - p->mdepth = CSSTEP_NONE; + p->mdepth = CSSTEP_PROFILE; S_StartSound(NULL, sfx_s3k5b); } @@ -2391,8 +2482,11 @@ boolean M_CharacterSelectHandler(INT32 choice) case CSSTEP_NONE: // Enter Game playersChanged = M_HandlePressStart(p, i); break; + case CSSTEP_PROFILE: + playersChanged = M_HandleCSelectProfile(p, i); + break; case CSSTEP_CHARS: // Character Select grid - playersChanged = M_HandleCharacterGrid(p, i); + M_HandleCharacterGrid(p, i); break; case CSSTEP_ALTS: // Select clone M_HandleCharRotate(p, i); @@ -2415,10 +2509,12 @@ boolean M_CharacterSelectHandler(INT32 choice) // Just makes it easier to access later p->skin = setup_chargrid[p->gridx][p->gridy].skinlist[p->clonenum]; - if (p->mdepth < CSSTEP_COLORS) + // Keep profile colour. + /*if (p->mdepth < CSSTEP_COLORS) { p->color = skins[p->skin].prefcolor; - } + + }*/ if (playersChanged == true) { @@ -3350,6 +3446,9 @@ void M_InitOptions(INT32 choice) Moviemode_option_Onchange(); Addons_option_Onchange(); + // For profiles: + memset(setup_player, 0, sizeof(setup_player)); + M_SetupNextMenu(&OPTIONS_MainDef, false); } @@ -3646,6 +3745,8 @@ boolean M_ProfileEditInputs(INT32 ch) optionsmenu.topty = 35; optionsmenu.resetprofile = true; // Reset profile after the transition is done. + PR_SaveProfiles(); // save profiles after we do that. + M_GoBack(0); return true; } diff --git a/src/k_profiles.c b/src/k_profiles.c index 7766629b3..9d25a759f 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -94,22 +94,56 @@ void PR_SaveProfiles(void) f = fopen(PROFILESFILE, "w"); if (f != NULL) { - fwrite(profilesList, sizeof(profile_t), MAXPROFILES, f); + fwrite(profilesList, sizeof(profile_t), MAXPROFILES+1, f); fclose(f); } + else + I_Error("Couldn't save profiles. Are you out of Disk space / playing in a protected folder?"); } void PR_LoadProfiles(void) { - //FILE *f = NULL; + FILE *f = NULL; profile_t dprofile = PR_MakeProfile(PROFILEDEFAULTNAME, PROFILEDEFAULTPNAME, PROFILEDEFAULTSKIN, PROFILEDEFAULTCOLOR, PROFILEDEFAULTFOLLOWER, PROFILEDEFAULTFOLLOWERCOLOR, gamecontroldefault); - PR_AddProfile(dprofile); - - /*f = fopen(PROFILESFILE, "r"); + f = fopen(PROFILESFILE, "r"); if (f != NULL) { - fread(&profilesList[1], sizeof(profile_t)*(MAXPROFILES), MAXPROFILES, f); + INT32 i; + fread(profilesList, sizeof(profile_t)*(MAXPROFILES+1), MAXPROFILES+1, f); fclose(f); - }*/ + + // Overwrite the first profile for the default profile to avoid letting anyone tamper with it. + memcpy(&profilesList[0], &dprofile, sizeof(profile_t)); + + // Omega, count how many profiles there are in the list. + // WHY DID YOU ASK HIM TO DO THAT IT'S GOING TO TAKE FOR-EVER + for (i=0; i < MAXPROFILES; i++) + { + if (!profilesList[i].version) + { + numprofiles = i; + return; + } + } + + } + else + { + // No profiles. Add the default one. + PR_AddProfile(dprofile); + } +} + +void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum) +{ + profile_t *p = PR_GetProfile(profilenum); + + CV_StealthSet(&cv_skin[playernum], p->skinname); + CV_StealthSetValue(&cv_playercolor[playernum], p->color); + CV_StealthSet(&cv_playername[playernum], p->playername); + // @TODO followers + + // set controls... + memcpy(&gamecontrol[playernum], p->controls, sizeof(gamecontroldefault)); } \ No newline at end of file diff --git a/src/k_profiles.h b/src/k_profiles.h index 686831605..8eb7ec3e1 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -104,6 +104,10 @@ void PR_SaveProfiles(void); // This also loads void PR_LoadProfiles(void); +// PR_ApplyProfile(UINT8 profilenum, UINT8 playernum) +// Applies the given profile's settings to the given player. +void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum); + #endif \ No newline at end of file From 33639d7a4ce160e7d75a0cacd4a285bf055b19f9 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 19 Feb 2022 19:25:51 +0100 Subject: [PATCH 220/379] Change button mappings ingame. Put delay after remapping a button to avoid very silly things --- src/g_game.c | 8 ++++---- src/k_menufunc.c | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 0fdf43e43..36693a352 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -990,20 +990,20 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) } // drift - if (G_PlayerInputDown(forplayer, gc_c, 0)) + if (G_PlayerInputDown(forplayer, gc_r, 0)) { cmd->buttons |= BT_DRIFT; } - // A + B + C shortcut - if (G_PlayerInputDown(forplayer, gc_abc, 0)) + // C + if (G_PlayerInputDown(forplayer, gc_c, 0)) { forward = 0; cmd->buttons |= BT_SPINDASHMASK; } // fire - if (G_PlayerInputDown(forplayer, gc_x, 0)) + if (G_PlayerInputDown(forplayer, gc_l, 0)) { cmd->buttons |= BT_ATTACK; } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index e019109d9..2ef29b9a3 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3961,7 +3961,10 @@ void M_MapProfileControl(event_t *ev) // Only consider keydown and joystick events to make sure we ignore ev_mouse and other events if (ev->type != ev_keydown && ev->type != ev_joystick) return; - + + // Set menu delay regardless of what we're doing to avoid stupid stuff. + M_SetMenuDelay(0); + // Check if this control is already assigned, it'd look silly to assign the same key twice on the same thing. if (n == 0 && optionsmenu.profile->controls[controln][1] == c) { From 8920ec8e6ae3af003b8585e55c2b68d2fc08ccb2 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 19 Feb 2022 21:45:42 +0100 Subject: [PATCH 221/379] Disable profile setup outside of GS_MENU --- src/k_menu.h | 2 +- src/k_menufunc.c | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 07f24df9b..b809a2c07 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -211,7 +211,7 @@ extern menu_t OPTIONS_MainDef; // We'll need this since we're gonna have to dynamically enable and disable options depending on which state we're in. typedef enum { - mopt_controls = 0, + mopt_profiles = 0, mopt_video, mopt_sound, mopt_hud, diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 2ef29b9a3..3b276d707 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3414,6 +3414,7 @@ void M_InitOptions(INT32 choice) { (void)choice; + OPTIONS_MainDef.menuitems[mopt_profiles].status = IT_STRING | IT_SUBMENU; OPTIONS_MainDef.menuitems[mopt_gameplay].status = IT_STRING | IT_SUBMENU; OPTIONS_MainDef.menuitems[mopt_server].status = IT_STRING | IT_SUBMENU; @@ -3424,6 +3425,10 @@ void M_InitOptions(INT32 choice) OPTIONS_MainDef.menuitems[mopt_server].status = IT_STRING | IT_TRANSTEXT; } + // disable profiles outside of gs_menu altogether. + if (gamestate != GS_MENU) + OPTIONS_MainDef.menuitems[mopt_profiles].status = IT_STRING | IT_TRANSTEXT; + optionsmenu.ticker = 0; optionsmenu.offset = 0; @@ -3961,10 +3966,10 @@ void M_MapProfileControl(event_t *ev) // Only consider keydown and joystick events to make sure we ignore ev_mouse and other events if (ev->type != ev_keydown && ev->type != ev_joystick) return; - + // Set menu delay regardless of what we're doing to avoid stupid stuff. M_SetMenuDelay(0); - + // Check if this control is already assigned, it'd look silly to assign the same key twice on the same thing. if (n == 0 && optionsmenu.profile->controls[controln][1] == c) { From b9a494551e7d64532aad20304929adc8ef3400b6 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 19 Feb 2022 21:49:08 +0100 Subject: [PATCH 222/379] Don't display empty profiles on player setup --- src/k_menudraw.c | 2 +- src/k_menufunc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 89f831b7a..df79edc11 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -936,7 +936,7 @@ static void M_DrawCharSelectPreview(UINT8 num) UINT8 i = 0; INT16 px = x+12; INT16 py = y+48 - p->profilen*12; - UINT8 maxp = MAXPROFILES+1; + UINT8 maxp = PR_GetNumProfiles(); for (i=0; i < maxp; i++) { diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 3b276d707..a76a7f8cd 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2231,7 +2231,7 @@ static void M_SetupProfileGridPos(setup_player_t *p) static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num) { - const UINT8 maxp = MAXPROFILES; + const UINT8 maxp = PR_GetNumProfiles() -1; UINT8 i; if (menucmd[num].dpad_ud > 0) From 2789d1d90edcc0b547625e3fb895796034e7077c Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 19 Feb 2022 22:10:37 +0100 Subject: [PATCH 223/379] Allow unbinding controls with C, prevent softlocks by having core keys unbound. (Also fix a silly bug introduced by a previous commit) --- src/k_menufunc.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index a76a7f8cd..c5adc1afa 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3414,7 +3414,7 @@ void M_InitOptions(INT32 choice) { (void)choice; - OPTIONS_MainDef.menuitems[mopt_profiles].status = IT_STRING | IT_SUBMENU; + OPTIONS_MainDef.menuitems[mopt_profiles].status = IT_STRING | IT_CALL; OPTIONS_MainDef.menuitems[mopt_gameplay].status = IT_STRING | IT_SUBMENU; OPTIONS_MainDef.menuitems[mopt_server].status = IT_STRING | IT_SUBMENU; @@ -3938,12 +3938,42 @@ void M_HandleProfileControls(void) boolean M_ProfileControlsInputs(INT32 ch) { + const UINT8 pid = 0; (void)ch; // By default, accept all inputs. if (optionsmenu.bindcontrol) return true; // Eat all inputs there. We'll use a stupid hack in M_Responder instead. + if (M_MenuButtonPressed(pid, MBT_C) || M_MenuButtonPressed(pid, MBT_Z)) + { + // check if we're on a valid menu option... + if (currentMenu->menuitems[itemOn].mvar1) + { + // clear controls for that key + optionsmenu.profile->controls[currentMenu->menuitems[itemOn].mvar1][0] = KEY_NULL; + optionsmenu.profile->controls[currentMenu->menuitems[itemOn].mvar1][1] = KEY_NULL; + S_StartSound(NULL, sfx_s3k66); + } + M_SetMenuDelay(pid); + return true; + } + + else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + { + // Quick check... + UINT8 checks[7] = {gc_a, gc_b, gc_up, gc_down, gc_left, gc_right, gc_start}; + UINT8 i; + + for (i = 0; i < 7; i++) + { + if (optionsmenu.profile->controls[checks[i]][0] == KEY_NULL) // NOT BOUND + { + M_StartMessage(M_GetText("Stupid softlock prevention!\nMake sure keys A, B, Start and\nall directions have been mapped.\n\nOtherwise, you could softlock when\nselecting this profile."), NULL, MM_NOTHING); + return true; + } + } + } return false; } From 7612d7cdf9d596f756cf99006e21a98a4f2e9ca9 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 19 Feb 2022 22:15:56 +0100 Subject: [PATCH 224/379] CharSel: Make sure the clone we're hovering over actually exists --- src/k_menufunc.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index c5adc1afa..a7b710ba6 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2296,6 +2296,8 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) { INT32 i; + UINT8 numclones; + if (menucmd[num].dpad_ud > 0) { p->gridy++; @@ -2330,6 +2332,13 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) M_SetMenuDelay(num); } + // Process this after possible pad movement, + // this makes sure we don't have a weird ghost hover on a character with no clones. + numclones = setup_chargrid[p->gridx][p->gridy].numskins; + + if (p->clonenum >= numclones) + p->clonenum = 0; + if (M_MenuButtonPressed(num, MBT_A) || M_MenuButtonPressed(num, MBT_X) /*|| M_MenuButtonPressed(num, MBT_START)*/) { if (setup_chargrid[p->gridx][p->gridy].numskins == 0) From 446fe4ce32b13bc65d359829b818e28d8c8b824a Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 19 Feb 2022 12:27:21 -0800 Subject: [PATCH 225/379] Fix event_t initializer error --- src/sdl/i_system.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 32d3980ff..f3bf4bf72 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -577,7 +577,7 @@ static void I_StartupConsole(void) void I_GetConsoleEvents(void) { // we use this when sending back commands - event_t ev = {0,0,0,0}; + event_t ev = {0}; char key = 0; ssize_t d; From c83803dd42e9a087d9592660e0c0c6ca3d1fe03c Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 19 Feb 2022 14:25:34 -0800 Subject: [PATCH 226/379] Refactor profiles code to use pointers --- src/k_profiles.c | 81 +++++++++++++++++++++++++----------------------- src/k_profiles.h | 8 ++--- 2 files changed, 47 insertions(+), 42 deletions(-) diff --git a/src/k_profiles.c b/src/k_profiles.c index 9d25a759f..72afd7420 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -11,9 +11,10 @@ /// \brief implements methods for profiles etc. #include "k_profiles.h" +#include "z_zone.h" // List of all the profiles. -static profile_t profilesList[MAXPROFILES+1]; // +1 because we're gonna add a default "GUEST' profile. +static profile_t *profilesList[MAXPROFILES+1]; // +1 because we're gonna add a default "GUEST' profile. static UINT8 numprofiles = 0; // # of loaded profiles INT32 PR_GetNumProfiles(void) @@ -21,46 +22,46 @@ INT32 PR_GetNumProfiles(void) return numprofiles; } -profile_t PR_MakeProfile(const char *prname, const char *pname, const char *sname, const UINT16 col, const char *fname, UINT16 fcol, INT32 controlarray[num_gamecontrols][MAXINPUTMAPPING]) +profile_t* PR_MakeProfile(const char *prname, const char *pname, const char *sname, const UINT16 col, const char *fname, UINT16 fcol, INT32 controlarray[num_gamecontrols][MAXINPUTMAPPING]) { - profile_t new; + profile_t *new = Z_Malloc(sizeof(profile_t), PU_STATIC, NULL); - new.version = PROFILEVER; + new->version = PROFILEVER; - strcpy(new.profilename, prname); + strcpy(new->profilename, prname); - strcpy(new.skinname, sname); - strcpy(new.playername, pname); - new.color = col; + strcpy(new->skinname, sname); + strcpy(new->playername, pname); + new->color = col; - strcpy(new.follower, fname); - new.followercolor = fcol; + strcpy(new->follower, fname); + new->followercolor = fcol; // Copy from gamecontrol directly as we'll be setting controls up directly in the profile. - memcpy(new.controls, controlarray, sizeof(new.controls)); + memcpy(new->controls, controlarray, sizeof(new->controls)); return new; } -profile_t PR_MakeProfileFromPlayer(const char *prname, const char *pname, const char *sname, const UINT16 col, const char *fname, UINT16 fcol, UINT8 pnum) +profile_t* PR_MakeProfileFromPlayer(const char *prname, const char *pname, const char *sname, const UINT16 col, const char *fname, UINT16 fcol, UINT8 pnum) { // Generate profile using the player's gamecontrol, as we set them directly when making profiles from menus. - profile_t new = PR_MakeProfile(prname, pname, sname, col, fname, fcol, gamecontrol[pnum]); + profile_t *new = PR_MakeProfile(prname, pname, sname, col, fname, fcol, gamecontrol[pnum]); // Player bound cvars: - new.kickstartaccel = cv_kickstartaccel[pnum].value; + new->kickstartaccel = cv_kickstartaccel[pnum].value; return new; } -boolean PR_AddProfile(profile_t p) +boolean PR_AddProfile(profile_t *p) { - if (numprofiles < MAXPROFILES) + if (numprofiles < MAXPROFILES+1) { - memcpy(&profilesList[numprofiles], &p, sizeof(profile_t)); + profilesList[numprofiles] = p; numprofiles++; - CONS_Printf("Profile '%s' added\n", p.profilename); + CONS_Printf("Profile '%s' added\n", p->profilename); return true; } @@ -70,8 +71,8 @@ boolean PR_AddProfile(profile_t p) profile_t* PR_GetProfile(INT32 num) { - if (num < MAXPROFILES+1) - return &profilesList[num]; + if (num < numprofiles) + return profilesList[num]; else return NULL; } @@ -79,7 +80,7 @@ profile_t* PR_GetProfile(INT32 num) void PR_InitNewProfile(void) { char pname[PROFILENAMELEN+1] = "PRF"; - profile_t dprofile; + profile_t *dprofile; strcpy(pname, va("PRF%c", 'A'+numprofiles-1)); @@ -94,7 +95,15 @@ void PR_SaveProfiles(void) f = fopen(PROFILESFILE, "w"); if (f != NULL) { - fwrite(profilesList, sizeof(profile_t), MAXPROFILES+1, f); + UINT8 i; + + fwrite(&numprofiles, sizeof numprofiles, 1, f); + + for (i = 1; i < numprofiles; ++i) + { + fwrite(profilesList[i], sizeof(profile_t), 1, f); + } + fclose(f); } else @@ -104,29 +113,25 @@ void PR_SaveProfiles(void) void PR_LoadProfiles(void) { FILE *f = NULL; - profile_t dprofile = PR_MakeProfile(PROFILEDEFAULTNAME, PROFILEDEFAULTPNAME, PROFILEDEFAULTSKIN, PROFILEDEFAULTCOLOR, PROFILEDEFAULTFOLLOWER, PROFILEDEFAULTFOLLOWERCOLOR, gamecontroldefault); + profile_t *dprofile = PR_MakeProfile(PROFILEDEFAULTNAME, PROFILEDEFAULTPNAME, PROFILEDEFAULTSKIN, PROFILEDEFAULTCOLOR, PROFILEDEFAULTFOLLOWER, PROFILEDEFAULTFOLLOWERCOLOR, gamecontroldefault); f = fopen(PROFILESFILE, "r"); if (f != NULL) { INT32 i; - fread(profilesList, sizeof(profile_t)*(MAXPROFILES+1), MAXPROFILES+1, f); + + fread(&numprofiles, sizeof numprofiles, 1, f); + + for (i = 1; i < numprofiles; ++i) + { + profilesList[i] = Z_Malloc(sizeof(profile_t), PU_STATIC, NULL); + fread(profilesList[i], sizeof(profile_t), 1, f); + } + fclose(f); // Overwrite the first profile for the default profile to avoid letting anyone tamper with it. - memcpy(&profilesList[0], &dprofile, sizeof(profile_t)); - - // Omega, count how many profiles there are in the list. - // WHY DID YOU ASK HIM TO DO THAT IT'S GOING TO TAKE FOR-EVER - for (i=0; i < MAXPROFILES; i++) - { - if (!profilesList[i].version) - { - numprofiles = i; - return; - } - } - + profilesList[0] = dprofile; } else { @@ -146,4 +151,4 @@ void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum) // set controls... memcpy(&gamecontrol[playernum], p->controls, sizeof(gamecontroldefault)); -} \ No newline at end of file +} diff --git a/src/k_profiles.h b/src/k_profiles.h index 8eb7ec3e1..4cd0a5f1d 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -74,17 +74,17 @@ INT32 PR_GetNumProfiles(void); // PR_MakeProfile // Makes a profile from the supplied profile name, player name, colour, follower, followercolour and controls. // The consvar values are left untouched. -profile_t PR_MakeProfile(const char *prname, const char *pname, const char *sname, const UINT16 col, const char *fname, UINT16 fcol, INT32 controlarray[num_gamecontrols][MAXINPUTMAPPING]); +profile_t* PR_MakeProfile(const char *prname, const char *pname, const char *sname, const UINT16 col, const char *fname, UINT16 fcol, INT32 controlarray[num_gamecontrols][MAXINPUTMAPPING]); // PR_MakeProfileFromPlayer // Makes a profile_t from the supplied profile name, player name, colour, follower and followercolour. // The last argument is a player number to read cvars from; as for convenience, cvars will be set directly when making a profile (since loading another one will overwrite them, this will be inconsequential) -profile_t PR_MakeProfileFromPlayer(const char *prname, const char *pname, const char *sname, const UINT16 col, const char *fname, UINT16 fcol, UINT8 pnum); +profile_t* PR_MakeProfileFromPlayer(const char *prname, const char *pname, const char *sname, const UINT16 col, const char *fname, UINT16 fcol, UINT8 pnum); // PR_AddProfile(profile_t p) // Adds a profile to profilesList and increments numprofiles. // Returns true if succesful, false if not. -boolean PR_AddProfile(profile_t p); +boolean PR_AddProfile(profile_t *p); // PR_GetProfile(INT32 num) // Returns a pointer to the profile you're asking for or NULL if the profile is uninitialized. @@ -110,4 +110,4 @@ void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum); -#endif \ No newline at end of file +#endif From a756b669b7fdf8448e96175bcea2ff75e46f89cc Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 19 Feb 2022 14:28:09 -0800 Subject: [PATCH 227/379] Save kartprofiles.cfg in config directory --- src/k_profiles.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/k_profiles.c b/src/k_profiles.c index 72afd7420..62a7bf298 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -10,6 +10,7 @@ /// \file k_profiles.c /// \brief implements methods for profiles etc. +#include "d_main.h" // pandf #include "k_profiles.h" #include "z_zone.h" @@ -92,7 +93,7 @@ void PR_SaveProfiles(void) { FILE *f = NULL; - f = fopen(PROFILESFILE, "w"); + f = fopen(va(pandf, srb2home, PROFILESFILE), "w"); if (f != NULL) { UINT8 i; @@ -114,7 +115,7 @@ void PR_LoadProfiles(void) { FILE *f = NULL; profile_t *dprofile = PR_MakeProfile(PROFILEDEFAULTNAME, PROFILEDEFAULTPNAME, PROFILEDEFAULTSKIN, PROFILEDEFAULTCOLOR, PROFILEDEFAULTFOLLOWER, PROFILEDEFAULTFOLLOWERCOLOR, gamecontroldefault); - f = fopen(PROFILESFILE, "r"); + f = fopen(va(pandf, srb2home, PROFILESFILE), "r"); if (f != NULL) { From 6eaf350387357e46283ddf5ad01815787bfc5509 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 19 Feb 2022 16:08:35 -0800 Subject: [PATCH 228/379] Process keyboard text inputs at responder --- src/k_menufunc.c | 52 ++++++++++++++++++------------------------------ 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index a7b710ba6..4e5d65c97 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -921,6 +921,12 @@ boolean M_Responder(event_t *ev) return false; } + // Typing for CV_IT_STRING + if (menutyping && !menutypingclose && keyboardtyping) + { + M_ChangeStringCvar(menuKey); + } + // We're in the menu itself now. // M_Ticker will take care of the rest. return true; @@ -1175,11 +1181,11 @@ static void M_UpdateKeyboardX(void) keyboardx--; } -// Hack... -// This is used to prevent keyboard inputs from being processed when they shouldn't. -// We need to find why this is needed so that I can remove this disgusting thing -static INT32 lastkey = 0; -static tic_t lastkeyheldfor = 0; +static boolean M_IsTypingKey(INT32 key) +{ + return key == KEY_BACKSPACE || key == KEY_ENTER || + key == KEY_DEL || isprint(key); +} static void M_MenuTypingInput(INT32 key) { @@ -1205,33 +1211,17 @@ static void M_MenuTypingInput(INT32 key) return; // Don't allow typing until it's fully opened. } - // Now handle the inputs. - // HACK: TODO: REMOVE THIS - // This prevents the same key from being processed multiple times. ev_keydown should account for this but it seems like it doesn't. - if (lastkey != key) - { - lastkey = key; - lastkeyheldfor = 0; - } - else - { - lastkeyheldfor++; - if (lastkeyheldfor > 0 && lastkeyheldfor < TICRATE/2) - key = -1; - } - // END OF HACK. - // Determine when to check for keyboard inputs or controller inputs using menuKey, which is the key passed here as argument. if (!keyboardtyping) // controller inputs { // we pressed a keyboard input that's not any of our buttons - if (key != -1 && menucmd[pid].dpad_lr == 0 && menucmd[pid].dpad_ud == 0 - && !M_MenuButtonPressed(pid, MBT_A) - && !M_MenuButtonPressed(pid, MBT_B) - && !M_MenuButtonPressed(pid, MBT_C) - && !M_MenuButtonPressed(pid, MBT_X) - && !M_MenuButtonPressed(pid, MBT_Y) - && !M_MenuButtonPressed(pid, MBT_Z)) + if (M_IsTypingKey(key) && menucmd[pid].dpad_lr == 0 && menucmd[pid].dpad_ud == 0 + && !(menucmd[pid].buttons & MBT_A) + && !(menucmd[pid].buttons & MBT_B) + && !(menucmd[pid].buttons & MBT_C) + && !(menucmd[pid].buttons & MBT_X) + && !(menucmd[pid].buttons & MBT_Y) + && !(menucmd[pid].buttons & MBT_Z)) { keyboardtyping = true; } @@ -1239,7 +1229,7 @@ static void M_MenuTypingInput(INT32 key) else // Keyboard inputs. { // On the flipside, if we're pressing any keyboard input, switch to controller inputs. - if (key == -1 && ( + if (!M_IsTypingKey(key) && ( M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_C) @@ -1260,10 +1250,6 @@ static void M_MenuTypingInput(INT32 key) menutypingclose = true; // close menu. return; } - else // process everything else as input for typing - { - M_ChangeStringCvar(key); - } } From 68ed9982b66fa90cf84e28d6d256d8f8ee1faeb9 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 19 Feb 2022 16:12:12 -0800 Subject: [PATCH 229/379] Fix virtual keyboard left wrap --- src/k_menufunc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 4e5d65c97..6299be4fe 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1289,7 +1289,7 @@ static void M_MenuTypingInput(INT32 key) keyboardx--; if (keyboardx < 0) { - keyboardx = 13; + keyboardx = 12; M_UpdateKeyboardX(); } M_SetMenuDelay(pid); From 3504f7ebd38f5223feeb1b3bdb31aee4bb5d739a Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 19 Feb 2022 16:30:23 -0800 Subject: [PATCH 230/379] Map buffered inputs to menucmd This lets buffered tapping not count as one held input. --- src/d_main.c | 8 ++++++++ src/k_menu.h | 1 + src/k_menufunc.c | 27 ++++++++++++++++++--------- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index e80d38b7b..a1bc307af 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -182,6 +182,7 @@ void D_ProcessEvents(void) event_t *ev; boolean eaten; + boolean menuresponse = false; memset(deviceResponding, false, sizeof (deviceResponding)); for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1)) @@ -205,6 +206,7 @@ void D_ProcessEvents(void) } // Menu input + menuresponse = true; #ifdef HAVE_THREADS I_lock_mutex(&k_menu_mutex); #endif @@ -244,6 +246,12 @@ void D_ProcessEvents(void) G_Responder(ev); } + + // Reset menu controls when no event is processed + if (!menuresponse) + { + M_MapMenuControls(NULL); + } } // diff --git a/src/k_menu.h b/src/k_menu.h index b809a2c07..650fe63b0 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -424,6 +424,7 @@ void Addons_option_Onchange(void); void M_SortServerList(void); +void M_MapMenuControls(event_t *ev); boolean M_Responder(event_t *ev); boolean M_MenuButtonPressed(UINT8 pid, UINT32 bt); void M_StartControlPanel(void); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 6299be4fe..89a28cc8c 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -828,8 +828,7 @@ boolean M_Responder(event_t *ev) menuKey = ev->data1; } - // update keys current state - G_MapEventsToControls(ev); + M_MapMenuControls(ev); // Profiles: Control mapping. // We take the WHOLE EVENT for convenience. @@ -1163,6 +1162,23 @@ static void M_UpdateMenuCMD(UINT8 i) } } +void M_MapMenuControls(event_t *ev) +{ + INT32 i; + + if (ev) + { + // update keys current state + G_MapEventsToControls(ev); + } + + // Update menu CMD + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + M_UpdateMenuCMD(i); + } +} + boolean M_MenuButtonPressed(UINT8 pid, UINT32 bt) { if (menucmd[pid].buttonsHeld & bt) @@ -1326,16 +1342,9 @@ static void M_MenuTypingInput(INT32 key) static void M_HandleMenuInput(void) { void (*routine)(INT32 choice); // for some casting problem - INT32 i; UINT8 pid = 0; // todo: Add ability for any splitscreen player to bring up the menu. SINT8 lr = 0, ud = 0; - // Update menu CMD - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) - { - M_UpdateMenuCMD(i); - } - if (menuactive == false) { // We're not in the menu. From 2d509f40ebf768fdced0e9647cb08f74e673fb04 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 19 Feb 2022 17:05:51 -0800 Subject: [PATCH 231/379] Remap game controls --- src/g_game.c | 16 ++++++++-------- src/g_input.h | 12 +++++++++++- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 36693a352..638afe659 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -940,12 +940,12 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) if (player->spectator || objectplacing) // SRB2Kart: spectators need special controls { - if (G_PlayerInputDown(forplayer, gc_a, 0)) + if (G_PlayerInputDown(forplayer, gc_accel, 0)) { cmd->buttons |= BT_ACCELERATE; } - if (G_PlayerInputDown(forplayer, gc_b, 0)) + if (G_PlayerInputDown(forplayer, gc_brake, 0)) { cmd->buttons |= BT_BRAKE; } @@ -963,14 +963,14 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) else { // forward with key or button // SRB2kart - we use an accel/brake instead of forward/backward. - INT32 value = G_PlayerInputAnalog(forplayer, gc_a, 0); + INT32 value = G_PlayerInputAnalog(forplayer, gc_accel, 0); if (value != 0) { cmd->buttons |= BT_ACCELERATE; forward += ((value * MAXPLMOVE) >> 10); } - value = G_PlayerInputAnalog(forplayer, gc_b, 0); + value = G_PlayerInputAnalog(forplayer, gc_brake, 0); if (value != 0) { cmd->buttons |= BT_BRAKE; @@ -990,26 +990,26 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) } // drift - if (G_PlayerInputDown(forplayer, gc_r, 0)) + if (G_PlayerInputDown(forplayer, gc_drift, 0)) { cmd->buttons |= BT_DRIFT; } // C - if (G_PlayerInputDown(forplayer, gc_c, 0)) + if (G_PlayerInputDown(forplayer, gc_spindash, 0)) { forward = 0; cmd->buttons |= BT_SPINDASHMASK; } // fire - if (G_PlayerInputDown(forplayer, gc_l, 0)) + if (G_PlayerInputDown(forplayer, gc_item, 0)) { cmd->buttons |= BT_ATTACK; } // rear view - if (G_PlayerInputDown(forplayer, gc_y, 0)) + if (G_PlayerInputDown(forplayer, gc_lookback, 0)) { cmd->buttons |= BT_LOOKBACK; } diff --git a/src/g_input.h b/src/g_input.h index d699a528c..f633d4f0f 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -76,7 +76,17 @@ typedef enum gc_screenshot, gc_recordgif, - num_gamecontrols + num_gamecontrols, + + // alias gameplay controls + gc_accel = gc_a, + gc_brake = gc_x, + gc_drift = gc_r, + + gc_item = gc_l, + gc_spindash = gc_c, + + gc_lookback = gc_b, } gamecontrols_e; // mouse values are used once From 27a97f03d9a5f8254bf995c5c639141737493dfa Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 19 Feb 2022 19:34:25 -0800 Subject: [PATCH 232/379] Fix declaration after label --- src/k_menudraw.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index df79edc11..278265af7 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2262,7 +2262,7 @@ void M_DrawEditProfile(void) { switch (currentMenu->menuitems[i].status & IT_DISPLAY) { - case IT_STRING: + case IT_STRING: { UINT8 *colormap = NULL; if (i == itemOn) @@ -2296,6 +2296,7 @@ void M_DrawEditProfile(void) y += 34; break; + } } } @@ -3613,4 +3614,4 @@ void M_DrawAddons(void) if (modifiedgame) V_DrawSmallScaledPatch(x, y + 4, 0, addonsp[NUM_EXT+2]); -} \ No newline at end of file +} From c24c0457e3017909063b7ea0ff1c7e99ba6e97bf Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 19 Feb 2022 20:26:31 -0800 Subject: [PATCH 233/379] NUL terminate profilename --- src/k_profiles.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/k_profiles.c b/src/k_profiles.c index 62a7bf298..158d21e78 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -30,6 +30,7 @@ profile_t* PR_MakeProfile(const char *prname, const char *pname, const char *sna new->version = PROFILEVER; strcpy(new->profilename, prname); + new->profilename[sizeof new->profilename - 1] = '\0'; strcpy(new->skinname, sname); strcpy(new->playername, pname); From 168f393a6232b692da6b4f24905b53263eeadf71 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sun, 20 Feb 2022 08:24:24 +0100 Subject: [PATCH 234/379] Remove softlock check as it's unecessary after all --- src/k_menufunc.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 89a28cc8c..cbf06a6d4 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3962,22 +3962,6 @@ boolean M_ProfileControlsInputs(INT32 ch) M_SetMenuDelay(pid); return true; } - - else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) - { - // Quick check... - UINT8 checks[7] = {gc_a, gc_b, gc_up, gc_down, gc_left, gc_right, gc_start}; - UINT8 i; - - for (i = 0; i < 7; i++) - { - if (optionsmenu.profile->controls[checks[i]][0] == KEY_NULL) // NOT BOUND - { - M_StartMessage(M_GetText("Stupid softlock prevention!\nMake sure keys A, B, Start and\nall directions have been mapped.\n\nOtherwise, you could softlock when\nselecting this profile."), NULL, MM_NOTHING); - return true; - } - } - } return false; } From 94c18f535a2b30d478340e8be651959b69bb0191 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sun, 20 Feb 2022 09:36:17 +0100 Subject: [PATCH 235/379] Map 4 keys per button. Detect device when mapping --- src/k_menudraw.c | 33 ++++++++++++++-------- src/k_menufunc.c | 72 ++++++++++++++++++++++++++++-------------------- 2 files changed, 63 insertions(+), 42 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 278265af7..6c01c5be2 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2332,7 +2332,7 @@ void M_DrawProfileControls(void) const UINT8 spacing = 34; INT32 y = 16 - (optionsmenu.controlscroll*spacing); INT32 x = 8; - INT32 i, j; + INT32 i, j, k; M_DrawOptionsCogs(); @@ -2353,7 +2353,7 @@ void M_DrawProfileControls(void) for (i = 0; i < currentMenu->numitems; i++) { char buf[256]; - INT32 keys[2]; + INT32 keys[MAXINPUTMAPPING]; // cursor if (i == itemOn) @@ -2372,34 +2372,43 @@ void M_DrawProfileControls(void) case IT_STRING2: + boolean drawnpatch = false; + if (currentMenu->menuitems[i].patch) + { V_DrawScaledPatch(x+12, y+12, 0, W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE)); + drawnpatch = true; + } else V_DrawString(x, y+1, (i == itemOn ? highlightflags : 0), currentMenu->menuitems[i].text); if (currentMenu->menuitems[i].status & IT_CONTROL) { // Draw what the controls are mapped to - keys[0] = optionsmenu.profile->controls[currentMenu->menuitems[i].mvar1][0]; - keys[1] = optionsmenu.profile->controls[currentMenu->menuitems[i].mvar1][1]; + for (k = 0; k < MAXINPUTMAPPING; k++) + keys[k] = optionsmenu.profile->controls[currentMenu->menuitems[i].mvar1][k]; buf[0] = '\0'; - if (keys[0] == KEY_NULL && keys[1] == KEY_NULL) + if (keys[0] == KEY_NULL) // If the first key's null, so should every other. strcpy(buf, "\x85NOT BOUND"); else { - if (keys[0] != KEY_NULL) - strcat (buf, G_KeynumToString (keys[0])); + for (k=0; k < MAXINPUTMAPPING && keys[k] != KEY_NULL; k++) + { + if (k > 0) + strcat(buf," / "); - if (keys[0] != KEY_NULL && keys[1] != KEY_NULL) - strcat(buf," / "); + if (k == 2 && drawnpatch) // hacky... + strcat(buf, "\n"); - if (keys[1] != KEY_NULL) - strcat (buf, G_KeynumToString (keys[1])); + strcat(buf, G_KeynumToString (keys[k])); + + } } - V_DrawThinString(x+32, y+12, V_6WIDTHSPACE, buf); + // don't shift the text if we didn't draw a patch. + V_DrawThinString(x+ (drawnpatch ? 32 : 0), y+ (drawnpatch? 2 : 12), V_6WIDTHSPACE, buf); // controller dest coords: if (itemOn == i && currentMenu->menuitems[i].mvar1 && currentMenu->menuitems[i].mvar1 <= gc_start) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index cbf06a6d4..45f762a31 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3870,10 +3870,10 @@ void M_HandleVideoModes(INT32 ch) } } -static void M_ProfileDeviceSelectResponse(INT32 key) +// sets whatever device has had its key pressed to the active device. +static void SetDeviceOnPress(void) { UINT8 i; - (void) key; for (i=0; i < MAXDEVICES; i++) { @@ -3900,7 +3900,8 @@ void M_ProfileDeviceSelect(INT32 choice) optionsmenu.contx = optionsmenu.tcontx = controlleroffsets[gc_a][0]; optionsmenu.conty = optionsmenu.tconty = controlleroffsets[gc_a][1]; - M_StartMessage(M_GetText("Press any key on the device\nyou would like to use"), M_ProfileDeviceSelectResponse, MM_EVENTHANDLER); + //M_StartMessage(M_GetText("Press any key on the device\nyou would like to use"), M_ProfileDeviceSelectResponse, MM_EVENTHANDLER); + M_SetupNextMenu(&OPTIONS_ProfileControlsDef, false); // Don't set device here anymore. } void M_HandleProfileControls(void) @@ -3930,11 +3931,7 @@ void M_HandleProfileControls(void) optionsmenu.bindtimer--; if (!optionsmenu.bindtimer) { - optionsmenu.bindcontrol++; - if (optionsmenu.bindcontrol > 2) - optionsmenu.bindcontrol = 0; // we've gone past the max, just stop. - else - optionsmenu.bindtimer = TICRATE*5; // skip control + optionsmenu.bindcontrol = 0; // we've gone past the max, just stop. } } @@ -3955,8 +3952,11 @@ boolean M_ProfileControlsInputs(INT32 ch) if (currentMenu->menuitems[itemOn].mvar1) { // clear controls for that key - optionsmenu.profile->controls[currentMenu->menuitems[itemOn].mvar1][0] = KEY_NULL; - optionsmenu.profile->controls[currentMenu->menuitems[itemOn].mvar1][1] = KEY_NULL; + INT32 i; + + for (i = 0; i < MAXINPUTMAPPING; i++) + optionsmenu.profile->controls[currentMenu->menuitems[itemOn].mvar1][i] = KEY_NULL; + S_StartSound(NULL, sfx_s3k66); } M_SetMenuDelay(pid); @@ -3967,9 +3967,24 @@ boolean M_ProfileControlsInputs(INT32 ch) void M_ProfileSetControl(INT32 ch) { + INT32 controln = currentMenu->menuitems[itemOn].mvar1; + UINT8 i; (void) ch; - optionsmenu.bindcontrol = 1; + optionsmenu.bindcontrol = 1; // Default to control #1 + + for (i = 0; i < MAXINPUTMAPPING; i++) + { + if (optionsmenu.profile->controls[controln][i] == KEY_NULL) + { + optionsmenu.bindcontrol = i+1; + break; + } + } + + // If we could find a null key to map into, map there. + // Otherwise, this will stay at 1 which means we'll overwrite the first bound control. + optionsmenu.bindtimer = TICRATE*5; } @@ -3977,9 +3992,12 @@ void M_ProfileSetControl(INT32 ch) void M_MapProfileControl(event_t *ev) { INT32 c = ev->data1; - UINT8 n = optionsmenu.bindcontrol-1; // # of input to bind + UINT8 n = optionsmenu.bindcontrol-1; // # of input to bind INT32 controln = currentMenu->menuitems[itemOn].mvar1; // gc_ UINT8 where = n; // By default, we'll save the bind where we're supposed to map. + INT32 i; + + SetDeviceOnPress(); // Update cv_usejoystick // Only consider keydown and joystick events to make sure we ignore ev_mouse and other events if (ev->type != ev_keydown && ev->type != ev_joystick) @@ -3988,28 +4006,22 @@ void M_MapProfileControl(event_t *ev) // Set menu delay regardless of what we're doing to avoid stupid stuff. M_SetMenuDelay(0); - // Check if this control is already assigned, it'd look silly to assign the same key twice on the same thing. - if (n == 0 && optionsmenu.profile->controls[controln][1] == c) + // Check if this particular key (c) is already bound in any slot. + // If that's the case, simply do nothing. + for (i = 0; i < MAXINPUTMAPPING; i++) { - optionsmenu.profile->controls[controln][1] = KEY_NULL; // unbind - where = 0; // save control in slot 0 - } - else if (n == 1 && optionsmenu.profile->controls[controln][0] == c) - { - // Do nothing and exit this menu. - optionsmenu.bindcontrol = 0; - return; + if (optionsmenu.profile->controls[controln][i] == c) + { + optionsmenu.bindcontrol = 0; + return; + } } + // With the way we do things, there cannot be instances of 'gaps' within the controls, so we don't need to pretend like we need to handle that. + // Unless of course you tamper with the cfg file, but then it's *your* fault, not mine. + optionsmenu.profile->controls[controln][where] = c; - - optionsmenu.bindcontrol++; - optionsmenu.bindtimer = TICRATE*5; - if (optionsmenu.bindcontrol > 2) - { - optionsmenu.bindtimer = 0; - optionsmenu.bindcontrol = 0; - } + optionsmenu.bindcontrol = 0; // not binding anymore } void M_HandleItemToggles(INT32 choice) From 87dfa790cb135bd847460681241eabf9fe2a66cc Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sun, 20 Feb 2022 09:50:18 +0100 Subject: [PATCH 236/379] Fix stupid potential softlock on control setup with device detection --- src/k_menufunc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 45f762a31..4f0a2ec64 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3881,7 +3881,6 @@ static void SetDeviceOnPress(void) { CV_SetValue(&cv_usejoystick[0], i); // Force-set this joystick as the current joystick we're using for P1 (which is the only one controlling menus) CONS_Printf("Using device %d for mappings\n", i); - M_SetupNextMenu(&OPTIONS_ProfileControlsDef, false); // with that set, send us to the control def return; } } From 5801d677061581dc5fc00ed748cdf59a104fa7af Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sun, 20 Feb 2022 15:23:08 +0100 Subject: [PATCH 237/379] Add profile # and player name on the card --- src/hu_stuff.c | 3 +++ src/hu_stuff.h | 1 + src/k_menudraw.c | 8 ++++++++ src/k_profiles.c | 12 ++++++++++++ src/k_profiles.h | 4 +++- src/v_video.c | 17 +++++++++++++++++ src/v_video.h | 2 ++ 7 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 45b5c079c..d41c6028f 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -290,6 +290,9 @@ void HU_Init(void) PR ("PINGN"); REG; + PR ("PRFN"); + REG; + DIG (3); ADIM (KART); diff --git a/src/hu_stuff.h b/src/hu_stuff.h index 0704185f3..db1de2447 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -66,6 +66,7 @@ enum X (TALLNUM), X (NIGHTSNUM), X (PINGNUM), + X (PROFNUM), X (KART), X (GM), diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 6c01c5be2..4c0f60ddf 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1121,6 +1121,14 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) // Card bottom to overlay the skin preview V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, cardbot, colormap); + + // Profile number & player name + + if (p != NULL) + { + V_DrawProfileNum(x + 37 + 10, y + 131, 0, PR_GetProfileNum(p)); + V_DrawCenteredThinString(x, y + 151, V_GRAYMAP|V_6WIDTHSPACE, p->playername); + } } void M_DrawCharacterSelect(void) diff --git a/src/k_profiles.c b/src/k_profiles.c index 158d21e78..ef8382036 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -154,3 +154,15 @@ void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum) // set controls... memcpy(&gamecontrol[playernum], p->controls, sizeof(gamecontroldefault)); } + +UINT8 PR_GetProfileNum(profile_t *p) +{ + UINT8 i; + for (i = 0; i < MAXPROFILES+1; i++) + { + profile_t *comp = PR_GetProfile(i); + if (comp == p) + return i; + } + return 0; +} \ No newline at end of file diff --git a/src/k_profiles.h b/src/k_profiles.h index 4cd0a5f1d..33c4575d8 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -108,6 +108,8 @@ void PR_LoadProfiles(void); // Applies the given profile's settings to the given player. void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum); - +// PR_GetProfileNum(profile_t *p) +// Gets the profile's index # in profilesList +UINT8 PR_GetProfileNum(profile_t *p); #endif diff --git a/src/v_video.c b/src/v_video.c index 832a945b6..2b5371a25 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -2650,6 +2650,23 @@ void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits) } while (--digits); } +void V_DrawProfileNum(INT32 x, INT32 y, INT32 flags, UINT8 num) +{ + UINT8 digits = 3; + INT32 w = fontv[PROFNUM_FONT].font[0]->width; + + if (flags & V_NOSCALESTART) + w *= vid.dupx; + + // draw the number + do + { + x -= (w-1); + V_DrawScaledPatch(x, y, flags, fontv[PROFNUM_FONT].font[num % 10]); + num /= 10; + } while (--digits); +} + // Draws a number using the PING font thingy. // TODO: Merge number drawing functions into one with "font name" selection. diff --git a/src/v_video.h b/src/v_video.h index 7319a58b0..a0d19c625 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -316,6 +316,8 @@ void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits) // This is a separate function because IMO lua should have access to it as well. void V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *colormap); +void V_DrawProfileNum(INT32 x, INT32 y, INT32 flags, UINT8 num); + #define V_DrawCreditString( x,y,option,string ) \ V__DrawOneScaleString (x,y,FRACUNIT,option,NULL,CRED_FONT,string) From 964d503988e76fe47908d70b247a0d06f5ca9897 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 26 Feb 2022 10:45:27 +0100 Subject: [PATCH 238/379] Prevent profile player name from writing out of bounds --- src/k_menufunc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 4f0a2ec64..c806446d1 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3775,7 +3775,7 @@ void M_HandleProfileEdit(void) strncpy(optionsmenu.profile->profilename, cv_dummyprofilename.string, PROFILENAMELEN); if (strlen(cv_dummyprofileplayername.string)) - strcpy(optionsmenu.profile->playername, cv_dummyprofileplayername.string); + strncpy(optionsmenu.profile->playername, cv_dummyprofileplayername.string, MAXPLAYERNAME); } // special menuitem key handler for video mode list From c750c17f377e8e651aaa63235cf07e2af7a48456 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 26 Feb 2022 10:52:50 +0100 Subject: [PATCH 239/379] fix softlock on profile character select --- src/k_menufunc.c | 57 ++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index c806446d1..4204df463 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1990,6 +1990,32 @@ struct setup_explosions_s setup_explosions[48]; UINT8 setup_numplayers = 0; // This variable is very important, it was extended to determine how many players exist in ALL menus. tic_t setup_animcounter = 0; +// sets up the grid pos for the skin used by the profile. +static void M_SetupProfileGridPos(setup_player_t *p) +{ + profile_t *pr = PR_GetProfile(p->profilen); + INT32 i; + + for (i = 0; i < numskins; i++) + { + if (!(strcmp(pr->skinname, skins[i].name))) + { + INT32 alt = 0; // Hey it's my character's name! + p->gridx = skins[i].kartspeed-1; + p->gridy = skins[i].kartweight-1; + + // Now this put our cursor on the good alt + while (setup_chargrid[p->gridx][p->gridy].skinlist[alt] != i) + alt++; + + p->clonenum = alt; + p->color = pr->color; + return; // we're done here + } + } +} + + void M_CharacterSelectInit(void) { UINT8 i, j; @@ -2041,8 +2067,12 @@ void M_CharacterSelectInit(void) // If we're on prpfile select, skip straight to CSSTEP_CHARS if (optionsmenu.profile && j == 0) + { + setup_player[j].profilen = optionsmenu.profilen; + PR_ApplyProfile(setup_player[j].profilen, 0); + M_SetupProfileGridPos(&setup_player[j]); setup_player[j].mdepth = CSSTEP_CHARS; - + } } } } @@ -2199,31 +2229,6 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num) return false; } -// sets up the grid pos for the skin used by the profile. -static void M_SetupProfileGridPos(setup_player_t *p) -{ - profile_t *pr = PR_GetProfile(p->profilen); - INT32 i; - - for (i = 0; i < numskins; i++) - { - if (!(strcmp(pr->skinname, skins[i].name))) - { - INT32 alt = 0; // Hey it's my character's name! - p->gridx = skins[i].kartspeed-1; - p->gridy = skins[i].kartweight-1; - - // Now this put our cursor on the good alt - while (setup_chargrid[p->gridx][p->gridy].skinlist[alt] != i) - alt++; - - p->clonenum = alt; - p->color = pr->color; - return; // we're done here - } - } -} - static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num) { const UINT8 maxp = PR_GetNumProfiles() -1; From ca11e07f950db955d361475d1479167beb234802 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 26 Feb 2022 10:56:16 +0100 Subject: [PATCH 240/379] Fix the profile closing if you back out from the profile charsel --- src/k_menufunc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 4204df463..0f0934cc1 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2366,6 +2366,7 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) if (optionsmenu.profile) { memset(setup_player, 0, sizeof(setup_player)); // Reset setup_player otherwise it does some VERY funky things. + M_SetMenuDelay(0); M_GoBack(0); return true; } From 425160ceafa487b2cefb726fc47ec5446224f472 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 26 Feb 2022 11:05:19 +0100 Subject: [PATCH 241/379] Don't allow editing guest profile --- src/k_menufunc.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 0f0934cc1..6a9438c75 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3707,6 +3707,15 @@ void M_HandleProfileSelect(INT32 ch) else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) { + + if (optionsmenu.profilen == 0) // Guest profile, you can't edit that one! + { + S_StartSound(NULL, sfx_s3k7b); + M_StartMessage(M_GetText("Guest Profile cannot be edited.\nTo change parameters,\ncreate a new Profile."), NULL, MM_NOTHING); + M_SetMenuDelay(pid); + return; + } + S_StartSound(NULL, sfx_menu1); if (optionsmenu.profilen == maxp) From 82bab536c705619e631e943f43ea021dd0921128 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 26 Feb 2022 13:37:23 +0100 Subject: [PATCH 242/379] Don't allow multiple profiles of the same name to exist --- src/k_menufunc.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 6a9438c75..8b599568a 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3761,10 +3761,37 @@ void M_HandleProfileSelect(INT32 ch) boolean M_ProfileEditInputs(INT32 ch) { const UINT8 pid = 0; + UINT8 i; (void) ch; if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) { + // check if some profiles have the same name + for (i = 0; i < PR_GetNumProfiles(); i++) + { + profile_t *check = PR_GetProfile(i); + + // For obvious reasons don't check if our name is the same as our name.... + if (check != optionsmenu.profile) + { + if (!(strcmp(optionsmenu.profile->profilename, check->profilename))) + { + S_StartSound(NULL, sfx_s3k7b); + M_StartMessage(M_GetText("Another Profile uses the same\nname identifier.\nPlease change the name\nof the Profile to save it."), NULL, MM_NOTHING); + M_SetMenuDelay(pid); + return true; + } + } + } + + if (optionsmenu.profilen == 0) // Guest profile, you can't edit that one! + { + S_StartSound(NULL, sfx_s3k7b); + M_StartMessage(M_GetText("Guest Profile cannot be edited.\nTo change parameters,\ncreate a new Profile."), NULL, MM_NOTHING); + M_SetMenuDelay(pid); + return true; + } + optionsmenu.toptx = 160; optionsmenu.topty = 35; optionsmenu.resetprofile = true; // Reset profile after the transition is done. @@ -3787,7 +3814,17 @@ void M_HandleProfileEdit(void) // Copy the first 6 chars for profile name if (strlen(cv_dummyprofilename.string)) + { + char *s; + // convert dummyprofilename to uppercase strncpy(optionsmenu.profile->profilename, cv_dummyprofilename.string, PROFILENAMELEN); + s = optionsmenu.profile->profilename; + while (*s) + { + *s = toupper(*s); + s++; + } + } if (strlen(cv_dummyprofileplayername.string)) strncpy(optionsmenu.profile->playername, cv_dummyprofileplayername.string, MAXPLAYERNAME); From ed5005cb05f5d55264e66aaa02c1b43072fea3ea Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 26 Feb 2022 15:09:39 +0100 Subject: [PATCH 243/379] Add kickstartaccel on profile controls --- src/k_menu.h | 1 + src/k_menudef.c | 6 ++++++ src/k_menudraw.c | 18 ++++++++++++++++-- src/k_menufunc.c | 7 +++++++ src/k_profiles.c | 3 +++ 5 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 650fe63b0..04f3da2be 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -665,6 +665,7 @@ extern INT16 controlleroffsets[][2]; extern consvar_t cv_dummyprofilename; extern consvar_t cv_dummyprofileplayername; +extern consvar_t cv_dummyprofilekickstart; void M_InitOptions(INT32 choice); // necessary for multiplayer since there's some options we won't want to access void M_OptionsTick(void); diff --git a/src/k_menudef.c b/src/k_menudef.c index aa68b816d..deb339834 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -531,6 +531,12 @@ menuitem_t OPTIONS_ProfileControls[] = { {IT_CONTROL, "LUA/C", "May be used by add-ons.", NULL, M_ProfileSetControl, gc_luac, 0}, + + {IT_HEADER, "TOGGLES", "For per-player commands", + NULL, NULL, 0, 0}, + + {IT_CONTROL | IT_CVAR, "KICKSTART ACCEL", "Hold A to auto-accel. Tap it to cancel.", + NULL, &cv_dummyprofilekickstart, 0, 0}, }; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 4c0f60ddf..903e5f1fa 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2352,7 +2352,7 @@ void M_DrawProfileControls(void) V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL); if (currentMenu->menuitems[itemOn].tooltip != NULL) { - V_DrawCenteredThinString(BASEVIDWIDTH*2/3, 12, V_ALLOWLOWERCASE|V_6WIDTHSPACE, currentMenu->menuitems[itemOn].tooltip); + V_DrawCenteredThinString(229, 12, V_ALLOWLOWERCASE|V_6WIDTHSPACE, currentMenu->menuitems[itemOn].tooltip); } V_DrawFill(0, 0, 138, 200, 31); // Black border @@ -2390,7 +2390,20 @@ void M_DrawProfileControls(void) else V_DrawString(x, y+1, (i == itemOn ? highlightflags : 0), currentMenu->menuitems[i].text); - if (currentMenu->menuitems[i].status & IT_CONTROL) + if (currentMenu->menuitems[i].status & IT_CVAR) // not the proper way to check but this menu only has normal onoff cvars. + { + INT32 w; + consvar_t *cv = (consvar_t *)currentMenu->menuitems[i].itemaction; + + w = V_StringWidth(cv->string, 0); + V_DrawString(x + 12, y + 12, ((cv->flags & CV_CHEAT) && !CV_IsSetToDefault(cv) ? warningflags : highlightflags), cv->string); + if (i == itemOn) + { + V_DrawCharacter(x - (skullAnimCounter/5), y+12, '\x1C' | highlightflags, false); // left arrow + V_DrawCharacter(x + 12 + w + 2 + (skullAnimCounter/5) , y+12, '\x1D' | highlightflags, false); // right arrow + } + } + else if (currentMenu->menuitems[i].status & IT_CONTROL) { // Draw what the controls are mapped to for (k = 0; k < MAXINPUTMAPPING; k++) @@ -2426,6 +2439,7 @@ void M_DrawProfileControls(void) } } + y += spacing; break; } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 8b599568a..1a51525f0 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -213,6 +213,7 @@ consvar_t cv_dummyspectate = CVAR_INIT ("dummyspectate", "Spectator", CV_HIDDEN, consvar_t cv_dummyprofilename = CVAR_INIT ("dummyprofilename", "", CV_HIDDEN, NULL, NULL); consvar_t cv_dummyprofileplayername = CVAR_INIT ("dummyprofileplayername", "", CV_HIDDEN, NULL, NULL); +consvar_t cv_dummyprofilekickstart = CVAR_INIT ("dummyprofilekickstart", "Off", CV_HIDDEN, CV_OnOff, NULL); consvar_t cv_dummygpdifficulty = CVAR_INIT ("dummygpdifficulty", "Normal", CV_HIDDEN, dummygpdifficulty_cons_t, NULL); consvar_t cv_dummykartspeed = CVAR_INIT ("dummykartspeed", "Auto", CV_HIDDEN, dummykartspeed_cons_t, NULL); @@ -1689,6 +1690,7 @@ void M_Init(void) CV_RegisterVar(&cv_dummyprofilename); CV_RegisterVar(&cv_dummyprofileplayername); + CV_RegisterVar(&cv_dummyprofilekickstart); CV_RegisterVar(&cv_dummygpdifficulty); CV_RegisterVar(&cv_dummykartspeed); @@ -3734,11 +3736,13 @@ void M_HandleProfileSelect(INT32 ch) { CV_StealthSet(&cv_dummyprofilename, optionsmenu.profile->profilename); CV_StealthSet(&cv_dummyprofileplayername, optionsmenu.profile->playername); + CV_StealthSetValue(&cv_dummyprofilekickstart, optionsmenu.profile->kickstartaccel); } else { CV_StealthSet(&cv_dummyprofilename, ""); CV_StealthSet(&cv_dummyprofileplayername, ""); + CV_StealthSetValue(&cv_dummyprofilekickstart, 0); // off } M_SetupNextMenu(&OPTIONS_EditProfileDef, false); @@ -4013,6 +4017,9 @@ boolean M_ProfileControlsInputs(INT32 ch) M_SetMenuDelay(pid); return true; } + else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + optionsmenu.profile->kickstartaccel = cv_dummyprofilekickstart.value; // Make sure to save kickstart accel. + return false; } diff --git a/src/k_profiles.c b/src/k_profiles.c index ef8382036..1449650b1 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -151,6 +151,9 @@ void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum) CV_StealthSet(&cv_playername[playernum], p->playername); // @TODO followers + // toggles + CV_StealthSetValue(&cv_kickstartaccel[playernum], p->kickstartaccel); + // set controls... memcpy(&gamecontrol[playernum], p->controls, sizeof(gamecontroldefault)); } From f5bd0dc3e4afafe3cda4ba6822a83f6ced91f82d Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 26 Feb 2022 17:15:45 +0100 Subject: [PATCH 244/379] Make typing submenu into a struct --- src/k_menu.h | 25 ++++++++------ src/k_menudraw.c | 16 ++++----- src/k_menufunc.c | 88 ++++++++++++++++++++++-------------------------- 3 files changed, 63 insertions(+), 66 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 04f3da2be..d0389c1ea 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -361,20 +361,23 @@ extern INT16 skullAnimCounter; // skull animation counter extern INT32 menuKey; // keyboard key pressed for menu -extern boolean menutyping; // Typing sub-menu -extern boolean menutypingclose; // if true, we're closing the menu. -extern boolean keyboardtyping; // If true, all keystrokes are treated as typing (ignores MBT_A etc). This is unset if you try moving the cursor on the virtual keyboard or use your controller -extern SINT8 menutypingfade; // Fade-in and out for typing sub-menu. (in 10 tics) -// While typing, we'll have a fade strongly darken the screen to overlay the typing menu instead - extern INT16 virtualKeyboard[5][13]; extern INT16 shift_virtualKeyboard[5][13]; -// cursor position on the virtual keyboard grid -extern SINT8 keyboardx; -extern SINT8 keyboardy; -extern boolean keyboardcapslock; -extern boolean keyboardshift; +extern struct menutyping_s +{ + boolean active; // Active + boolean menutypingclose; // Closing + boolean keyboardtyping; // If true, all keystrokes are treated as typing (ignores MBT_A etc). This is unset if you try moving the cursor on the virtual keyboard or use your controller + SINT8 menutypingfade; // fade in and out + + SINT8 keyboardx; + SINT8 keyboardy; + boolean keyboardcapslock; + boolean keyboardshift; + +} menutyping; +// While typing, we'll have a fade strongly darken the screen to overlay the typing menu instead #define MENUDELAYTIME 7 diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 903e5f1fa..0ae4cb651 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -230,14 +230,14 @@ static void M_DrawMenuTyping(void) INT32 i, j; INT32 x = 60; - INT32 y = 100 + (9-menutypingfade)*8; - INT32 tflag = (9 - menutypingfade)<menuitems[itemOn].itemaction; char buf[8]; // We write there to use drawstring for convenience. - V_DrawFadeScreen(31, menutypingfade); + V_DrawFadeScreen(31, menutyping.menutypingfade); // Draw the string we're editing at the top. V_DrawString(x, y-48 + 12, V_ALLOWLOWERCASE|tflag, cv->string); @@ -245,7 +245,7 @@ static void M_DrawMenuTyping(void) V_DrawCharacter(x + V_StringWidth(cv->string, 0), y - 35, '_' | 0x80, false); // Some contextual stuff - if (keyboardtyping) + if (menutyping.keyboardtyping) V_DrawThinString(10, 175, V_ALLOWLOWERCASE|tflag|V_GRAYMAP, "Type using your keyboard. Press Enter to confirm & exit.\nUse your controller or any directional input to use the Virtual Keyboard.\n"); else V_DrawThinString(10, 175, V_ALLOWLOWERCASE|tflag|V_GRAYMAP, "Type using the Virtual Keyboard. Use the \'OK\' button to confirm & exit.\nPress any keyboard key not bound to a control to use it."); @@ -258,7 +258,7 @@ static void M_DrawMenuTyping(void) { INT32 mflag = 0; INT16 c = virtualKeyboard[i][j]; - if (keyboardshift ^ keyboardcapslock) + if (menutyping.keyboardshift ^ menutyping.keyboardcapslock) c = shift_virtualKeyboard[i][j]; @@ -284,9 +284,9 @@ static void M_DrawMenuTyping(void) } // highlight: - if (keyboardx == j && keyboardy == i && !keyboardtyping) + if (menutyping.keyboardx == j && menutyping.keyboardy == i && !menutyping.keyboardtyping) mflag |= highlightflags; - else if (keyboardtyping) + else if (menutyping.keyboardtyping) mflag |= V_TRANSLUCENT; // grey it out if we can't use it. V_DrawString(x, y, V_ALLOWLOWERCASE|tflag|mflag, buf); @@ -353,7 +353,7 @@ void M_Drawer(void) } // Draw typing overlay when needed, above all other menu elements. - if (menutyping) + if (menutyping.active) M_DrawMenuTyping(); } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 1a51525f0..6b25122d6 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -97,16 +97,10 @@ INT32 menuKey = -1; // keyboard key pressed for menu menucmd_t menucmd[MAXSPLITSCREENPLAYERS]; // Typing "sub"-menu -boolean menutyping = false; -boolean menutypingclose = false; -SINT8 menutypingfade = 0; -boolean keyboardtyping = false; -SINT8 keyboardx = 0; -SINT8 keyboardy = 0; -boolean keyboardcapslock = false; -boolean keyboardshift = false; +struct menutyping_s menutyping; +// keyboard layouts INT16 virtualKeyboard[5][13] = { {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0}, @@ -922,7 +916,7 @@ boolean M_Responder(event_t *ev) } // Typing for CV_IT_STRING - if (menutyping && !menutypingclose && keyboardtyping) + if (menutyping.active && !menutyping.menutypingclose && menutyping.keyboardtyping) { M_ChangeStringCvar(menuKey); } @@ -1194,8 +1188,8 @@ boolean M_MenuButtonPressed(UINT8 pid, UINT32 bt) static void M_UpdateKeyboardX(void) { // 0s are only at the rightmost edges of the keyboard table, so just go backwards until we get something. - while (!virtualKeyboard[keyboardy][keyboardx]) - keyboardx--; + while (!virtualKeyboard[menutyping.keyboardy][menutyping.keyboardx]) + menutyping.keyboardx--; } static boolean M_IsTypingKey(INT32 key) @@ -1211,25 +1205,25 @@ static void M_MenuTypingInput(INT32 key) // Fade-in - if (menutypingclose) // closing + if (menutyping.menutypingclose) // closing { - menutypingfade--; - if (!menutypingfade) - menutyping = false; + menutyping.menutypingfade--; + if (!menutyping.menutypingfade) + menutyping.active = false; return; // prevent inputs while closing the menu. } else // opening { - menutypingfade++; - if (menutypingfade > 9) // Don't fade all the way, but have it VERY strong to be readable - menutypingfade = 9; - else if (menutypingfade < 9) + menutyping.menutypingfade++; + if (menutyping.menutypingfade > 9) // Don't fade all the way, but have it VERY strong to be readable + menutyping.menutypingfade = 9; + else if (menutyping.menutypingfade < 9) return; // Don't allow typing until it's fully opened. } // Determine when to check for keyboard inputs or controller inputs using menuKey, which is the key passed here as argument. - if (!keyboardtyping) // controller inputs + if (!menutyping.keyboardtyping) // controller inputs { // we pressed a keyboard input that's not any of our buttons if (M_IsTypingKey(key) && menucmd[pid].dpad_lr == 0 && menucmd[pid].dpad_ud == 0 @@ -1240,7 +1234,7 @@ static void M_MenuTypingInput(INT32 key) && !(menucmd[pid].buttons & MBT_Y) && !(menucmd[pid].buttons & MBT_Z)) { - keyboardtyping = true; + menutyping.keyboardtyping = true; } } else // Keyboard inputs. @@ -1257,26 +1251,26 @@ static void M_MenuTypingInput(INT32 key) || menucmd[pid].dpad_ud != 0 )) { - keyboardtyping = false; + menutyping.keyboardtyping = false; return; } // OTHERWISE, process keyboard inputs for typing! if (key == KEY_ENTER) { - menutypingclose = true; // close menu. + menutyping.menutypingclose = true; // close menu. return; } } - if (menucmd[pid].delay == 0 && !keyboardtyping) // We must check for this here because we bypass the normal delay check to allow for normal keyboard inputs + if (menucmd[pid].delay == 0 && !menutyping.keyboardtyping) // We must check for this here because we bypass the normal delay check to allow for normal keyboard inputs { if (menucmd[pid].dpad_ud > 0) // down { - keyboardy++; - if (keyboardy > 4) - keyboardy = 0; + menutyping.keyboardy++; + if (menutyping.keyboardy > 4) + menutyping.keyboardy = 0; M_UpdateKeyboardX(); M_SetMenuDelay(pid); @@ -1284,9 +1278,9 @@ static void M_MenuTypingInput(INT32 key) } else if (menucmd[pid].dpad_ud < 0) // up { - keyboardy--; - if (keyboardy < 0) - keyboardy = 4; + menutyping.keyboardy--; + if (menutyping.keyboardy < 0) + menutyping.keyboardy = 4; M_UpdateKeyboardX(); M_SetMenuDelay(pid); @@ -1294,19 +1288,19 @@ static void M_MenuTypingInput(INT32 key) } else if (menucmd[pid].dpad_lr > 0) // right { - keyboardx++; - if (!virtualKeyboard[keyboardy][keyboardx]) - keyboardx = 0; + menutyping.keyboardx++; + if (!virtualKeyboard[menutyping.keyboardy][menutyping.keyboardx]) + menutyping.keyboardx = 0; M_SetMenuDelay(pid); S_StartSound(NULL, sfx_menu1); } else if (menucmd[pid].dpad_lr < 0) // left { - keyboardx--; - if (keyboardx < 0) + menutyping.keyboardx--; + if (menutyping.keyboardx < 0) { - keyboardx = 12; + menutyping.keyboardx = 12; M_UpdateKeyboardX(); } M_SetMenuDelay(pid); @@ -1315,23 +1309,23 @@ static void M_MenuTypingInput(INT32 key) else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) { // Add the character. First though, check what we're pressing.... - INT16 c = virtualKeyboard[keyboardy][keyboardx]; - if (keyboardshift ^ keyboardcapslock) - c = shift_virtualKeyboard[keyboardy][keyboardx]; + INT16 c = virtualKeyboard[menutyping.keyboardy][menutyping.keyboardx]; + if (menutyping.keyboardshift ^ menutyping.keyboardcapslock) + c = shift_virtualKeyboard[menutyping.keyboardy][menutyping.keyboardx]; if (c == KEY_RSHIFT) - keyboardshift = !keyboardshift; + menutyping.keyboardshift = !menutyping.keyboardshift; else if (c == KEY_CAPSLOCK) - keyboardcapslock = !keyboardcapslock; + menutyping.keyboardcapslock = !menutyping.keyboardcapslock; else if (c == KEY_ENTER) { - menutypingclose = true; // close menu. + menutyping.menutypingclose = true; // close menu. return; } else { M_ChangeStringCvar((INT32)c); // Write! - keyboardshift = false; // undo shift if it had been pressed + menutyping.keyboardshift = false; // undo shift if it had been pressed } M_SetMenuDelay(pid); @@ -1353,7 +1347,7 @@ static void M_HandleMenuInput(void) } // Typing for CV_IT_STRING - if (menutyping) + if (menutyping.active) { M_MenuTypingInput(menuKey); return; @@ -1427,9 +1421,9 @@ static void M_HandleMenuInput(void) // If we're hovering over a IT_CV_STRING option, pressing A/X opens the typing submenu if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) { - keyboardtyping = menuKey != 0 ? true : false; // If we entered this menu by pressing a menu Key, default to keyboard typing, otherwise use controller. - menutyping = true; - menutypingclose = false; + menutyping.keyboardtyping = menuKey != 0 ? true : false; // If we entered this menu by pressing a menu Key, default to keyboard typing, otherwise use controller. + menutyping.active = true; + menutyping.menutypingclose = false; return; } From 2696effe7afe97f06fdab81b642e682098922fa0 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sun, 27 Feb 2022 00:15:31 +0100 Subject: [PATCH 245/379] Make message boxes separate overlays --- src/k_menu.h | 24 +++++ src/k_menudraw.c | 93 +++++++++++++++++-- src/k_menufunc.c | 235 +++++++++++++++++++++++++++-------------------- 3 files changed, 241 insertions(+), 111 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index d0389c1ea..b32d1d97d 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -379,6 +379,30 @@ extern struct menutyping_s } menutyping; // While typing, we'll have a fade strongly darken the screen to overlay the typing menu instead +typedef enum +{ + MA_NONE = 0, + MA_YES, + MA_NO +} manswer_e; + +#define MAXMENUMESSAGE 256 +extern struct menumessage_s +{ + boolean active; + INT32 flags; // MM_ + char message[MAXMENUMESSAGE]; // message to display + + SINT8 fadetimer; // opening + INT32 x; + INT32 y; + INT32 m; + + void (*routine)(INT32 choice); // Normal routine + void (*eroutine)(event_t *ev); // Event routine (MM_EVENTHANDLER) +} menumessage; + +void M_HandleMenuMessage(void); #define MENUDELAYTIME 7 #define MENUMINDELAY 2 diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 0ae4cb651..9d908f5d6 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -298,6 +298,68 @@ static void M_DrawMenuTyping(void) } } +// Draw the message popup submenu +static void M_DrawMenuMessage(void) +{ + + INT32 y = menumessage.y + (9-menumessage.fadetimer)*20; + size_t i, start = 0; + INT16 max; + char string[MAXMENUMESSAGE]; + INT32 mlines; + const char *msg = menumessage.message; + + mlines = menumessage.m>>8; + max = (INT16)((UINT8)(menumessage.m & 0xFF)*8); + + V_DrawFadeScreen(31, menumessage.fadetimer); + M_DrawTextBox(menumessage.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, MAXMENUMESSAGE); + if (i >= MAXMENUMESSAGE) + { + CONS_Printf("M_DrawMenuMessage: 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 >= MAXMENUMESSAGE) + { + CONS_Printf("M_DrawMenuMessage: 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; + } +} + // // M_Drawer // Called after the view has been rendered, @@ -352,6 +414,11 @@ void M_Drawer(void) } + + // Draw message overlay when needed + if (menumessage.active) + M_DrawMenuMessage(); + // Draw typing overlay when needed, above all other menu elements. if (menutyping.active) M_DrawMenuTyping(); @@ -648,7 +715,7 @@ void M_DrawMessageMenu(void) INT32 y = currentMenu->y; size_t i, start = 0; INT16 max; - char string[MAXMSGLINELEN]; + char string[MAXMENUMESSAGE]; INT32 mlines; const char *msg = currentMenu->menuitems[0].text; @@ -665,8 +732,8 @@ void M_DrawMessageMenu(void) { if (*(msg+start+i) == '\n') { - memset(string, 0, MAXMSGLINELEN); - if (i >= MAXMSGLINELEN) + memset(string, 0, MAXMENUMESSAGE); + if (i >= MAXMENUMESSAGE) { CONS_Printf("M_DrawMessageMenu: too long segment in %s\n", msg); return; @@ -685,7 +752,7 @@ void M_DrawMessageMenu(void) if (i == strlen(msg+start)) { - if (i >= MAXMSGLINELEN) + if (i >= MAXMENUMESSAGE) { CONS_Printf("M_DrawMessageMenu: too long segment in %s\n", msg); return; @@ -2448,13 +2515,21 @@ void M_DrawProfileControls(void) // Overlay for control binding if (optionsmenu.bindcontrol) { - V_DrawFadeScreen(31, 8); + INT16 reversetimer = TICRATE*5 - optionsmenu.bindtimer; + INT32 fade = reversetimer; + INT32 ypos; - M_DrawTextBox((BASEVIDWIDTH/2) - (120), (BASEVIDHEIGHT/2) - (16), 30, 4); + if (fade > 9) + fade = 9; - V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2) - (4), 0, va("Press key #%d for control", optionsmenu.bindcontrol)); - V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2) - (4) +10, 0, va("\"%s\"", currentMenu->menuitems[itemOn].text)); - V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2) - (4) +20, highlightflags, va("(WAIT %d SECONDS TO SKIP)", optionsmenu.bindtimer/TICRATE)); + ypos = (BASEVIDHEIGHT/2) - 4 +16*(9 - fade); + V_DrawFadeScreen(31, fade); + + M_DrawTextBox((BASEVIDWIDTH/2) - (120), ypos - 12, 30, 4); + + V_DrawCenteredString(BASEVIDWIDTH/2, ypos, 0, va("Press key #%d for control", optionsmenu.bindcontrol)); + V_DrawCenteredString(BASEVIDWIDTH/2, ypos +10, 0, va("\"%s\"", currentMenu->menuitems[itemOn].text)); + V_DrawCenteredString(BASEVIDWIDTH/2, ypos +20, highlightflags, va("(WAIT %d SECONDS TO SKIP)", optionsmenu.bindtimer/TICRATE)); } } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 6b25122d6..49e74e9f4 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -96,8 +96,10 @@ struct menutransition_s menutransition; // Menu transition properties INT32 menuKey = -1; // keyboard key pressed for menu menucmd_t menucmd[MAXSPLITSCREENPLAYERS]; -// Typing "sub"-menu +// message prompt struct +struct menumessage_s menumessage; +// Typing "sub"-menu struct menutyping_s menutyping; // keyboard layouts @@ -455,7 +457,7 @@ static void M_EraseDataResponse(INT32 ch) { UINT8 i; - if (ch != 'y' && ch != KEY_ENTER) + if (ch == MA_NO) return; S_StartSound(NULL, sfx_itrole); // bweh heh heh @@ -480,7 +482,7 @@ static void M_EraseDataResponse(INT32 ch) 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"); + const char *eschoice, *esstr = M_GetText("Are you sure you want to erase\n%s?\n\n(Press A to confirm)\n"); optionsmenu.erasecontext = (UINT8)choice; @@ -833,6 +835,14 @@ boolean M_Responder(event_t *ev) return true; // eat events. } + // event handler for MM_EVENTHANDLER + if (menumessage.active && menumessage.flags == MM_EVENTHANDLER && menumessage.routine) + { + CONS_Printf("MM_EVENTHANDLER...\n"); + menumessage.eroutine(ev); // What a terrible hack... + return true; + } + // Handle menu handling in-game. if (menuactive == false) { @@ -999,6 +1009,9 @@ void M_ClearMenus(boolean callexitmenufunc) if (gamestate == GS_MENU) // Back to title screen D_StartTitle(); + menutyping.active = false; + menumessage.active = false; + menuactive = false; } @@ -1346,6 +1359,12 @@ static void M_HandleMenuInput(void) return; } + if (menumessage.active) + { + M_HandleMenuMessage(); + return; + } + // Typing for CV_IT_STRING if (menutyping.active) { @@ -1375,41 +1394,6 @@ static void M_HandleMenuInput(void) return; } - // TODO: Move this to message menu code - if (currentMenu->menuitems[itemOn].status == IT_MSGHANDLER) - { - if (currentMenu->menuitems[itemOn].mvar1 != MM_EVENTHANDLER) - { - if (menucmd[pid].buttons != 0 && menucmd[pid].buttonsHeld == 0) - { - if (routine) - { - routine(menuKey); - } - - M_StopMessage(0); - noFurtherInput = true; - M_SetMenuDelay(pid); - return; - } - - return; - } - else - { -//#if 0 // this shit is crazy - if (routine) - { - //void (*otherroutine)(event_t *sev) = currentMenu->menuitems[itemOn].itemaction; - //otherroutine(menuKey); //Alam: what a hack - routine(menuKey); - } -//#endif - - return; - } - } - // BP: one of the more big hack i have never made if (routine && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_CVAR) { @@ -1727,7 +1711,7 @@ menu_t MessageDef = 0, 0, // x, y (TO HACK) 0, 0, // extra1, extra2 0, 0, // transition tics - M_DrawMessageMenu, // drawing routine -> + NULL, // drawing routine -> NULL, // ticker routine NULL, // init routine NULL, // quit routine @@ -1753,12 +1737,16 @@ static inline size_t M_StringHeight(const char *string) // default message handler void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtype) { + + const UINT8 pid = 0; size_t max = 0, start = 0, i, strlines; static char *message = NULL; Z_Free(message); message = Z_StrDup(string); DEBFILE(message); + CONS_Printf("M_StartMessage()...\n"); + // Rudementary word wrapping. // Simple and effective. Does not handle nonuniform letter sizes, colors, etc. but who cares. strlines = 0; @@ -1789,34 +1777,28 @@ void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtyp } } + strncpy(menumessage.message, string, MAXMENUMESSAGE); + menumessage.flags = itemtype; + menumessage.routine = routine; + menumessage.fadetimer = 1; + menumessage.active = true; + start = 0; max = 0; - M_StartControlPanel(); // can't put menuactive to true - - if (currentMenu == &MessageDef) // Prevent recursion - MessageDef.prevMenu = ((demo.playback) ? &PAUSE_PlaybackMenuDef : &MainDef); - else - MessageDef.prevMenu = currentMenu; - - MessageDef.menuitems[0].text = message; - MessageDef.menuitems[0].mvar1 = (UINT8)itemtype; - if (!routine && itemtype != MM_NOTHING) itemtype = MM_NOTHING; - switch (itemtype) + if (!routine || menumessage.flags == MM_NOTHING) { - 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; + menumessage.flags = MM_NOTHING; + menumessage.routine = M_StopMessage; } + + // event routine + if (menumessage.flags == MM_EVENTHANDLER) + { + menumessage.eroutine = routine; + menumessage.routine = NULL; + } + //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++) @@ -1838,21 +1820,73 @@ void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtyp start += i; } - MessageDef.x = (INT16)((BASEVIDWIDTH - 8*max-16)/2); - MessageDef.y = (INT16)((BASEVIDHEIGHT - M_StringHeight(message))/2); + menumessage.x = (INT16)((BASEVIDWIDTH - 8*max-16)/2); + menumessage.y = (INT16)((BASEVIDHEIGHT - M_StringHeight(message))/2); - MessageDef.lastOn = (INT16)((strlines<<8)+max); + menumessage.m = (INT16)((strlines<<8)+max); - //M_SetupNextMenu(); - currentMenu = &MessageDef; - itemOn = 0; + M_SetMenuDelay(pid); // Set menu delay to avoid setting off any of the handlers. } void M_StopMessage(INT32 choice) { - (void)choice; - if (menuactive) - M_SetupNextMenu(MessageDef.prevMenu, true); + const char pid = 0; + (void) choice; + + menumessage.active = false; + M_SetMenuDelay(pid); +} + +// regular handler for MM_NOTHING and MM_YESNO +void M_HandleMenuMessage(void) +{ + const UINT8 pid = 0; + boolean btok = M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X); + boolean btnok = M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y); + + menumessage.fadetimer++; + + if (menumessage.fadetimer > 9) + menumessage.fadetimer = 9; + + + switch (menumessage.flags) + { + // Send 1 to the routine if we're pressing A/B/X/Y + case MM_NOTHING: + { + // send 1 if any button is pressed, 0 otherwise. + if (btok || btnok) + menumessage.routine(0); + + break; + } + // Send 1 to the routine if we're pressing A/X, 2 if B/Y, 0 otherwise. + case MM_YESNO: + { + INT32 answer = MA_NONE; + if (btok) + answer = MA_YES; + else if (btnok) + answer = MA_NO; + + // send 1 if btok is pressed, 2 if nok is pressed, 0 otherwise. + if (answer) + { + menumessage.routine(answer); + M_StopMessage(0); + } + + break; + } + // MM_EVENTHANDLER: In M_Responder to allow full event compat. + default: + break; + } + + // if we detect any keypress, don't forget to set the menu delay regardless. + if (btok || btnok) + M_SetMenuDelay(pid); } // ========= @@ -1941,27 +1975,26 @@ void M_QuitResponse(INT32 ch) tic_t ptime; INT32 mrand; - if (ch != 'y' && ch != KEY_ENTER) - return; - - if (!(netgame || cv_debug)) + if (ch == MA_YES) { - 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()) + if (!(netgame || cv_debug)) { - 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(); - } - } + mrand = M_RandomKey(sizeof(quitsounds) / sizeof(INT32)); + if (quitsounds[mrand]) + S_StartSound(NULL, quitsounds[mrand]); - I_Quit(); + //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(); + } } void M_QuitSRB2(INT32 choice) @@ -1969,7 +2002,7 @@ 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("Are you sure you want to quit playing?\n\n(Press 'Y' to exit)", M_QuitResponse, MM_YESNO); + M_StartMessage("Are you sure you want to quit playing?\n\n(Press A to exit)", M_QuitResponse, MM_YESNO); } // ========= @@ -2577,7 +2610,7 @@ static void M_MPConfirmCharacterSelection(void) static void M_MPConfirmCharacterResponse(INT32 ch) { - if (ch == 'y' || ch == KEY_ENTER) + if (ch == MA_YES) M_MPConfirmCharacterSelection(); M_ClearMenus(true); @@ -2665,7 +2698,7 @@ void M_CharacterSelectTick(void) if (playeringame[j] && !players[consoleplayer].spectator) { // Warn the player! - M_StartMessage(M_GetText("Any player who has changed skin will\nautomatically spectate. Proceed?\n(Press 'Y' to confirm)\n"), M_MPConfirmCharacterResponse, MM_YESNO); + M_StartMessage(M_GetText("Any player who has changed skin will\nautomatically spectate. Proceed?\n(Press A to confirm)\n"), M_MPConfirmCharacterResponse, MM_YESNO); return; } } @@ -3936,6 +3969,7 @@ static void SetDeviceOnPress(void) } } + // Prompt a device selection window (just tap any button on the device you want) void M_ProfileDeviceSelect(INT32 choice) { @@ -3949,7 +3983,6 @@ void M_ProfileDeviceSelect(INT32 choice) optionsmenu.contx = optionsmenu.tcontx = controlleroffsets[gc_a][0]; optionsmenu.conty = optionsmenu.tconty = controlleroffsets[gc_a][1]; - //M_StartMessage(M_GetText("Press any key on the device\nyou would like to use"), M_ProfileDeviceSelectResponse, MM_EVENTHANDLER); M_SetupNextMenu(&OPTIONS_ProfileControlsDef, false); // Don't set device here anymore. } @@ -4445,11 +4478,9 @@ void M_ConfirmEnterGame(INT32 choice) static void M_ExitGameResponse(INT32 ch) { - if (ch != 'y' && ch != KEY_ENTER) - return; + if (ch == MA_YES) + G_SetExitGameFlag(); - //Command_ExitGame_f(); - G_SetExitGameFlag(); M_ClearMenus(true); } @@ -4462,7 +4493,7 @@ void M_EndGame(INT32 choice) if (!Playing()) return; - M_StartMessage(M_GetText("Are you sure you want to return\nto the title screen?\n(Press 'Y' to confirm)\n"), M_ExitGameResponse, MM_YESNO); + M_StartMessage(M_GetText("Are you sure you want to return\nto the title screen?\n(Press A to confirm)\n"), M_ExitGameResponse, MM_YESNO); } @@ -4951,7 +4982,7 @@ boolean M_AddonsRefresh(void) if (message) { - M_StartMessage(message,M_AddonsClearName,MM_EVENTHANDLER); + M_StartMessage(message,M_AddonsClearName,MM_YESNO); return true; } @@ -4964,7 +4995,7 @@ boolean M_AddonsRefresh(void) static void M_AddonExec(INT32 ch) { - if (ch != 'y' && ch != KEY_ENTER) + if (ch == MA_NO) return; S_StartSound(NULL, sfx_zoom); @@ -5117,11 +5148,11 @@ void M_HandleAddons(INT32 choice) 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); + M_StartMessage(va("%c%s\x80\nThis file may not be a console script.\nAttempt to run anyways? \n\n(Press A 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); + M_AddonExec(MA_YES); break; case EXT_LUA: From 1c0a849b6fd3c664f62c16fe41475ab7ecb593f9 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sun, 27 Feb 2022 00:24:19 +0100 Subject: [PATCH 246/379] Fix addons & replay hut menus --- src/k_menufunc.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 49e74e9f4..4be5728b6 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -4704,7 +4704,7 @@ void M_HandleReplayHutList(INT32 choice) const UINT8 pid = 0; (void) choice; - if (menucmd[pid].dpad_ud > 0) + if (menucmd[pid].dpad_ud < 0) { if (dir_on[menudepthleft]) dir_on[menudepthleft]--; @@ -4713,10 +4713,11 @@ void M_HandleReplayHutList(INT32 choice) //M_PrevOpt(); S_StartSound(NULL, sfx_menu1); + M_SetMenuDelay(pid); extrasmenu.replayScrollTitle = 0; extrasmenu.replayScrollDelay = TICRATE; extrasmenu.replayScrollDir = 1; } - else if (menucmd[pid].dpad_ud < 0) + else if (menucmd[pid].dpad_ud > 0) { if (dir_on[menudepthleft] < sizedirmenu-1) dir_on[menudepthleft]++; @@ -4725,16 +4726,19 @@ void M_HandleReplayHutList(INT32 choice) //itemOn = 0; // Not M_NextOpt because that would take us to the extra dummy item S_StartSound(NULL, sfx_menu1); + M_SetMenuDelay(pid); extrasmenu.replayScrollTitle = 0; extrasmenu.replayScrollDelay = TICRATE; extrasmenu.replayScrollDir = 1; } else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) { + M_SetMenuDelay(pid); M_QuitReplayHut(); } else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) { + M_SetMenuDelay(pid); switch (dirmenu[dir_on[menudepthleft]][DIR_TYPE]) { case EXT_FOLDER: @@ -5063,17 +5067,19 @@ void M_HandleAddons(INT32 choice) #endif } - if (menucmd[pid].dpad_ud < 0) + if (menucmd[pid].dpad_ud > 0) { if (dir_on[menudepthleft] < sizedirmenu-1) dir_on[menudepthleft]++; S_StartSound(NULL, sfx_menu1); + M_SetMenuDelay(pid); } - else if (menucmd[pid].dpad_ud > 0) + else if (menucmd[pid].dpad_ud < 0) { if (dir_on[menudepthleft]) dir_on[menudepthleft]--; S_StartSound(NULL, sfx_menu1); + M_SetMenuDelay(pid); } else if (M_MenuButtonPressed(pid, MBT_L)) @@ -5083,6 +5089,7 @@ void M_HandleAddons(INT32 choice) dir_on[menudepthleft]++; S_StartSound(NULL, sfx_menu1); + M_SetMenuDelay(pid); } else if (M_MenuButtonPressed(pid, MBT_R)) @@ -5092,11 +5099,14 @@ void M_HandleAddons(INT32 choice) dir_on[menudepthleft]--; S_StartSound(NULL, sfx_menu1); + M_SetMenuDelay(pid); } else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) { boolean refresh = true; + M_SetMenuDelay(pid); + if (!dirmenu[dir_on[menudepthleft]]) S_StartSound(NULL, sfx_s26d); else @@ -5175,7 +5185,8 @@ void M_HandleAddons(INT32 choice) } else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) { - exitmenu = true; + exitmenu = true; + M_SetMenuDelay(pid); } @@ -5190,6 +5201,8 @@ void M_HandleAddons(INT32 choice) M_SetupNextMenu(currentMenu->prevMenu, false); else M_ClearMenus(true); + + M_SetMenuDelay(pid); } } From b3410f7eefd999e103a823fd3ebd655590844c98 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sun, 27 Feb 2022 00:29:29 +0100 Subject: [PATCH 247/379] Fix manual menu --- src/k_menufunc.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 4be5728b6..bf2369ace 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1897,30 +1897,32 @@ void M_HandleMenuMessage(void) // uses left and right movement. void M_HandleImageDef(INT32 choice) { + const UINT8 pid = 0; boolean exitmenu = false; + (void) choice; - switch (choice) + if (menucmd[pid].dpad_lr > 0) { - case KEY_RIGHTARROW: - if (itemOn >= (INT16)(currentMenu->numitems-1)) - break; - S_StartSound(NULL, sfx_s3k5b); - itemOn++; - break; - - case KEY_LEFTARROW: - if (!itemOn) - break; - - S_StartSound(NULL, sfx_s3k5b); - itemOn--; - break; - - case KEY_ESCAPE: - case KEY_ENTER: - exitmenu = true; - break; + if (itemOn >= (INT16)(currentMenu->numitems-1)) + return; + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + itemOn++; } + else if (menucmd[pid].dpad_lr < 0) + { + if (!itemOn) + return; + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + itemOn--; + } + else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_X) || M_MenuButtonPressed(pid, MBT_Y)) + { + exitmenu = true; + M_SetMenuDelay(pid); + } + if (exitmenu) { From c85cc0f182ad4e7af8612627b6c5de6589f54d25 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 29 Mar 2022 17:15:18 +0100 Subject: [PATCH 248/379] Update all data files that have the "kart" prefix to "ring", so that you can use this in the same folder as an existing non-newmenus install. ringconfig.cfg ringprofiles.cfg ringsavedips.txt ringexec.cfg ringserv.cfg ringdata.dat replay folder "ringracers" Screenshots/gifs do not currently have adjusted name to prevent name leakage, we can change this later. --- src/d_main.c | 4 ++-- src/g_game.c | 4 ++-- src/k_profiles.h | 2 +- src/m_misc.h | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index aaf485731..295130374 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1715,9 +1715,9 @@ void D_SRB2Main(void) // user settings come before "+" parameters. if (dedicated) - COM_ImmedExecute(va("exec \"%s"PATHSEP"kartserv.cfg\"\n", srb2home)); + COM_ImmedExecute(va("exec \"%s"PATHSEP"ringserv.cfg\"\n", srb2home)); else - COM_ImmedExecute(va("exec \"%s"PATHSEP"kartexec.cfg\" -noerror\n", srb2home)); + COM_ImmedExecute(va("exec \"%s"PATHSEP"ringexec.cfg\" -noerror\n", srb2home)); if (!autostart) M_PushSpecialParameters(); // push all "+" parameters at the command buffer diff --git a/src/g_game.c b/src/g_game.c index 9ccd0fc0d..45682aae9 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -74,8 +74,8 @@ JoyType_t Joystick[MAXSPLITSCREENPLAYERS]; #define SAVEGAMESIZE (1024) // SRB2kart -char gamedatafilename[64] = "kartdata.dat"; -char timeattackfolder[64] = "kart"; +char gamedatafilename[64] = "ringdata.dat"; +char timeattackfolder[64] = "ringracers"; char customversionstring[32] = "\0"; static void G_DoCompleted(void); diff --git a/src/k_profiles.h b/src/k_profiles.h index 33c4575d8..070587284 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -28,7 +28,7 @@ #define PROFILENAMELEN 6 #define PROFILEVER 1 #define MAXPROFILES 16 -#define PROFILESFILE "kartprofiles.cfg" +#define PROFILESFILE "ringprofiles.cfg" #define PROFILEDEFAULTNAME "guest" #define PROFILEDEFAULTPNAME "Player" diff --git a/src/m_misc.h b/src/m_misc.h index c85aaad8e..7c12fe841 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -40,10 +40,10 @@ void M_SaveFrame(void); void M_StopMovie(void); // the file where game vars and settings are saved -#define CONFIGFILENAME "kartconfig.cfg" +#define CONFIGFILENAME "ringconfig.cfg" // The file where we'll save the last IPs we joined -#define IPLOGFILE "kartsavedips.txt" +#define IPLOGFILE "ringsavedips.txt" #define IPLOGFILESEP ";" #define NUMLOGIP 3 From 4cc587b5b28887349a02fb1856a7cc6afd7aeff6 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 29 Mar 2022 21:08:36 +0100 Subject: [PATCH 249/379] * Make the scrolling text on the menu interpolate. * Replace the shitty vidwait option with Sal's shiny new fpscap option. * Update the item toggles menu for all the new items (and re-enable shitsfree for the new empty spots). --- src/k_hud.c | 2 ++ src/k_menu.h | 1 + src/k_menudef.c | 72 +++++++++++++++++++++++++++------------------- src/k_menudraw.c | 75 ++++++++++++++++++++++++++++++++---------------- src/k_menufunc.c | 19 ++++++++---- src/p_user.c | 2 +- 6 files changed, 112 insertions(+), 59 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index 206da4e04..1b67abc75 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -662,6 +662,8 @@ const char *K_GetItemPatch(UINT8 item, boolean tiny) return (tiny ? "K_ISMINE" : "K_ITMINE"); case KITEM_LANDMINE: return (tiny ? "K_ISLNDM" : "K_ITLNDM"); + case KITEM_DROPTARGET: + return (tiny ? "K_ISDTRG" : "K_ITDTRG"); case KITEM_BALLHOG: return (tiny ? "K_ISBHOG" : "K_ITBHOG"); case KITEM_SPB: diff --git a/src/k_menu.h b/src/k_menu.h index 8717946dc..4d35962e5 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -861,6 +861,7 @@ void M_DrawEditProfile(void); void M_DrawProfileControls(void); void M_DrawVideoModes(void); void M_DrawItemToggles(void); +extern tic_t shitsfree; // Extras menu: void M_DrawExtrasMovingButton(void); diff --git a/src/k_menudef.c b/src/k_menudef.c index d9084ed58..694a9ade9 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -11,6 +11,7 @@ #include "console.h" // console cvars #include "filesrch.h" // addons cvars #include "m_misc.h" // screenshot cvars +#include "r_fps.h" // fps cvars #include "discord.h" // discord rpc cvars // ========================================================================== @@ -578,8 +579,8 @@ menuitem_t OPTIONS_Video[] = {IT_STRING | IT_CVAR | IT_CV_SLIDER, "Gamma", "Adjusts the overall brightness of the game.", NULL, {.cvar = &cv_globalgamma}, 0, 0}, - {IT_STRING | IT_CVAR, "Vertical Sync", "Locks the framerate to your monitor's refresh rate.", - NULL, {.cvar = &cv_vidwait}, 0, 0}, + {IT_STRING | IT_CVAR, "FPS Cap", "Handles the refresh rate of the game (does not affect gamelogic).", + NULL, {.cvar = &cv_fpscap}, 0, 0}, {IT_STRING | IT_CVAR, "Enable Skyboxes", "Turning this off will improve performance at the detriment of visuals for many maps.", NULL, {.cvar = &cv_skybox}, 0, 0}, @@ -926,32 +927,45 @@ menu_t OPTIONS_GameplayDef = { menuitem_t OPTIONS_GameplayItems[] = { // Mostly handled by the drawing function. - {IT_KEYHANDLER | IT_NOTHING, "Sneakers", NULL, NULL, {.routine = M_HandleItemToggles}, KITEM_SNEAKER, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Sneakers x3", NULL, NULL, {.routine = M_HandleItemToggles}, KRITEM_TRIPLESNEAKER, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Toggle All", NULL, NULL, {.routine = M_HandleItemToggles}, 0, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Rocket Sneakers", NULL, NULL, {.routine = M_HandleItemToggles}, KITEM_ROCKETSNEAKER, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Bananas", NULL, NULL, {.routine = M_HandleItemToggles}, KITEM_BANANA, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Bananas x3", NULL, NULL, {.routine = M_HandleItemToggles}, KRITEM_TRIPLEBANANA, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Bananas x10", NULL, NULL, {.routine = M_HandleItemToggles}, KRITEM_TENFOLDBANANA, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Eggman Monitors", NULL, NULL, {.routine = M_HandleItemToggles}, KITEM_EGGMAN, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Orbinauts", NULL, NULL, {.routine = M_HandleItemToggles}, KITEM_ORBINAUT, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Orbinauts x3", NULL, NULL, {.routine = M_HandleItemToggles}, KRITEM_TRIPLEORBINAUT, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Orbinauts x4", NULL, NULL, {.routine = M_HandleItemToggles}, KRITEM_QUADORBINAUT, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Mines", NULL, NULL, {.routine = M_HandleItemToggles}, KITEM_MINE, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Jawz", NULL, NULL, {.routine = M_HandleItemToggles}, KITEM_JAWZ, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Jawz x2", NULL, NULL, {.routine = M_HandleItemToggles}, KRITEM_DUALJAWZ, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Ballhogs", NULL, NULL, {.routine = M_HandleItemToggles}, KITEM_BALLHOG, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Self-Propelled Bombs", NULL, NULL, {.routine = M_HandleItemToggles}, KITEM_SPB, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Invinciblity", NULL, NULL, {.routine = M_HandleItemToggles}, KITEM_INVINCIBILITY, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Grow", NULL, NULL, {.routine = M_HandleItemToggles}, KITEM_GROW, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Shrink", NULL, NULL, {.routine = M_HandleItemToggles}, KITEM_SHRINK, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Thunder Shields", NULL, NULL, {.routine = M_HandleItemToggles}, KITEM_THUNDERSHIELD, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Bubble Shields", NULL, NULL, {.routine = M_HandleItemToggles}, KITEM_BUBBLESHIELD, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Flame Shields", NULL, NULL, {.routine = M_HandleItemToggles}, KITEM_FLAMESHIELD, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Hyudoros", NULL, NULL, {.routine = M_HandleItemToggles}, KITEM_HYUDORO, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Pogo Springs", NULL, NULL, {.routine = M_HandleItemToggles}, KITEM_POGOSPRING, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Super Rings", NULL, NULL, {.routine = M_HandleItemToggles}, KITEM_SUPERRING, 0}, - {IT_KEYHANDLER | IT_NOTHING, "Kitchen Sinks", NULL, NULL, {.routine = M_HandleItemToggles}, KITEM_KITCHENSINK, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Super Rings", NULL, {.routine = M_HandleItemToggles}, KITEM_SUPERRING, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Self-Propelled Bombs", NULL, {.routine = M_HandleItemToggles}, KITEM_SPB, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Eggman Marks", NULL, {.routine = M_HandleItemToggles}, KITEM_EGGMAN, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Toggle All", NULL, {.routine = M_HandleItemToggles}, 0, 0}, + + {IT_KEYHANDLER | IT_NOTHING, NULL, "Sneakers", NULL, {.routine = M_HandleItemToggles}, KITEM_SNEAKER, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Sneakers x2", NULL, {.routine = M_HandleItemToggles}, KRITEM_DUALSNEAKER, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Sneakers x3", NULL, {.routine = M_HandleItemToggles}, KRITEM_TRIPLESNEAKER, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Rocket Sneakers", NULL, {.routine = M_HandleItemToggles}, KITEM_ROCKETSNEAKER, 0}, + + {IT_KEYHANDLER | IT_NOTHING, NULL, "Bananas", NULL, {.routine = M_HandleItemToggles}, KITEM_BANANA, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Bananas x3", NULL, {.routine = M_HandleItemToggles}, KRITEM_TRIPLEBANANA, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Bananas x10", NULL, {.routine = M_HandleItemToggles}, KRITEM_TENFOLDBANANA, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Mines", NULL, {.routine = M_HandleItemToggles}, KITEM_MINE, 0}, + + {IT_KEYHANDLER | IT_NOTHING, NULL, "Orbinauts", NULL, {.routine = M_HandleItemToggles}, KITEM_ORBINAUT, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Orbinauts x3", NULL, {.routine = M_HandleItemToggles}, KRITEM_TRIPLEORBINAUT, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Orbinauts x4", NULL, {.routine = M_HandleItemToggles}, KRITEM_QUADORBINAUT, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Drop Targets", NULL, {.routine = M_HandleItemToggles}, KITEM_DROPTARGET, sfx_s258}, + + {IT_KEYHANDLER | IT_NOTHING, NULL, "Jawz", NULL, {.routine = M_HandleItemToggles}, KITEM_JAWZ, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Jawz x2", NULL, {.routine = M_HandleItemToggles}, KRITEM_DUALJAWZ, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Ballhogs", NULL, {.routine = M_HandleItemToggles}, KITEM_BALLHOG, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Land Mines", NULL, {.routine = M_HandleItemToggles}, KITEM_LANDMINE, 0}, + + {IT_KEYHANDLER | IT_NOTHING, NULL, "Thunder Shields", NULL, {.routine = M_HandleItemToggles}, KITEM_THUNDERSHIELD, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Bubble Shields", NULL, {.routine = M_HandleItemToggles}, KITEM_BUBBLESHIELD, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Flame Shields", NULL, {.routine = M_HandleItemToggles}, KITEM_FLAMESHIELD, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Hyudoros", NULL, {.routine = M_HandleItemToggles}, KITEM_HYUDORO, 0}, + + {IT_KEYHANDLER | IT_NOTHING, NULL, "Invinciblity", NULL, {.routine = M_HandleItemToggles}, KITEM_INVINCIBILITY, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Grow", NULL, {.routine = M_HandleItemToggles}, KITEM_GROW, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Shrink", NULL, {.routine = M_HandleItemToggles}, KITEM_SHRINK, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, NULL, NULL, {.routine = M_HandleItemToggles}, 255, 0}, + + {IT_KEYHANDLER | IT_NOTHING, NULL, "Pogo Springs", NULL, {.routine = M_HandleItemToggles}, KITEM_POGOSPRING, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Kitchen Sinks", NULL, {.routine = M_HandleItemToggles}, KITEM_KITCHENSINK, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, NULL, NULL, {.routine = M_HandleItemToggles}, 255, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, NULL, NULL, {.routine = M_HandleItemToggles}, 255, 0} }; menu_t OPTIONS_GameplayItemsDef = { @@ -959,7 +973,7 @@ menu_t OPTIONS_GameplayItemsDef = { &OPTIONS_GameplayDef, 0, OPTIONS_GameplayItems, - 0, 75, + 14, 40, SKINCOLOR_SCARLET, 0, 2, 10, M_DrawItemToggles, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index afa3f9f2a..375113bd4 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -150,8 +150,9 @@ static void M_DrawSlider(INT32 x, INT32 y, const consvar_t *cv, boolean ontop) static patch_t *addonsp[NUM_EXT+5]; -static UINT32 bgTextScroll = 0; -static UINT32 bgImageScroll = 0; +static fixed_t bgText1Scroll = 0; +static fixed_t bgText2Scroll = 0; +static fixed_t bgImageScroll = 0; static char bgImageName[9]; #define MENUBG_TEXTSCROLL 6 @@ -174,7 +175,7 @@ void M_UpdateMenuBGImage(boolean forceReset) if (forceReset == false && strcmp(bgImageName, oldName)) { - bgImageScroll = (BASEVIDWIDTH / 2) / MENUBG_IMAGESCROLL; + bgImageScroll = (BASEVIDWIDTH / 2)*FRACUNIT; } } @@ -183,34 +184,41 @@ void M_DrawMenuBackground(void) patch_t *text1 = W_CachePatchName("MENUBGT1", PU_CACHE); patch_t *text2 = W_CachePatchName("MENUBGT2", PU_CACHE); - INT32 text1loop = SHORT(text1->height); - INT32 text2loop = SHORT(text2->width); - - fixed_t text1scroll = -((bgTextScroll * MENUBG_TEXTSCROLL) % text1loop) * FRACUNIT; - fixed_t text2scroll = -((bgTextScroll * MENUBG_TEXTSCROLL) % text2loop) * FRACUNIT; + fixed_t text1loop = SHORT(text1->height)*FRACUNIT; + fixed_t text2loop = SHORT(text2->width)*FRACUNIT; V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUBG4", PU_CACHE), NULL); - V_DrawFixedPatch(-(bgImageScroll * MENUBG_IMAGESCROLL) * FRACUNIT, 0, FRACUNIT, 0, W_CachePatchName("MENUBG1", PU_CACHE), NULL); - V_DrawFixedPatch(-(bgImageScroll * MENUBG_IMAGESCROLL) * FRACUNIT, 0, FRACUNIT, 0, W_CachePatchName(bgImageName, PU_CACHE), NULL); + V_DrawFixedPatch(-bgImageScroll, 0, FRACUNIT, 0, W_CachePatchName("MENUBG1", PU_CACHE), NULL); + V_DrawFixedPatch(-bgImageScroll, 0, FRACUNIT, 0, W_CachePatchName(bgImageName, PU_CACHE), NULL); V_DrawFixedPatch(0, (BASEVIDHEIGHT + 16) * FRACUNIT, FRACUNIT, V_TRANSLUCENT, W_CachePatchName("MENUBG2", PU_CACHE), NULL); - V_DrawFixedPatch(text2scroll, (BASEVIDHEIGHT-8) * FRACUNIT, + V_DrawFixedPatch(-bgText2Scroll, (BASEVIDHEIGHT-8) * FRACUNIT, FRACUNIT, V_TRANSLUCENT, text2, NULL); - V_DrawFixedPatch(text2scroll + (text2loop * FRACUNIT), (BASEVIDHEIGHT-8) * FRACUNIT, + V_DrawFixedPatch(-bgText2Scroll + text2loop, (BASEVIDHEIGHT-8) * FRACUNIT, FRACUNIT, V_TRANSLUCENT, text2, NULL); - V_DrawFixedPatch(8 * FRACUNIT, text1scroll, + V_DrawFixedPatch(8 * FRACUNIT, -bgText1Scroll, FRACUNIT, V_TRANSLUCENT, text1, NULL); - V_DrawFixedPatch(8 * FRACUNIT, text1scroll + (text1loop * FRACUNIT), + V_DrawFixedPatch(8 * FRACUNIT, -bgText1Scroll + text1loop, FRACUNIT, V_TRANSLUCENT, text1, NULL); - bgTextScroll++; + bgText1Scroll += (MENUBG_TEXTSCROLL*rendertimefrac); + while (bgText1Scroll > text1loop) + bgText1Scroll -= text1loop; + + bgText2Scroll += (MENUBG_TEXTSCROLL*rendertimefrac); + while (bgText2Scroll > text2loop) + bgText2Scroll -= text2loop; if (bgImageScroll > 0) { - bgImageScroll--; + bgImageScroll -= (MENUBG_IMAGESCROLL*rendertimefrac); + if (bgImageScroll < 0) + { + bgImageScroll = 0; + } } } @@ -2608,17 +2616,17 @@ void M_DrawVideoModes(void) } // Gameplay Item Tggles: -static tic_t shitsfree = 0; +tic_t shitsfree = 0; void M_DrawItemToggles(void) { - const INT32 edges = 9; - const INT32 height = 3; + const INT32 edges = 8; + 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 + menutransition.tics*64, y = currentMenu->y+(spacing/4); + INT32 x = currentMenu->x + menutransition.tics*64, y = currentMenu->y; INT32 onx = 0, ony = 0; consvar_t *cv; INT32 i, translucent, drawnum; @@ -2653,7 +2661,7 @@ void M_DrawItemToggles(void) const INT32 thisitem = (i*height)+j; if (thisitem >= currentMenu->numitems) - continue; + break; if (thisitem == itemOn) { @@ -2667,6 +2675,14 @@ void M_DrawItemToggles(void) { V_DrawScaledPatch(x, y, 0, W_CachePatchName("K_ISBG", PU_CACHE)); V_DrawScaledPatch(x, y, 0, W_CachePatchName("K_ISTOGL", PU_CACHE)); + y += spacing; + continue; + } + + if (currentMenu->menuitems[thisitem].mvar1 == 255) + { + V_DrawScaledPatch(x, y, 0, W_CachePatchName("K_ISBGD", PU_CACHE)); + y += spacing; continue; } @@ -2713,7 +2729,7 @@ void M_DrawItemToggles(void) } x += spacing; - y = currentMenu->y+(spacing/4); + y = currentMenu->y; } { @@ -2722,6 +2738,19 @@ void M_DrawItemToggles(void) 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 if (currentMenu->menuitems[itemOn].mvar1 == 255) + { + V_DrawScaledPatch(onx-1, ony-2, 0, W_CachePatchName("K_ITBGD", PU_CACHE)); + if (shitsfree) + { + INT32 trans = V_TRANSLUCENT; + if (shitsfree-1 > TICRATE-5) + trans = ((10-TICRATE)+shitsfree-1)<menuitems[itemOn].mvar1-1]; @@ -2764,8 +2793,6 @@ void M_DrawItemToggles(void) if (shitsfree) shitsfree--; - - V_DrawCenteredString(BASEVIDWIDTH/2 + menutransition.tics*48, currentMenu->y, highlightflags, va("* %s *", currentMenu->menuitems[itemOn].text)); } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 361e9e766..b2521ba21 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3553,6 +3553,11 @@ void M_OptionsTick(void) optionsmenu.toptx = 420; optionsmenu.topty = 70+1; } + else if (currentMenu == &OPTIONS_GameplayItemsDef) + { + optionsmenu.toptx = -160; // off the side of the screen + optionsmenu.topty = 50; + } else { optionsmenu.toptx = 160; @@ -4118,7 +4123,7 @@ void M_MapProfileControl(event_t *ev) void M_HandleItemToggles(INT32 choice) { - const INT32 width = 9, height = 3; + const INT32 width = 8, height = 4; INT32 column = itemOn/height, row = itemOn%height; INT16 next; UINT8 i; @@ -4127,7 +4132,6 @@ void M_HandleItemToggles(INT32 choice) (void) choice; - if (menucmd[pid].dpad_lr > 0) { S_StartSound(NULL, sfx_menu1); @@ -4187,7 +4191,6 @@ void M_HandleItemToggles(INT32 choice) else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) { M_SetMenuDelay(pid); -#ifdef ITEMTOGGLEBOTTOMRIGHT if (currentMenu->menuitems[itemOn].mvar1 == 255) { //S_StartSound(NULL, sfx_s26d); @@ -4198,7 +4201,6 @@ void M_HandleItemToggles(INT32 choice) } } else -#endif if (currentMenu->menuitems[itemOn].mvar1 == 0) { INT32 v = cv_sneaker.value; @@ -4211,7 +4213,14 @@ void M_HandleItemToggles(INT32 choice) } else { - S_StartSound(NULL, sfx_s1ba); + if (currentMenu->menuitems[itemOn].mvar2) + { + S_StartSound(NULL, currentMenu->menuitems[itemOn].mvar2); + } + else + { + S_StartSound(NULL, sfx_s1ba); + } CV_AddValue(KartItemCVars[currentMenu->menuitems[itemOn].mvar1-1], 1); } } diff --git a/src/p_user.c b/src/p_user.c index eb1ef31fd..2f3c13c4c 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -181,7 +181,7 @@ fixed_t P_ReturnThrustY(mobj_t *mo, angle_t angle, fixed_t move) boolean P_AutoPause(void) { // Don't pause even on menu-up or focus-lost in netgames or record attack - if (netgame || modeattacking || gamestate == GS_TITLESCREEN) + if (netgame || modeattacking || gamestate == GS_TITLESCREEN || gamestate == GS_MENU || con_startup) return false; return ((menuactive && !demo.playback) || ( window_notinfocus && cv_pauseifunfocused.value )); From 6db7e673efcec5ff684a8f5580f93f9a27c681cd Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 29 Mar 2022 21:26:50 +0100 Subject: [PATCH 250/379] da two mines lol --- src/k_menudef.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/k_menudef.c b/src/k_menudef.c index 694a9ade9..8c76c29a1 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -940,17 +940,17 @@ menuitem_t OPTIONS_GameplayItems[] = {IT_KEYHANDLER | IT_NOTHING, NULL, "Bananas", NULL, {.routine = M_HandleItemToggles}, KITEM_BANANA, 0}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Bananas x3", NULL, {.routine = M_HandleItemToggles}, KRITEM_TRIPLEBANANA, 0}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Bananas x10", NULL, {.routine = M_HandleItemToggles}, KRITEM_TENFOLDBANANA, 0}, - {IT_KEYHANDLER | IT_NOTHING, NULL, "Mines", NULL, {.routine = M_HandleItemToggles}, KITEM_MINE, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Proximity Mines", NULL, {.routine = M_HandleItemToggles}, KITEM_MINE, 0}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Orbinauts", NULL, {.routine = M_HandleItemToggles}, KITEM_ORBINAUT, 0}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Orbinauts x3", NULL, {.routine = M_HandleItemToggles}, KRITEM_TRIPLEORBINAUT, 0}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Orbinauts x4", NULL, {.routine = M_HandleItemToggles}, KRITEM_QUADORBINAUT, 0}, - {IT_KEYHANDLER | IT_NOTHING, NULL, "Drop Targets", NULL, {.routine = M_HandleItemToggles}, KITEM_DROPTARGET, sfx_s258}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Land Mines", NULL, {.routine = M_HandleItemToggles}, KITEM_LANDMINE, 0}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Jawz", NULL, {.routine = M_HandleItemToggles}, KITEM_JAWZ, 0}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Jawz x2", NULL, {.routine = M_HandleItemToggles}, KRITEM_DUALJAWZ, 0}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Ballhogs", NULL, {.routine = M_HandleItemToggles}, KITEM_BALLHOG, 0}, - {IT_KEYHANDLER | IT_NOTHING, NULL, "Land Mines", NULL, {.routine = M_HandleItemToggles}, KITEM_LANDMINE, 0}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Drop Targets", NULL, {.routine = M_HandleItemToggles}, KITEM_DROPTARGET, sfx_s258}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Thunder Shields", NULL, {.routine = M_HandleItemToggles}, KITEM_THUNDERSHIELD, 0}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Bubble Shields", NULL, {.routine = M_HandleItemToggles}, KITEM_BUBBLESHIELD, 0}, From 55b0d5d07a8d0282f70de78194bfdab626a53e65 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 30 Mar 2022 23:42:45 +0100 Subject: [PATCH 251/379] Fix axes in M_MapProfileControl. * Now properly converts ev->data1 to a control, instead of leaving it as an unfiltered axis number. * Now checks it exceeds a deadzone (TODO: always p1's deadzone for now) Still a lot of bugs in input support, but we're getting there. --- src/k_menufunc.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index b2521ba21..bb0915fd8 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -4088,7 +4088,7 @@ void M_ProfileSetControl(INT32 ch) // Map the event to the profile. void M_MapProfileControl(event_t *ev) { - INT32 c = ev->data1; + INT32 c = 0; UINT8 n = optionsmenu.bindcontrol-1; // # of input to bind INT32 controln = currentMenu->menuitems[itemOn].mvar1; // gc_ UINT8 where = n; // By default, we'll save the bind where we're supposed to map. @@ -4097,7 +4097,75 @@ void M_MapProfileControl(event_t *ev) SetDeviceOnPress(); // Update cv_usejoystick // Only consider keydown and joystick events to make sure we ignore ev_mouse and other events - if (ev->type != ev_keydown && ev->type != ev_joystick) + // See also G_MapEventsToControls + switch (ev->type) + { + case ev_keydown: + if (ev->data1 < NUMINPUTS) + { + c = ev->data1; + } +#ifdef PARANOIA + else + { + CONS_Debug(DBG_GAMELOGIC, "Bad downkey input %d\n", ev->data1); + } +#endif + break; + case ev_joystick: + if (ev->data1 >= JOYAXISSET) + { +#ifdef PARANOIA + CONS_Debug(DBG_GAMELOGIC, "Bad joystick axis event %d\n", ev->data1); +#endif + return; + } + else + { + INT32 deadzone = deadzone = (JOYAXISRANGE * cv_deadzone[0].value) / FRACUNIT; // TODO how properly account for different deadzone cvars for different devices + boolean responsivelr = ((ev->data2 != INT32_MAX) && (abs(ev->data2) >= deadzone)); + boolean responsiveud = ((ev->data3 != INT32_MAX) && (abs(ev->data3) >= deadzone)); + + // Only consider unambiguous assignment. + if (responsivelr == responsiveud) + return; + + i = (ev->data1 * 4); + + if (responsivelr) + { + if (ev->data2 < 0) + { + // Left + c = KEY_AXIS1 + i; + } + else + { + // Right + c = KEY_AXIS1 + i + 1; + } + } + else //if (responsiveud) + { + if (ev->data3 < 0) + { + // Up + c = KEY_AXIS1 + i + 2; + } + else + { + // Down + c = KEY_AXIS1 + i + 3; + } + } + } + break; + default: + return; + } + + // safety result + if (!c) return; // Set menu delay regardless of what we're doing to avoid stupid stuff. From e3f9a925d8531e2f5fab64215adc44b0efb6ea47 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 1 Apr 2022 20:32:48 +0100 Subject: [PATCH 252/379] Fix a fresh profile not having kickstartaccel zeroed out. --- src/k_profiles.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/k_profiles.c b/src/k_profiles.c index 1449650b1..e48688d41 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -38,6 +38,7 @@ profile_t* PR_MakeProfile(const char *prname, const char *pname, const char *sna strcpy(new->follower, fname); new->followercolor = fcol; + new->kickstartaccel = false; // Copy from gamecontrol directly as we'll be setting controls up directly in the profile. memcpy(new->controls, controlarray, sizeof(new->controls)); From ff5992e3c48877b097b7740729121069159a57ee Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 1 Apr 2022 20:51:37 +0100 Subject: [PATCH 253/379] Rework the entire G_PlayerInputAnalog system. * Previous control checking flow: * Current controller/keyboard (userbound controls). * If on a menu: * Current controller/keyboard (default controls). * All controllers not in use by a player (default controls). * New control checking flow: * Current controller/keyboard (userbound controls). * If player 0 and just checked a controller, check keyboard (userbound controls). * If on a menu: * Check all controllers not in use by a player (userbound controls). * If keys are inaccessible/unbound and keybind is necessary to navigate menus, repeat eveyrhting with default controls. * Instead of duplicated code, control the flow in a finer fashion. * Now able to detect if gamepad inputs are possible to recieve (via checking deviceID), instead of assuming they are. * If a keybind is set but inaccessible by the above metric, make it flash on the Profile Controls screen. * Fix out-of-order key mappings for a given bind being invisible on the Profile Controls menu. --- src/g_game.c | 148 +++++++++++++++++------------------------------ src/g_input.c | 45 ++++++++++++++ src/g_input.h | 9 ++- src/k_menudraw.c | 79 +++++++++++++++++++++---- 4 files changed, 174 insertions(+), 107 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 45682aae9..5f7b0cd73 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -660,70 +660,14 @@ INT16 G_SoftwareClipAimingPitch(INT32 *aiming) return (INT16)((*aiming)>>16); } -static INT32 KeyValue(UINT8 p, INT32 key, UINT8 menuPlayers) -{ - INT32 deviceID; - INT32 i, j; - - if (key <= 0 || key >= NUMINPUTS) - { - return 0; - } - - deviceID = cv_usejoystick[p].value; - - if (menuPlayers > 0) - { - // Try every device that does NOT belong to another player. - for (i = MAXDEVICES-1; i >= 0; i--) - { - if (i == deviceID) - { - // We've tried this one multiple times :V - continue; - } - - if (menuPlayers > 1) - { - for (j = 1; j < menuPlayers; j++) - { - if (i == cv_usejoystick[j].value) - { - break; - } - } - - if (j < menuPlayers) - { - // This one's taken. - continue; - } - } - - if (gamekeydown[i][key] != 0) - { - return gamekeydown[i][key]; - } - } - } - else - { - if (deviceID < 0 || deviceID >= MAXDEVICES) - { - // Device is unset - return 0; - } - - return gamekeydown[deviceID][key]; - } - - return 0; -} - INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers) { + INT32 deviceID; INT32 i; INT32 deadzone = 0; + boolean trydefaults = true; + boolean tryingotherID = false; + INT32 *controltable = &(gamecontrol[p][gc][0]); if (p >= MAXSPLITSCREENPLAYERS) { @@ -735,17 +679,24 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers) deadzone = (JOYAXISRANGE * cv_deadzone[p].value) / FRACUNIT; + deviceID = cv_usejoystick[p].value; + +retrygetcontrol: for (i = 0; i < MAXINPUTMAPPING; i++) { - INT32 key = gamecontrol[p][gc][i]; + INT32 key = controltable[i]; INT32 value = 0; - if (key <= 0 || key >= NUMINPUTS) + // Invalid key number. + if (!G_KeyIsAvailable(key, deviceID)) { continue; } - value = KeyValue(p, key, false); + // It's possible to access this control right now, so let's disable the default control backup for later. + trydefaults = false; + + value = gamekeydown[deviceID][key]; if (value >= deadzone) { @@ -753,42 +704,51 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers) } } - if (menuPlayers != 0) + // If you're on controller, try your keyboard-based binds as an immediate backup. + if (p == 0 && deviceID > 0 && !tryingotherID) { - // We don't want menus to become unnavigable if people unbind - // all of their controls, so we do several things in this scenario. + deviceID = 0; + goto retrygetcontrol; + } - // First: check the same device, but with default binds. - for (i = 0; i < MAXINPUTMAPPING; i++) + if (menuPlayers == 0) + { + return 0; + } + + // We don't want menus to become unnavigable if people unbind + // all of their controls, so we do several things in this scenario. + // First: try other controllers. + + if (!tryingotherID) + { + deviceID = MAXDEVICES; + tryingotherID = true; + } +loweringid: + deviceID--; + if (deviceID > 0) + { + for (i = 0; i < menuPlayers; i++) { - INT32 key = gamecontroldefault[gc][i]; - INT32 value = 0; - - if (key <= 0 || key >= NUMINPUTS) - { + if (deviceID != cv_usejoystick[i].value) continue; - } - - value = KeyValue(p, key, false); - - if (value >= deadzone) - { - return value; - } - - if (p == 0 && menuPlayers == 1) - { - // Second: if we're Player 1 and there are no other players, - // then we can use keyboard defaults as a final resort. - - value = KeyValue(p, key, menuPlayers); - - if (value >= deadzone) - { - return value; - } - } + // Controller taken? Try again... + goto loweringid; } + goto retrygetcontrol; + } + + if (trydefaults && G_KeyBindIsNecessary(gc)) + { + // If we still haven't found anything and the keybind is necessary, + // try it all again but with default binds. + trydefaults = false; + controltable = &(gamecontroldefault[gc][0]); + tryingotherID = false; + deviceID = cv_usejoystick[p].value; + goto retrygetcontrol; + } return 0; diff --git a/src/g_input.c b/src/g_input.c index 35e5f7da9..2ab595912 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -417,6 +417,51 @@ static const char *gamecontrolname[num_gamecontrols] = #define NUMKEYNAMES (sizeof (keynames)/sizeof (keyname_t)) +// If keybind is necessary to navigate menus, it's on this list. +boolean G_KeyBindIsNecessary(INT32 gc) +{ + switch (gc) + { + case gc_a: + case gc_b: + case gc_up: + case gc_down: + case gc_left: + case gc_right: + case gc_start: + return true; + default: + return false; + } + return false; +} + +// Returns false if a key is deemed unreachable for this device. +boolean G_KeyIsAvailable(INT32 key, INT32 deviceID) +{ + // Invalid key number. + if (key <= 0 || key >= NUMINPUTS) + { + return false; + } + + // Valid controller-specific virtual key, but no controller attached for player. + if (key >= KEY_JOY1 && key < JOYINPUTEND && deviceID <= 0) + { + return false; + } + + // Valid mouse-specific virtual key, but no mouse attached for player. TODO HOW TO DETECT ACTIVE MOUSE CONNECTION + /* + if (key >= KEY_MOUSE1 && key < MOUSEINPUTEND && ????????) + { + return false; + } + */ + + return true; +} + // // Detach any keys associated to the given game control // - pass the pointer to the gamecontrol table for the player being edited diff --git a/src/g_input.h b/src/g_input.h index f633d4f0f..ae9b130bc 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -37,13 +37,15 @@ typedef enum KEY_JOY1 = NUMKEYS, KEY_HAT1 = KEY_JOY1 + JOYBUTTONS, KEY_AXIS1 = KEY_HAT1 + JOYHATS*4, + JOYINPUTEND = KEY_AXIS1 + JOYAXISSET*2*2, // 4 sets of 2 axes, each with positive & negative - KEY_MOUSE1 = KEY_AXIS1 + JOYAXISSET*2*2, // 4 sets of 2 axes, each with positive & negative + KEY_MOUSE1 = JOYINPUTEND, KEY_MOUSEMOVE = KEY_MOUSE1 + MOUSEBUTTONS, KEY_MOUSEWHEELUP = KEY_MOUSEMOVE + 4, KEY_MOUSEWHEELDOWN = KEY_MOUSEWHEELUP + 1, + MOUSEINPUTEND = KEY_MOUSEWHEELDOWN + 1, - NUMINPUTS = KEY_MOUSEWHEELDOWN + 1, + NUMINPUTS = MOUSEINPUTEND, } key_input_e; typedef enum @@ -134,6 +136,9 @@ void G_MapEventsToControls(event_t *ev); const char *G_KeynumToString(INT32 keynum); INT32 G_KeyStringtoNum(const char *keystr); +boolean G_KeyBindIsNecessary(INT32 gc); +boolean G_KeyIsAvailable(INT32 key, INT32 deviceID); + // detach any keys associated to the given game control void G_ClearControlKeys(INT32 (*setupcontrols)[MAXINPUTMAPPING], INT32 control); void G_ClearAllControlKeys(void); diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 375113bd4..e1b2731b0 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2337,7 +2337,7 @@ void M_DrawEditProfile(void) V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL); if (currentMenu->menuitems[itemOn].tooltip != NULL) { - V_DrawCenteredThinString(BASEVIDWIDTH*2/3, 12, V_ALLOWLOWERCASE|V_6WIDTHSPACE, currentMenu->menuitems[itemOn].tooltip); + V_DrawCenteredThinString(224, 12, V_ALLOWLOWERCASE|V_6WIDTHSPACE, currentMenu->menuitems[itemOn].tooltip); } // Draw the menu options... @@ -2480,18 +2480,76 @@ void M_DrawProfileControls(void) } else if (currentMenu->menuitems[i].status & IT_CONTROL) { - // Draw what the controls are mapped to + UINT32 vflags = V_6WIDTHSPACE; + INT32 gc = currentMenu->menuitems[i].mvar1; + UINT8 available = 0, set = 0; + + // Get userbound controls... for (k = 0; k < MAXINPUTMAPPING; k++) - keys[k] = optionsmenu.profile->controls[currentMenu->menuitems[i].mvar1][k]; + { + keys[k] = optionsmenu.profile->controls[gc][k]; + if (keys[k] == KEY_NULL) + continue; + set++; + if (!G_KeyIsAvailable(keys[k], cv_usejoystick[0].value)) + continue; + available++; + }; buf[0] = '\0'; - if (keys[0] == KEY_NULL) // If the first key's null, so should every other. - strcpy(buf, "\x85NOT BOUND"); + // Can't reach any of them? + if (available == 0) + { + if (((3*optionsmenu.ticker)/(2*TICRATE)) & 1) // 1.5 seconds + { + vflags |= V_ORANGEMAP; +#ifdef SHOWCONTROLDEFAULT + if (G_KeyBindIsNecessary(gc)) + { + // Get the defaults for essential keys. + // Went through all the trouble of making this look cool, + // then realised defaulting should only apply to menus. + // Too much opportunity for confusion if kept. + for (k = 0; k < MAXINPUTMAPPING; k++) + { + keys[k] = gamecontroldefault[gc][k]; + if (keys[k] == KEY_NULL) + continue; + available++; + } + set = available; + } + else if (set) +#else + if (!set) + { + if (!G_KeyBindIsNecessary(gc)) + vflags = V_REDMAP|V_6WIDTHSPACE; + } + else +#endif + { + strcpy(buf, "CURRENTLY UNAVAILABLE"); + } + } + else + { + vflags |= V_REDMAP; + } + } + + if (buf[0]) + ; + else if (!set) + strcpy(buf, "NOT BOUND"); else { - for (k=0; k < MAXINPUTMAPPING && keys[k] != KEY_NULL; k++) + for (k = 0; k < MAXINPUTMAPPING; k++) { + if (keys[k] == KEY_NULL) + continue; + if (k > 0) strcat(buf," / "); @@ -2499,18 +2557,17 @@ void M_DrawProfileControls(void) strcat(buf, "\n"); strcat(buf, G_KeynumToString (keys[k])); - } } // don't shift the text if we didn't draw a patch. - V_DrawThinString(x+ (drawnpatch ? 32 : 0), y+ (drawnpatch? 2 : 12), V_6WIDTHSPACE, buf); + V_DrawThinString(x + (drawnpatch ? 32 : 0), y + (drawnpatch ? 2 : 12), vflags, buf); // controller dest coords: - if (itemOn == i && currentMenu->menuitems[i].mvar1 && currentMenu->menuitems[i].mvar1 <= gc_start) + if (itemOn == i && gc > 0 && gc <= gc_start) { - optionsmenu.tcontx = controlleroffsets[currentMenu->menuitems[i].mvar1][0]; - optionsmenu.tconty = controlleroffsets[currentMenu->menuitems[i].mvar1][1]; + optionsmenu.tcontx = controlleroffsets[gc][0]; + optionsmenu.tconty = controlleroffsets[gc][1]; } } From 8b88284b329302faf4a29e3ace30cb2a2ea9d63a Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 1 Apr 2022 20:54:15 +0100 Subject: [PATCH 254/379] Fix in-game ticcmds being generated with your inputs while a menu is supposed to be eating them. (we can no longer rely on the ev_ system eating them before they get processed, so just add extra exceptions... --- src/g_game.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 5f7b0cd73..ed2998f25 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -870,6 +870,20 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) return; } + cmd->flags = 0; + + if (menuactive || chat_on || CON_Ready()) + { + cmd->flags |= TICCMD_TYPING; + + if (hu_keystrokes) + { + cmd->flags |= TICCMD_KEYSTROKE; + } + + goto aftercmdinput; + } + if (K_PlayerUsesBotMovement(player)) { // Bot ticcmd is generated by K_BuildBotTiccmd @@ -1017,17 +1031,8 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) */ cmd->forwardmove += (SINT8)forward; - cmd->flags = 0; - if (chat_on || CON_Ready()) - { - cmd->flags |= TICCMD_TYPING; - - if (hu_keystrokes) - { - cmd->flags |= TICCMD_KEYSTROKE; - } - } +aftercmdinput: /* Lua: Allow this hook to overwrite ticcmd. We check if we're actually in a level because for some reason this Hook would run in menus and on the titlescreen otherwise. From 6db54f77d87703e495f177b9ebb02177807ca0d6 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Mon, 11 Apr 2022 14:50:51 +0200 Subject: [PATCH 255/379] Force menus to use default controls for now. Update device in realtime in control setup menu --- src/g_game.c | 5 +++++ src/g_input.c | 2 +- src/k_menufunc.c | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index ed2998f25..0dc3fb8ef 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -669,6 +669,11 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers) boolean tryingotherID = false; INT32 *controltable = &(gamecontrol[p][gc][0]); + // Due to issues currently, force menus to use default controls regardless. + // @FIXME ? + if (gamestate == GS_MENU || menuactive == true) + controltable = &(gamecontroldefault[gc][0]); + if (p >= MAXSPLITSCREENPLAYERS) { #ifdef PARANOIA diff --git a/src/g_input.c b/src/g_input.c index 2ab595912..c9486eb35 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -35,7 +35,7 @@ consvar_t cv_controlperkey = CVAR_INIT ("controlperkey", "One", CV_SAVE, onecont // current state of the keys // FRACUNIT for fully pressed, 0 for not pressed INT32 gamekeydown[MAXDEVICES][NUMINPUTS]; -boolean deviceResponding[MAXDEVICES]; +boolean deviceResponding[MAXDEVICES]; // two key codes (or virtual key) per game control INT32 gamecontrol[MAXSPLITSCREENPLAYERS][num_gamecontrols][MAXINPUTMAPPING]; diff --git a/src/k_menufunc.c b/src/k_menufunc.c index bb0915fd8..c90abed0f 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -4040,6 +4040,8 @@ boolean M_ProfileControlsInputs(INT32 ch) if (optionsmenu.bindcontrol) return true; // Eat all inputs there. We'll use a stupid hack in M_Responder instead. + SetDeviceOnPress(); // Update device constantly so that we don't stay stuck with otpions saying a device is unavailable just because we're mapping multiple devices... + if (M_MenuButtonPressed(pid, MBT_C) || M_MenuButtonPressed(pid, MBT_Z)) { // check if we're on a valid menu option... From 7132a1be10c1b5f872ee1a6f5e6d5a14b2549d39 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Tue, 12 Apr 2022 00:58:57 +0200 Subject: [PATCH 256/379] Profiles: Followers support --- src/deh_soc.c | 6 ++ src/k_menu.h | 9 ++ src/k_menudraw.c | 270 ++++++++++++++++++++++++++++++++++++++++++++++- src/k_menufunc.c | 203 ++++++++++++++++++++++++++++++++--- src/r_skins.h | 3 + 5 files changed, 475 insertions(+), 16 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index 72c76d406..a1586cf2a 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -3505,6 +3505,7 @@ void readfollower(MYFILE *f) followers[numfollowers].bobamp = 4; followers[numfollowers].hitconfirmtime = TICRATE; followers[numfollowers].defaultcolor = SKINCOLOR_GREEN; + strcpy(followers[numfollowers].icon, "M_NORANK"); do { @@ -3538,6 +3539,11 @@ void readfollower(MYFILE *f) strcpy(followers[numfollowers].name, word2); nameset = true; } + else if (fastcmp(word, "ICON")) + { + strcpy(followers[numfollowers].icon, word2); + nameset = true; + } else if (fastcmp(word, "DEFAULTCOLOR")) { followers[numfollowers].defaultcolor = (UINT16)get_number(word2); diff --git a/src/k_menu.h b/src/k_menu.h index 4d35962e5..64162ae87 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -493,6 +493,8 @@ typedef enum CSSTEP_CHARS, CSSTEP_ALTS, CSSTEP_COLORS, + CSSTEP_FOLLOWER, + CSSTEP_FOLLOWERCOLORS, CSSTEP_READY } setup_mdepth_t; @@ -506,6 +508,13 @@ typedef struct setup_player_s UINT8 delay; UINT8 color; UINT8 mdepth; + + INT32 followern; + INT16 followercolor; + tic_t follower_tics; + tic_t follower_timer; + UINT8 follower_frame; + state_t *follower_state; } setup_player_t; extern setup_player_t setup_player[MAXSPLITSCREENPLAYERS]; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index e1b2731b0..bbff97bba 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -823,6 +823,8 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) if (p->mdepth == CSSTEP_ALTS) numoptions = setup_chargrid[p->gridx][p->gridy].numskins; + else if (p->mdepth == CSSTEP_FOLLOWERCOLORS) + numoptions = numskincolors-1 +2; else numoptions = numskincolors-1; @@ -857,6 +859,55 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) cx -= (SHORT(patch->width) << FRACBITS) >> 1; cy -= (SHORT(patch->height) << FRACBITS) >> 1; } + + else if (p->mdepth == CSSTEP_FOLLOWERCOLORS) + { + INT16 diff; + UINT16 col; + + n = (p->followercolor-1) + numoptions/2; + + if (subtract) + n -= ((i+1)/2); + else + n += ((i+1)/2); + + n %= numoptions; + n++; + + if (!n) + continue; + + col = (unsigned)(n); + switch (col) + { + case FOLLOWERCOLOR_MATCH: // "Match" + col = p->color; + break; + case FOLLOWERCOLOR_OPPOSITE: // "Opposite" + col = skincolors[p->color].invcolor; + break; + } + + colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); + + if (n > p->followercolor) + diff = n - p->followercolor; + else + diff = p->followercolor - n; + + if (diff == 0) + patch = W_CachePatchName("COLORSP2", PU_CACHE); + else if (abs(diff) < 25) + patch = W_CachePatchName("COLORSP1", PU_CACHE); + else + patch = W_CachePatchName("COLORSP0", PU_CACHE); + + radius = 28<width) << FRACBITS; + + cx -= (SHORT(patch->width) << FRACBITS) >> 1; + } else { INT16 diff; @@ -949,6 +1000,164 @@ static boolean M_DrawCharacterSprite(INT16 x, INT16 y, SINT8 skin, INT32 addflag return true; } +// Draws the follower list. +static void M_DrawFollowerList(setup_player_t *p, UINT8 num) +{ + INT16 x = 82; + INT16 y = 3; + INT32 cf = p->followern; + UINT8 i; + UINT8 *colormap = NULL; + + if (num & 1) + x = 172; + + if (num >= 2) + y = 176; + + // this places it at the bottom of the card! + if (optionsmenu.profile) + { + x = 35; + y = 143; + } + + // Start 1 follower below. + cf--; + if (cf < -1) + cf = numfollowers-1; + + for (i = 0; i < 3; i++) + { + patch_t *pp = NULL; + follower_t fl = followers[cf]; + + if (W_LumpExists(fl.icon) && cf >= 0) + { + UINT16 col = (unsigned)p->followercolor; + pp = W_CachePatchName(fl.icon, PU_CACHE); + + switch (col) + { + case FOLLOWERCOLOR_MATCH: // "Match" + col = p->color; + break; + case FOLLOWERCOLOR_OPPOSITE: // "Opposite" + col = skincolors[p->color].invcolor; + break; + } + + colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); + V_DrawMappedPatch(x, y, 0, pp, colormap); + if (i == 1) + V_DrawMappedPatch(x, y, 0, W_CachePatchName(va("K_CHILI%d", p->follower_timer%16 /2 +1), PU_CACHE), NULL); + + } + // No patch.... + else + { + V_DrawFill(x, y, 16, 16, 0); + + if (i == 1) + V_DrawMappedPatch(x, y, 0, W_CachePatchName(va("K_CHILI%d", p->follower_timer%16 /2 +1), PU_CACHE), NULL); + + if (cf >= 0) + V_DrawString(x, y, 0, va("%d\n", cf)); + else + V_DrawMappedPatch(x-4, y-3, 0, W_CachePatchName("K_NOBLNS", PU_CACHE), NULL); + + } + + cf++; + if (cf > numfollowers-1) + cf = -1; + + x += 23; + } +} + +// Returns false is the follower shouldn't be rendered. +// 'num' can be used to directly specify the follower number, but doing this will not animate it. +// if a setup_player_t is specified instead, its data will be used to animate the follower sprite. +static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, INT32 addflags, UINT16 color, setup_player_t *p) +{ + + spritedef_t *sprdef; + spriteframe_t *sprframe; + patch_t *patch; + INT32 followernum; + state_t *usestate; + UINT32 useframe; + follower_t fl; + UINT8 *colormap = NULL; + + if (p != NULL) + followernum = p->followern; + else + followernum = num; + + // Don't draw if we're outta bounds. + if (followernum < 0 || followernum > numfollowers-1) + return false; + + fl = followers[followernum]; + + if (p != NULL) + { + usestate = p->follower_state; + useframe = p->follower_frame; + } + else + { + usestate = &states[followers[followernum].followstate]; + useframe = usestate->frame & FF_FRAMEMASK; + } + + sprdef = &sprites[usestate->sprite]; + + // draw the follower + + if (useframe >= sprdef->numframes) + useframe = 0; // frame doesn't exist, we went beyond it... what? + + sprframe = &sprdef->spriteframes[useframe]; + patch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE); + + if (sprframe->flip & 2) // Only for first sprite + { + if (addflags & V_FLIP) + addflags &= ~V_FLIP; + else + addflags |= V_FLIP; // This sprite is left/right flipped! + } + + fixed_t sine = 0; + + if (p != NULL) + { + const fixed_t pi = (22<follower_timer)>>ANGLETOFINESHIFT) & FINEMASK); + color = p->followercolor; + + switch (color) + { + case FOLLOWERCOLOR_MATCH: // "Match" + color = p->color; + break; + case FOLLOWERCOLOR_OPPOSITE: // "Opposite" + color = skincolors[p->color].invcolor; + break; + default: + break; + } + } + + colormap = R_GetTranslationColormap(TC_DEFAULT, color, GTC_MENUCACHE); + V_DrawFixedPatch((x)*FRACUNIT, (y-12)*FRACUNIT+sine, fl.scale, addflags, patch, colormap); + + return true; +} + static void M_DrawCharSelectSprite(UINT8 num, INT16 x, INT16 y) { setup_player_t *p = &setup_player[num]; @@ -983,7 +1192,15 @@ static void M_DrawCharSelectPreview(UINT8 num) { M_DrawCharSelectSprite(num, x+32, y+75); - if (p->mdepth == CSSTEP_ALTS || p->mdepth == CSSTEP_COLORS) + if (p->mdepth >= CSSTEP_FOLLOWER) + { + M_DrawFollowerSprite(x+16, y+75, -1, !(num & 1) ? V_FLIP : 0, 0, p); + + if (p->mdepth == CSSTEP_FOLLOWER) + M_DrawFollowerList(p, num); + } + + if (p->mdepth == CSSTEP_ALTS || p->mdepth == CSSTEP_COLORS || p->mdepth == CSSTEP_FOLLOWERCOLORS) { M_DrawCharSelectCircle(p, x+32, y+64); } @@ -1175,21 +1392,63 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) // check what setup_player is doing in priority. if (sp->mdepth >= CSSTEP_CHARS) { - skinnum = setup_chargrid[sp->gridx][sp->gridy].skinlist[sp->clonenum]; + skinnum = setup_chargrid[sp->gridx][sp->gridy].skinlist[sp->clonenum]; if (M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap)) V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap); - if (sp->mdepth == CSSTEP_ALTS || sp->mdepth == CSSTEP_COLORS) + if (M_DrawFollowerSprite(x-44 +12, y+119, 0, V_FLIP, 0, sp)) + { + UINT16 col = (unsigned)p->followercolor; + patch_t *ico = W_CachePatchName(followers[sp->followern].icon, PU_CACHE); + UINT8 *fcolormap; + + switch (col) + { + case FOLLOWERCOLOR_MATCH: // "Match" + col = sp->color; + break; + case FOLLOWERCOLOR_OPPOSITE: // "Opposite" + col = skincolors[sp->color].invcolor; + break; + } + + fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); + V_DrawMappedPatch(x+14+18, y+66, 0, ico, fcolormap); + } + + if (sp->mdepth == CSSTEP_ALTS || sp->mdepth == CSSTEP_COLORS || sp->mdepth == CSSTEP_FOLLOWERCOLORS) { M_DrawCharSelectCircle(sp, x-22, y+104); } } else if (skinnum > -1) // otherwise, read from profile. { + + UINT16 col = (unsigned)p->followercolor; + UINT8 fln = R_FollowerAvailable(p->follower); + if (M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap)) V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap); + + switch (col) + { + case FOLLOWERCOLOR_MATCH: // "Match" + col = p->color; + break; + case FOLLOWERCOLOR_OPPOSITE: // "Opposite" + col = skincolors[p->color].invcolor; + break; + } + + + if (M_DrawFollowerSprite(x-44 +12, y+119, fln, V_FLIP, col, NULL)) + { + patch_t *ico = W_CachePatchName(followers[fln].icon, PU_CACHE); + UINT8 *fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); + V_DrawMappedPatch(x+14+18, y+66, 0, ico, fcolormap); + } } V_DrawCenteredGamemodeString(x, y+24, 0, 0, pname); @@ -1287,8 +1546,13 @@ void M_DrawCharacterSelect(void) if (optionsmenu.profile == NULL) M_DrawCharSelectPreview(i); else if (i == 0) + { M_DrawProfileCard(optionsmenu.optx, optionsmenu.opty, false, optionsmenu.profile); + if (setup_player[0].mdepth == CSSTEP_FOLLOWER) + M_DrawFollowerList(&setup_player[0], 0); + } + if (i >= setup_numplayers) continue; diff --git a/src/k_menufunc.c b/src/k_menufunc.c index c90abed0f..65b5260f1 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2027,6 +2027,11 @@ static void M_SetupProfileGridPos(setup_player_t *p) profile_t *pr = PR_GetProfile(p->profilen); INT32 i; + // While we're here, read follower values. + p->followern = R_FollowerAvailable(pr->follower); + p->followercolor = pr->followercolor; + + // Now position the grid for skin for (i = 0; i < numskins; i++) { if (!(strcmp(pr->skinname, skins[i].name))) @@ -2058,9 +2063,9 @@ void M_CharacterSelectInit(void) if (i != 0 || optionsmenu.profile) CV_SetValue(&cv_usejoystick[i], -1); - CONS_Printf("Device for %d set to %d\n", i, -1); + //CONS_Printf("Device for %d set to %d\n", i, -1); } - CONS_Printf("========\n"); + //CONS_Printf("========\n"); memset(setup_chargrid, -1, sizeof(setup_chargrid)); for (i = 0; i < 9; i++) @@ -2075,6 +2080,13 @@ void M_CharacterSelectInit(void) memset(setup_explosions, 0, sizeof(setup_explosions)); setup_animcounter = 0; + // Default to no follower / Match + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + setup_player[i].followern = -1; + setup_player[i].followercolor = -1; + } + for (i = 0; i < numskins; i++) { UINT8 x = skins[i].kartspeed-1; @@ -2229,16 +2241,16 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num) { // Available!! Let's use this one!! CV_SetValue(&cv_usejoystick[num], i); - CONS_Printf("Device for %d set to %d\n", num, i); - CONS_Printf("========\n"); + //CONS_Printf("Device for %d set to %d\n", num, i); + //CONS_Printf("========\n"); for (j = num+1; j < MAXSPLITSCREENPLAYERS; j++) { // Un-set devices for other players. CV_SetValue(&cv_usejoystick[j], -1); - CONS_Printf("Device for %d set to %d\n", j, -1); + //CONS_Printf("Device for %d set to %d\n", j, -1); } - CONS_Printf("========\n"); + //CONS_Printf("========\n"); //setup_numplayers++; p->mdepth = CSSTEP_PROFILE; @@ -2428,6 +2440,21 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) return false; } +// Gets the selected follower's state for a given setup player. +static void M_GetFollowerState(setup_player_t *p) +{ + + p->follower_state = &states[followers[p->followern].followstate]; + + if (p->follower_state->frame & FF_ANIMATE) + p->follower_tics = p->follower_state->var2; // support for FF_ANIMATE + else + p->follower_tics = p->follower_state->tics; + + p->follower_frame = p->follower_state->frame & FF_FRAMEMASK; +} + + static void M_HandleCharRotate(setup_player_t *p, UINT8 num) { UINT8 numclones = setup_chargrid[p->gridx][p->gridy].numskins; @@ -2488,10 +2515,12 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num) if (M_MenuButtonPressed(num, MBT_A) || M_MenuButtonPressed(num, MBT_X) /*|| M_MenuButtonPressed(num, MBT_START)*/) { - p->mdepth = CSSTEP_READY; - p->delay = TICRATE; - M_SetupReadyExplosions(p); - S_StartSound(NULL, sfx_s3k4e); + p->mdepth = CSSTEP_FOLLOWER; + M_GetFollowerState(p); + + //p->delay = TICRATE; + //M_SetupReadyExplosions(p); + //S_StartSound(NULL, sfx_s3k4e); M_SetMenuDelay(num); } else if (M_MenuButtonPressed(num, MBT_B) || M_MenuButtonPressed(num, MBT_Y)) @@ -2505,6 +2534,133 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num) } } +static void M_AnimateFollower(setup_player_t *p) +{ + if (--p->follower_tics <= 0) + { + + // FF_ANIMATE; cycle through FRAMES and get back afterwards. This will be prominent amongst followers hence why it's being supported here. + if (p->follower_state->frame & FF_ANIMATE) + { + p->follower_frame++; + p->follower_tics = p->follower_state->var2; + if (p->follower_frame > (p->follower_state->frame & FF_FRAMEMASK) + p->follower_state->var1) // that's how it works, right? + p->follower_frame = p->follower_state->frame & FF_FRAMEMASK; + } + else + { + if (p->follower_state->nextstate != S_NULL) + p->follower_state = &states[p->follower_state->nextstate]; + p->follower_tics = p->follower_state->tics; + /*if (p->follower_tics == -1) + p->follower_tics = 15; // er, what?*/ + // get spritedef: + p->follower_frame = p->follower_state->frame & FF_FRAMEMASK; + } + } + + p->follower_timer++; +} + +static void M_HandleChooseFollower(setup_player_t *p, UINT8 num) +{ + + M_AnimateFollower(p); + + if (menucmd[num].dpad_lr > 0 && numfollowers) + { + p->followern++; + if (p->followern > numfollowers-1) + p->followern = -1; + + M_SetMenuDelay(num); + S_StartSound(NULL, sfx_menu1); + M_GetFollowerState(p); + } + else if (menucmd[num].dpad_lr < 0 && numfollowers) + { + p->followern--; + if (p->followern < -1) + p->followern = numfollowers-1; + + M_SetMenuDelay(num); + S_StartSound(NULL, sfx_menu1); + M_GetFollowerState(p); + } + else if (M_MenuButtonPressed(num, MBT_A) || M_MenuButtonPressed(num, MBT_X)) + { + if (p->followern > -1) + p->mdepth = CSSTEP_FOLLOWERCOLORS; + else + { + p->mdepth = CSSTEP_READY; + p->delay = TICRATE; + M_SetupReadyExplosions(p); + S_StartSound(NULL, sfx_s3k4e); + M_SetMenuDelay(num); + } + + S_StartSound(NULL, sfx_s3k63); + M_SetMenuDelay(num); + } + else if (M_MenuButtonPressed(num, MBT_B) || M_MenuButtonPressed(num, MBT_Y)) + { + p->mdepth = CSSTEP_COLORS; + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(num); + } +} + +static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num) +{ + M_AnimateFollower(p); + + if (menucmd[num].dpad_lr > 0) + { + p->followercolor++; + + // Go back to -2 (Opposite) + if (p->followercolor >= numskincolors) + p->followercolor = -2; + + // Make sure we skip 0. + if (p->followercolor == 0) + p->followercolor++; + + p->rotate = CSROTATETICS; + M_SetMenuDelay(num); //CSROTATETICS + S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s + } + else if (menucmd[num].dpad_lr < 0) + { + p->followercolor--; + if (p->followercolor < -2) + p->followercolor = numskincolors-1; + + if (p->followercolor == 0) + p->followercolor--; + + p->rotate = -CSROTATETICS; + M_SetMenuDelay(num); //CSROTATETICS + S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s + } + + if (M_MenuButtonPressed(num, MBT_A) || M_MenuButtonPressed(num, MBT_X) /*|| M_MenuButtonPressed(num, MBT_START)*/) + { + p->mdepth = CSSTEP_READY; + p->delay = TICRATE; + M_SetupReadyExplosions(p); + S_StartSound(NULL, sfx_s3k4e); + M_SetMenuDelay(num); + } + else if (M_MenuButtonPressed(num, MBT_B) || M_MenuButtonPressed(num, MBT_Y)) + { + p->mdepth = CSSTEP_FOLLOWER; + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(num); + } +} + boolean M_CharacterSelectHandler(INT32 choice) { INT32 i; @@ -2535,6 +2691,12 @@ boolean M_CharacterSelectHandler(INT32 choice) case CSSTEP_COLORS: // Select color M_HandleColorRotate(p, i); break; + case CSSTEP_FOLLOWER: + M_HandleChooseFollower(p, i); + break; + case CSSTEP_FOLLOWERCOLORS: + M_HandleFollowerColorRotate(p, i); + break; case CSSTEP_READY: default: // Unready if (M_MenuButtonPressed(i, MBT_B) || M_MenuButtonPressed(i, MBT_Y)) @@ -2584,7 +2746,7 @@ static void M_MPConfirmCharacterSelection(void) INT16 col; char colstr[8]; - char commandnames[][2][MAXSTRINGLENGTH] = { {"skin ", "color "}, {"skin2 ", "color2 "}, {"skin3 ", "color3 "}, {"skin4 ", "color4 "}}; + char commandnames[][4][MAXSTRINGLENGTH] = { {"skin ", "color ", "follower ", "followercolor "}, {"skin2 ", "color2 ", "follower2 ", "followercolor2 "}, {"skin3 ", "color3 " "follower3 ", "followercolor3 "}, {"skin4 ", "color4 ", "follower4 ", "followercolor4 "}}; // ^ laziness 100 (we append a space directly so that we don't have to do it later too!!!!) for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) @@ -2594,7 +2756,6 @@ static void M_MPConfirmCharacterSelection(void) // skin strcpy(cmd, commandnames[i][0]); strcat(cmd, skins[setup_player[i].skin].name); - COM_ImmedExecute(cmd); // colour @@ -2603,10 +2764,18 @@ static void M_MPConfirmCharacterSelection(void) sprintf(colstr, "%d", col); strcpy(cmd, commandnames[i][1]); strcat(cmd, colstr); + COM_ImmedExecute(cmd); + // follower + strcpy(cmd, commandnames[i][2]); + strcat(cmd, va("%d", setup_player[i].followern)); + COM_ImmedExecute(cmd); + + // follower color + strcpy(cmd, commandnames[i][3]); + strcat(cmd, va("%d", setup_player[i].followercolor)); COM_ImmedExecute(cmd); } - M_ClearMenus(true); } @@ -2659,9 +2828,14 @@ void M_CharacterSelectTick(void) // in a profile; update the selected profile and then go back to the profile menu. if (optionsmenu.profile) { + // save player strcpy(optionsmenu.profile->skinname, skins[setup_player[0].skin].name); optionsmenu.profile->color = setup_player[0].color; + // save follower + strcpy(optionsmenu.profile->follower, followers[setup_player[0].followern].skinname); + optionsmenu.profile->followercolor = setup_player[0].followercolor; + // reset setup_player memset(setup_player, 0, sizeof(setup_player)); @@ -2674,6 +2848,9 @@ void M_CharacterSelectTick(void) { CV_StealthSet(&cv_skin[i], skins[setup_player[i].skin].name); CV_StealthSetValue(&cv_playercolor[i], setup_player[i].color); + + CV_StealthSetValue(&cv_follower[i], setup_player[i].followern); + CV_StealthSetValue(&cv_followercolor[i], setup_player[i].followercolor); } CV_StealthSetValue(&cv_splitplayers, setup_numplayers); diff --git a/src/r_skins.h b/src/r_skins.h index 2d29af188..95de83788 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -128,6 +128,9 @@ typedef struct follower_s INT32 losestate; // state when the player has lost INT32 hitconfirmstate; // state for hit confirm UINT32 hitconfirmtime; // time to keep the above playing for + + // visual + char icon[8+1]; // Lump names are only 8 characters. (+1 for \0) } follower_t; extern INT32 numfollowers; From 1d4ee407581446318efbfeb780aa72f2b165f001 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Tue, 12 Apr 2022 11:46:50 +0200 Subject: [PATCH 257/379] Remove unecessary button associations in menu. X is now Back --- src/g_game.c | 5 --- src/g_input.c | 6 +-- src/k_menudef.c | 10 ++--- src/k_menufunc.c | 113 +++++++++++++++++++++++++++-------------------- 4 files changed, 74 insertions(+), 60 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 0dc3fb8ef..ed2998f25 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -669,11 +669,6 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers) boolean tryingotherID = false; INT32 *controltable = &(gamecontrol[p][gc][0]); - // Due to issues currently, force menus to use default controls regardless. - // @FIXME ? - if (gamestate == GS_MENU || menuactive == true) - controltable = &(gamecontroldefault[gc][0]); - if (p >= MAXSPLITSCREENPLAYERS) { #ifdef PARANOIA diff --git a/src/g_input.c b/src/g_input.c index c9486eb35..cf33b2b78 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -561,9 +561,9 @@ void G_DefineDefaultControls(void) gamecontroldefault[gc_left ][1] = KEY_HAT1+2; // D-Pad Left gamecontroldefault[gc_right][1] = KEY_HAT1+3; // D-Pad Right gamecontroldefault[gc_a ][1] = KEY_JOY1+0; // A - gamecontroldefault[gc_b ][1] = KEY_JOY1+1; // B - gamecontroldefault[gc_c ][1] = KEY_JOY1+2; // ? - gamecontroldefault[gc_x ][1] = KEY_JOY1+3; + gamecontroldefault[gc_b ][1] = KEY_JOY1+2; // X + gamecontroldefault[gc_c ][1] = KEY_JOY1+3; // Y + gamecontroldefault[gc_x ][1] = KEY_JOY1+1; // B gamecontroldefault[gc_y ][1] = KEY_JOY1+6; gamecontroldefault[gc_z ][1] = KEY_JOY1+8; gamecontroldefault[gc_l ][1] = KEY_JOY1+4; // LB diff --git a/src/k_menudef.c b/src/k_menudef.c index 8c76c29a1..d1419a949 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -469,20 +469,20 @@ menuitem_t OPTIONS_ProfileControls[] = { {IT_CONTROL, "A", "Accelerate / Confirm", "PR_BTA", {.routine = M_ProfileSetControl}, gc_a, 0}, - {IT_CONTROL, "B", "Look backwards / Back", + {IT_CONTROL, "B", "Look backwards", "PR_BTB", {.routine = M_ProfileSetControl}, gc_b, 0}, - {IT_CONTROL, "C", "Spindash", + {IT_CONTROL, "C", "Spindash / Extra", "PR_BTC", {.routine = M_ProfileSetControl}, gc_c, 0}, - {IT_CONTROL, "X", "Brake", + {IT_CONTROL, "X", "Brake / Back", "PR_BTX", {.routine = M_ProfileSetControl}, gc_x, 0}, // @TODO What does this do??? - {IT_CONTROL, "Y", "We just don't know", + {IT_CONTROL, "Y", "N/A ?", "PR_BTY", {.routine = M_ProfileSetControl}, gc_y, 0}, - {IT_CONTROL, "Z", "We just don't know", + {IT_CONTROL, "Z", "N/A ?", "PR_BTZ", {.routine = M_ProfileSetControl}, gc_z, 0}, {IT_CONTROL, "L", "Use item", diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 65b5260f1..98a26f3c2 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1197,6 +1197,25 @@ boolean M_MenuButtonPressed(UINT8 pid, UINT32 bt) return (menucmd[pid].buttons & bt); } +// Returns true if we press the confirmation button +static boolean M_MenuConfirmPressed(UINT8 pid) +{ + return M_MenuButtonPressed(pid, MBT_A); +} + +// Returns true if we press the Cancel button +static boolean M_MenuBackPressed(UINT8 pid) +{ + return M_MenuButtonPressed(pid, MBT_X); +} + +// Retrurns true if we press the tertiary option button (C) +static boolean M_MenuExtraPressed(UINT8 pid) +{ + return M_MenuButtonPressed(pid, MBT_C); +} + + // Updates the x coordinate of the keybord so prevent it from going in weird places static void M_UpdateKeyboardX(void) { @@ -1319,7 +1338,7 @@ static void M_MenuTypingInput(INT32 key) M_SetMenuDelay(pid); S_StartSound(NULL, sfx_menu1); } - else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + else if (M_MenuConfirmPressed(pid)) { // Add the character. First though, check what we're pressing.... INT16 c = virtualKeyboard[menutyping.keyboardy][menutyping.keyboardx]; @@ -1404,7 +1423,7 @@ static void M_HandleMenuInput(void) routine = NULL; // If we're hovering over a IT_CV_STRING option, pressing A/X opens the typing submenu - if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + if (M_MenuConfirmPressed(pid)) { menutyping.keyboardtyping = menuKey != 0 ? true : false; // If we entered this menu by pressing a menu Key, default to keyboard typing, otherwise use controller. menutyping.active = true; @@ -1465,7 +1484,7 @@ static void M_HandleMenuInput(void) return; } - else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X) /*|| M_MenuButtonPressed(pid, MBT_START)*/) + else if (M_MenuConfirmPressed(pid) /*|| M_MenuButtonPressed(pid, MBT_START)*/) { noFurtherInput = true; currentMenu->lastOn = itemOn; @@ -1504,13 +1523,13 @@ static void M_HandleMenuInput(void) M_SetMenuDelay(pid); return; } - else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + else if (M_MenuBackPressed(pid)) { M_GoBack(0); M_SetMenuDelay(pid); return; } - else if (M_MenuButtonPressed(pid, MBT_C) || M_MenuButtonPressed(pid, MBT_Z)) + else if (M_MenuExtraPressed(pid)) { if (routine && ((currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_ARROWS || (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_CVAR)) @@ -1841,8 +1860,8 @@ void M_StopMessage(INT32 choice) void M_HandleMenuMessage(void) { const UINT8 pid = 0; - boolean btok = M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X); - boolean btnok = M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y); + boolean btok = M_MenuConfirmPressed(pid); + boolean btnok = M_MenuBackPressed(pid); menumessage.fadetimer++; @@ -1917,7 +1936,7 @@ void M_HandleImageDef(INT32 choice) M_SetMenuDelay(pid); itemOn--; } - else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_X) || M_MenuButtonPressed(pid, MBT_Y)) + else if (M_MenuConfirmPressed(pid) || M_MenuButtonPressed(pid, MBT_X) || M_MenuButtonPressed(pid, MBT_Y)) { exitmenu = true; M_SetMenuDelay(pid); @@ -2206,7 +2225,7 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num) return false; // Don't allow for the possibility of SOMEHOW another player joining in. // Detect B press first ... this means P1 can actually exit out of the menu. - if (M_MenuButtonPressed(num, MBT_B) || M_MenuButtonPressed(num, MBT_Y)) + if (M_MenuBackPressed(num)) { M_SetMenuDelay(num); @@ -2261,7 +2280,7 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num) { setup_player[j].delay = MENUDELAYTIME; M_SetMenuDelay(j); - menucmd[j].buttonsHeld |= (MBT_B|MBT_Y); + menucmd[j].buttonsHeld |= MBT_X; } memset(deviceResponding, false, sizeof(deviceResponding)); @@ -2296,7 +2315,7 @@ static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_menu1); M_SetMenuDelay(num); } - else if (M_MenuButtonPressed(num, MBT_B) || M_MenuButtonPressed(num, MBT_Y)) + else if (M_MenuBackPressed(num)) { if (num == setup_numplayers-1) { @@ -2309,7 +2328,7 @@ static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num) { setup_player[i].delay = MENUDELAYTIME; M_SetMenuDelay(i); - menucmd[i].buttonsHeld |= (MBT_B|MBT_Y); + menucmd[i].buttonsHeld |= MBT_X; } return true; @@ -2321,7 +2340,7 @@ static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num) M_SetMenuDelay(num); } - else if (M_MenuButtonPressed(num, MBT_A) || M_MenuButtonPressed(num, MBT_X)) + else if (M_MenuConfirmPressed(num)) { // Apply the profile. PR_ApplyProfile(p->profilen, num); @@ -2382,7 +2401,7 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) if (p->clonenum >= numclones) p->clonenum = 0; - if (M_MenuButtonPressed(num, MBT_A) || M_MenuButtonPressed(num, MBT_X) /*|| M_MenuButtonPressed(num, MBT_START)*/) + if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/) { if (setup_chargrid[p->gridx][p->gridy].numskins == 0) { @@ -2400,7 +2419,7 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) M_SetMenuDelay(num); } - else if (M_MenuButtonPressed(num, MBT_B) || M_MenuButtonPressed(num, MBT_Y)) + else if (M_MenuBackPressed(num)) { if (num == setup_numplayers-1) { @@ -2424,7 +2443,7 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) { setup_player[i].delay = MENUDELAYTIME; M_SetMenuDelay(i); - menucmd[i].buttonsHeld |= (MBT_B|MBT_Y); + menucmd[i].buttonsHeld |= MBT_X; } return true; @@ -2478,13 +2497,13 @@ static void M_HandleCharRotate(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_s3kc3s); } - if (M_MenuButtonPressed(num, MBT_A) || M_MenuButtonPressed(num, MBT_X) /*|| M_MenuButtonPressed(num, MBT_START)*/) + if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/) { p->mdepth = CSSTEP_COLORS; S_StartSound(NULL, sfx_s3k63); M_SetMenuDelay(num); } - else if (M_MenuButtonPressed(num, MBT_B) || M_MenuButtonPressed(num, MBT_Y)) + else if (M_MenuBackPressed(num)) { p->mdepth = CSSTEP_CHARS; S_StartSound(NULL, sfx_s3k5b); @@ -2513,7 +2532,7 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s } - if (M_MenuButtonPressed(num, MBT_A) || M_MenuButtonPressed(num, MBT_X) /*|| M_MenuButtonPressed(num, MBT_START)*/) + if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/) { p->mdepth = CSSTEP_FOLLOWER; M_GetFollowerState(p); @@ -2523,7 +2542,7 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num) //S_StartSound(NULL, sfx_s3k4e); M_SetMenuDelay(num); } - else if (M_MenuButtonPressed(num, MBT_B) || M_MenuButtonPressed(num, MBT_Y)) + else if (M_MenuBackPressed(num)) { if (setup_chargrid[p->gridx][p->gridy].numskins == 1) p->mdepth = CSSTEP_CHARS; // Skip clones menu @@ -2587,7 +2606,7 @@ static void M_HandleChooseFollower(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_menu1); M_GetFollowerState(p); } - else if (M_MenuButtonPressed(num, MBT_A) || M_MenuButtonPressed(num, MBT_X)) + else if (M_MenuConfirmPressed(num)) { if (p->followern > -1) p->mdepth = CSSTEP_FOLLOWERCOLORS; @@ -2603,7 +2622,7 @@ static void M_HandleChooseFollower(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_s3k63); M_SetMenuDelay(num); } - else if (M_MenuButtonPressed(num, MBT_B) || M_MenuButtonPressed(num, MBT_Y)) + else if (M_MenuBackPressed(num)) { p->mdepth = CSSTEP_COLORS; S_StartSound(NULL, sfx_s3k5b); @@ -2645,7 +2664,7 @@ static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s } - if (M_MenuButtonPressed(num, MBT_A) || M_MenuButtonPressed(num, MBT_X) /*|| M_MenuButtonPressed(num, MBT_START)*/) + if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/) { p->mdepth = CSSTEP_READY; p->delay = TICRATE; @@ -2653,7 +2672,7 @@ static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_s3k4e); M_SetMenuDelay(num); } - else if (M_MenuButtonPressed(num, MBT_B) || M_MenuButtonPressed(num, MBT_Y)) + else if (M_MenuBackPressed(num)) { p->mdepth = CSSTEP_FOLLOWER; S_StartSound(NULL, sfx_s3k5b); @@ -2699,7 +2718,7 @@ boolean M_CharacterSelectHandler(INT32 choice) break; case CSSTEP_READY: default: // Unready - if (M_MenuButtonPressed(i, MBT_B) || M_MenuButtonPressed(i, MBT_Y)) + if (M_MenuBackPressed(i)) { p->mdepth = CSSTEP_COLORS; S_StartSound(NULL, sfx_s3k5b); @@ -3185,7 +3204,7 @@ void M_CupSelectHandler(INT32 choice) M_SetMenuDelay(pid); } - if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X) /*|| M_MenuButtonPressed(pid, MBT_START)*/) + if (M_MenuConfirmPressed(pid) /*|| M_MenuButtonPressed(pid, MBT_START)*/) { M_SetMenuDelay(pid); @@ -3257,7 +3276,7 @@ void M_CupSelectHandler(INT32 choice) S_StartSound(NULL, sfx_s3k63); } } - else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + else if (M_MenuBackPressed(pid)) { M_SetMenuDelay(pid); @@ -3305,7 +3324,7 @@ void M_LevelSelectHandler(INT32 choice) M_LevelSelectScrollDest(); - if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X) /*|| M_MenuButtonPressed(pid, MBT_START)*/) + if (M_MenuConfirmPressed(pid) /*|| M_MenuButtonPressed(pid, MBT_START)*/) { INT16 map = start; INT16 add = levellist.cursor; @@ -3398,7 +3417,7 @@ void M_LevelSelectHandler(INT32 choice) M_ClearMenus(true); } } - else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + else if (M_MenuBackPressed(pid)) { M_SetMenuDelay(pid); @@ -3784,7 +3803,7 @@ boolean M_OptionsInputs(INT32 ch) return true; } - else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + else if (M_MenuConfirmPressed(pid)) { if (currentMenu->menuitems[itemOn].status & IT_TRANSTEXT) @@ -3923,7 +3942,7 @@ void M_HandleProfileSelect(INT32 ch) M_SetMenuDelay(pid); } - else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + else if (M_MenuConfirmPressed(pid)) { if (optionsmenu.profilen == 0) // Guest profile, you can't edit that one! @@ -3964,7 +3983,7 @@ void M_HandleProfileSelect(INT32 ch) M_SetupNextMenu(&OPTIONS_EditProfileDef, false); } - else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + else if (M_MenuBackPressed(pid)) { optionsmenu.resetprofilemenu = true; M_GoBack(0); @@ -3984,7 +4003,7 @@ boolean M_ProfileEditInputs(INT32 ch) UINT8 i; (void) ch; - if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + if (M_MenuBackPressed(pid)) { // check if some profiles have the same name for (i = 0; i < PR_GetNumProfiles(); i++) @@ -4060,12 +4079,12 @@ void M_HandleVideoModes(INT32 ch) if (optionsmenu.vidm_testingmode > 0) { // change back to the previous mode quickly - if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + if (M_MenuBackPressed(pid)) { setmodeneeded = optionsmenu.vidm_previousmode + 1; optionsmenu.vidm_testingmode = 0; } - else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + else if (M_MenuConfirmPressed(pid)) { S_StartSound(NULL, sfx_menu1); optionsmenu.vidm_testingmode = 0; // stop testing @@ -4116,7 +4135,7 @@ void M_HandleVideoModes(INT32 ch) M_SetMenuDelay(pid); } - else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + else if (M_MenuConfirmPressed(pid)) { M_SetMenuDelay(pid); S_StartSound(NULL, sfx_menu1); @@ -4131,7 +4150,7 @@ void M_HandleVideoModes(INT32 ch) } } - else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + else if (M_MenuBackPressed(pid)) { M_SetMenuDelay(pid); if (currentMenu->prevMenu) @@ -4219,7 +4238,7 @@ boolean M_ProfileControlsInputs(INT32 ch) SetDeviceOnPress(); // Update device constantly so that we don't stay stuck with otpions saying a device is unavailable just because we're mapping multiple devices... - if (M_MenuButtonPressed(pid, MBT_C) || M_MenuButtonPressed(pid, MBT_Z)) + if (M_MenuExtraPressed(pid)) { // check if we're on a valid menu option... if (currentMenu->menuitems[itemOn].mvar1) @@ -4235,7 +4254,7 @@ boolean M_ProfileControlsInputs(INT32 ch) M_SetMenuDelay(pid); return true; } - else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + else if (M_MenuBackPressed(pid)) optionsmenu.profile->kickstartaccel = cv_dummyprofilekickstart.value; // Make sure to save kickstart accel. return false; @@ -4435,7 +4454,7 @@ void M_HandleItemToggles(INT32 choice) M_SetMenuDelay(pid); } - else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + else if (M_MenuConfirmPressed(pid)) { M_SetMenuDelay(pid); if (currentMenu->menuitems[itemOn].mvar1 == 255) @@ -4472,7 +4491,7 @@ void M_HandleItemToggles(INT32 choice) } } - else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + else if (M_MenuBackPressed(pid)) { M_SetMenuDelay(pid); exitmenu = true; @@ -4575,7 +4594,7 @@ boolean M_ExtrasInputs(INT32 ch) return true; } - else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + else if (M_MenuConfirmPressed(pid)) { if (currentMenu->menuitems[itemOn].status & IT_TRANSTEXT) @@ -4711,7 +4730,7 @@ boolean M_PauseInputs(INT32 ch) return true; } - else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + else if (M_MenuBackPressed(pid)) { M_QuitPauseMenu(-1); return true; @@ -4994,13 +5013,13 @@ void M_HandleReplayHutList(INT32 choice) extrasmenu.replayScrollTitle = 0; extrasmenu.replayScrollDelay = TICRATE; extrasmenu.replayScrollDir = 1; } - else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + else if (M_MenuBackPressed(pid)) { M_SetMenuDelay(pid); M_QuitReplayHut(); } - else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + else if (M_MenuConfirmPressed(pid)) { M_SetMenuDelay(pid); switch (dirmenu[dir_on[menudepthleft]][DIR_TYPE]) @@ -5370,7 +5389,7 @@ void M_HandleAddons(INT32 choice) M_SetMenuDelay(pid); } - else if (M_MenuButtonPressed(pid, MBT_A) || M_MenuButtonPressed(pid, MBT_X)) + else if (M_MenuConfirmPressed(pid)) { boolean refresh = true; M_SetMenuDelay(pid); @@ -5451,7 +5470,7 @@ void M_HandleAddons(INT32 choice) refreshdirmenu |= REFRESHDIR_NORMAL; } } - else if (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_Y)) + else if (M_MenuBackPressed(pid)) { exitmenu = true; M_SetMenuDelay(pid); From 4f919f883d66502fc92c926424fe4c45bbaff1d4 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Tue, 12 Apr 2022 16:38:40 +0200 Subject: [PATCH 258/379] Bandaid fix for trigger mapping, not super consistent but it helps most of the time... --- src/k_menu.h | 5 +++++ src/k_menudraw.c | 38 +++++++++++++++++++++++++++++++++++++- src/k_menufunc.c | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/k_menu.h b/src/k_menu.h index 64162ae87..be0f24b47 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -454,6 +454,7 @@ void M_SortServerList(void); void M_MapMenuControls(event_t *ev); boolean M_Responder(event_t *ev); boolean M_MenuButtonPressed(UINT8 pid, UINT32 bt); +boolean M_MenuButtonHeld(UINT8 pid, UINT32 bt); void M_StartControlPanel(void); void M_ClearMenus(boolean callexitmenufunc); void M_SelectableClearMenus(INT32 choice); @@ -673,6 +674,10 @@ extern struct optionsmenu_s { UINT8 bindcontrol; // 0: not binding, 1: binding control #1, 2: binding control #2 INT16 bindtimer; // Timer until binding is cancelled (5s) + // Used for horrible axis shenanigans + INT32 lastkey; + tic_t keyheldfor; + // controller coords... // Works the same as (t)opt INT16 contx; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index bbff97bba..a1098bc26 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2672,6 +2672,24 @@ INT16 controlleroffsets[][2] = { {149, 187}, // gc_start }; +// Controller patches for button presses. +// {patch if not pressed, patch if pressed} +// if NULL, draws nothing. +// reminder that lumpnames can only be 8 chars at most. (+1 for \0) + +char controllerpresspatch[9][2][9] = { + {"", "BTP_A"}, // MBT_A + {"", "BTP_B"}, // MBT_B + {"", "BTP_C"}, // MBT_C + {"", "BTP_X"}, // MBT_X + {"", "BTP_Y"}, // MBT_Y + {"", "BTP_Z"}, // MBT_Z + {"BTNP_L", "BTP_L"},// MBT_L + {"BTNP_R", "BTP_R"},// MBT_R + {"", "BTP_ST"} // MBT_START +}; + + // the control stuff. // Dear god. void M_DrawProfileControls(void) @@ -2680,12 +2698,30 @@ void M_DrawProfileControls(void) INT32 y = 16 - (optionsmenu.controlscroll*spacing); INT32 x = 8; INT32 i, j, k; + const UINT8 pid = 0; M_DrawOptionsCogs(); - // @TODO: have it move around and shit. V_DrawScaledPatch(BASEVIDWIDTH*2/3 - optionsmenu.contx, BASEVIDHEIGHT/2 -optionsmenu.conty, 0, W_CachePatchName("PR_CONT", PU_CACHE)); + // Draw button presses... + // @TODO: Dpad when we get the sprites for it. + + for (i = 0; i < 9; i++) + { + INT32 bt = 1<kickstartaccel = cv_dummyprofilekickstart.value; // Make sure to save kickstart accel. + } return false; } @@ -4284,6 +4294,8 @@ void M_ProfileSetControl(INT32 ch) } // Map the event to the profile. + +#define KEYHOLDFOR 1 void M_MapProfileControl(event_t *ev) { INT32 c = 0; @@ -4357,6 +4369,32 @@ void M_MapProfileControl(event_t *ev) } } } + + /* I hate this. + I shouldn't have to do this. + But we HAVE to because of some controllers, INCLUDING MINE. + + Triggers on X360 controllers go from -1024 when not held + To 1023 when pressed. + The result is that pressing the trigger makes the game THINK the input is for a negative value. + Which then means that the input is considered pressed when the trigger is released. + + So what we're going to do is make sure that the detected 'c' key is the same for multiple rolls of ev_joystick. + NOTE: We need the player to press the key all the way down otherwise this might just not work... + + @TODO: This isn't entirely consistent because we only check for events..... maybe check continuously for gamekeydown[]? + but this seems messy... + */ + + //CONS_Printf("mapping joystick ... attempt (%d)\n", optionsmenu.keyheldfor); + + if (c != optionsmenu.lastkey) + { + optionsmenu.lastkey = c; + optionsmenu.keyheldfor = 0; + return; + } + break; default: return; @@ -4386,6 +4424,7 @@ void M_MapProfileControl(event_t *ev) optionsmenu.profile->controls[controln][where] = c; optionsmenu.bindcontrol = 0; // not binding anymore } +#undef KEYHOLDFOR void M_HandleItemToggles(INT32 choice) { From 66c62b38daf561fac3bcd7271dc0ef72c5390651 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Wed, 13 Apr 2022 00:53:27 +0200 Subject: [PATCH 259/379] Various fixes in charsel / charswitch menus --- src/d_netcmd.c | 13 +++++++++++++ src/d_netcmd.h | 1 + src/k_menudraw.c | 5 +++-- src/k_menufunc.c | 47 ++++++++++++++++++++++++++++++++++++++--------- src/k_profiles.c | 8 +++++++- 5 files changed, 62 insertions(+), 12 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index ad1eed24b..eb2c7388d 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -300,6 +300,18 @@ consvar_t cv_followercolor[MAXSPLITSCREENPLAYERS] = { CVAR_INIT ("followercolor4", "1", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor4_OnChange) }; +// last selected profile, unaccessible cvar only set internally but is saved. +// It's used to know what profile to autoload you to when you get into the character setup. + +static CV_PossibleValue_t lastprofile_cons_t[] = {{0, "MIN"}, {MAXPROFILES, "MAX"}, {0, NULL}}; + +consvar_t cv_lastprofile[MAXSPLITSCREENPLAYERS] = { + CVAR_INIT ("lastprofile", "1", CV_SAVE|CV_HIDDEN, lastprofile_cons_t, NULL), + CVAR_INIT ("lastprofile2", "1", CV_SAVE|CV_HIDDEN, lastprofile_cons_t, NULL), + CVAR_INIT ("lastprofile3", "1", CV_SAVE|CV_HIDDEN, lastprofile_cons_t, NULL), + CVAR_INIT ("lastprofile4", "1", CV_SAVE|CV_HIDDEN, lastprofile_cons_t, NULL), +}; + consvar_t cv_skipmapcheck = CVAR_INIT ("skipmapcheck", "Off", CV_SAVE, CV_OnOff, NULL); INT32 cv_debug; @@ -861,6 +873,7 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_skin[i]); CV_RegisterVar(&cv_follower[i]); CV_RegisterVar(&cv_followercolor[i]); + CV_RegisterVar(&cv_lastprofile[i]); } // preferred number of players diff --git a/src/d_netcmd.h b/src/d_netcmd.h index be4727b59..7a385ba92 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -23,6 +23,7 @@ extern consvar_t cv_playercolor[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_skin[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_follower[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_followercolor[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_lastprofile[MAXSPLITSCREENPLAYERS]; // preferred number of players extern consvar_t cv_splitplayers; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index a1098bc26..9fd382022 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1171,7 +1171,8 @@ static void M_DrawCharSelectSprite(UINT8 num, INT16 x, INT16 y) if (!(num & 1)) flags ^= V_FLIP; - M_DrawCharacterSprite(x, y, skin, flags, colormap); + if (skin >= 0) + M_DrawCharacterSprite(x, y, skin, flags, colormap); } static void M_DrawCharSelectPreview(UINT8 num) @@ -1206,7 +1207,7 @@ static void M_DrawCharSelectPreview(UINT8 num) } } - if ((setup_animcounter/10) & 1) + if ((setup_animcounter/10) & 1 && gamestate == GS_MENU) // Not drawn outside of GS_MENU. { if (p->mdepth == CSSTEP_NONE && num == setup_numplayers) { diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 52f14a311..ad77b37a0 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2104,11 +2104,15 @@ void M_CharacterSelectInit(void) memset(setup_explosions, 0, sizeof(setup_explosions)); setup_animcounter = 0; - // Default to no follower / Match for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { + // Default to no follower / match colour. setup_player[i].followern = -1; setup_player[i].followercolor = -1; + + // Set default selected profile to the last used profile for each player: + // (Make sure we don't overshoot it somehow if we deleted profiles or whatnot) + setup_player[i].profilen = min(cv_lastprofile[i].value, PR_GetNumProfiles()); } for (i = 0; i < numskins; i++) @@ -2133,10 +2137,18 @@ void M_CharacterSelectInit(void) setup_player[j].color = skins[i].prefcolor; // If we're on prpfile select, skip straight to CSSTEP_CHARS - if (optionsmenu.profile && j == 0) + if ((optionsmenu.profile || gamestate != GS_MENU) && j == 0) { - setup_player[j].profilen = optionsmenu.profilen; - PR_ApplyProfile(setup_player[j].profilen, 0); + if (optionsmenu.profile) // In menu, setting up profile character/follower + { + setup_player[j].profilen = optionsmenu.profilen; + PR_ApplyProfile(setup_player[j].profilen, 0); + } + else // gamestate != GS_MENU, in that case, assume this is whatever profile we chose to play with. + setup_player[j].profilen = cv_lastprofile[j].value; + // Don't reapply the profile here, it was already applied. + + M_SetupProfileGridPos(&setup_player[j]); setup_player[j].mdepth = CSSTEP_CHARS; } @@ -2428,9 +2440,9 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) { if (num == setup_numplayers-1) { - // for profiles, exit out of the menu instantly, + // for profiles / gameplay, exit out of the menu instantly, // we don't want to go to the input detection menu. - if (optionsmenu.profile) + if (optionsmenu.profile || gamestate != GS_MENU) { memset(setup_player, 0, sizeof(setup_player)); // Reset setup_player otherwise it does some VERY funky things. M_SetMenuDelay(0); @@ -2701,7 +2713,9 @@ boolean M_CharacterSelectHandler(INT32 choice) switch (p->mdepth) { case CSSTEP_NONE: // Enter Game - playersChanged = M_HandlePressStart(p, i); + if (gamestate == GS_MENU) // do NOT handle that outside of GS_MENU. + playersChanged = M_HandlePressStart(p, i); + break; case CSSTEP_PROFILE: playersChanged = M_HandleCSelectProfile(p, i); @@ -2770,10 +2784,10 @@ static void M_MPConfirmCharacterSelection(void) INT16 col; char colstr[8]; - char commandnames[][4][MAXSTRINGLENGTH] = { {"skin ", "color ", "follower ", "followercolor "}, {"skin2 ", "color2 ", "follower2 ", "followercolor2 "}, {"skin3 ", "color3 " "follower3 ", "followercolor3 "}, {"skin4 ", "color4 ", "follower4 ", "followercolor4 "}}; + char commandnames[][4][MAXSTRINGLENGTH] = { {"skin ", "color ", "follower ", "followercolor "}, {"skin2 ", "color2 ", "follower2 ", "followercolor2 "}, {"skin3 ", "color3 ", "follower3 ", "followercolor3 "}, {"skin4 ", "color4 ", "follower4 ", "followercolor4 "}}; // ^ laziness 100 (we append a space directly so that we don't have to do it later too!!!!) - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + for (i = 0; i < splitscreen +1; i++) { char cmd[MAXSTRINGLENGTH]; @@ -4264,7 +4278,22 @@ boolean M_ProfileControlsInputs(INT32 ch) } else if (M_MenuBackPressed(pid)) { + UINT8 i; + UINT8 pnum; + optionsmenu.profile->kickstartaccel = cv_dummyprofilekickstart.value; // Make sure to save kickstart accel. + pnum = PR_GetProfileNum(optionsmenu.profile); + + // Check if this profile is one we have last used on any player: + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + if (cv_lastprofile[i].value == pnum) + { + PR_ApplyProfile(pnum, i); + break; + } + } + } return false; diff --git a/src/k_profiles.c b/src/k_profiles.c index e48688d41..3baa18159 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -150,13 +150,19 @@ void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum) CV_StealthSet(&cv_skin[playernum], p->skinname); CV_StealthSetValue(&cv_playercolor[playernum], p->color); CV_StealthSet(&cv_playername[playernum], p->playername); - // @TODO followers + + // Followers + CV_StealthSet(&cv_follower[playernum], p->follower); + CV_StealthSetValue(&cv_followercolor[playernum], p->followercolor); // toggles CV_StealthSetValue(&cv_kickstartaccel[playernum], p->kickstartaccel); // set controls... memcpy(&gamecontrol[playernum], p->controls, sizeof(gamecontroldefault)); + + // set memory cvar + CV_StealthSetValue(&cv_lastprofile[playernum], profilenum); } UINT8 PR_GetProfileNum(profile_t *p) From 62b6b7ea8fc47cdcbe37d327083a15a7884677d8 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Wed, 13 Apr 2022 03:03:58 +0200 Subject: [PATCH 260/379] Update controls in real time in control setup, allow testing controller mappings --- src/k_menu.h | 3 +++ src/k_menudef.c | 6 +++++ src/k_menudraw.c | 27 +++++++++++++++++++++ src/k_menufunc.c | 62 +++++++++++++++++++++++++++++++++++++----------- 4 files changed, 84 insertions(+), 14 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index be0f24b47..004fc1bef 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -674,6 +674,8 @@ extern struct optionsmenu_s { UINT8 bindcontrol; // 0: not binding, 1: binding control #1, 2: binding control #2 INT16 bindtimer; // Timer until binding is cancelled (5s) + INT16 trycontroller; // Starts at 3*TICRATE, holding B lowers this, when at 0, cancel controller try mode. + // Used for horrible axis shenanigans INT32 lastkey; tic_t keyheldfor; @@ -731,6 +733,7 @@ boolean M_ProfileControlsInputs(INT32 ch); void M_ProfileSetControl(INT32 ch); void M_MapProfileControl(event_t *ev); +void M_ProfileTryController(INT32 choice); // video modes menu (resolution) void M_VideoModeMenu(INT32 choice); diff --git a/src/k_menudef.c b/src/k_menudef.c index d1419a949..068863b43 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -538,6 +538,12 @@ menuitem_t OPTIONS_ProfileControls[] = { {IT_CONTROL | IT_CVAR, "KICKSTART ACCEL", "Hold A to auto-accel. Tap it to cancel.", NULL, {.cvar = &cv_dummyprofilekickstart}, 0, 0}, + + {IT_HEADER, "EXTRA", "", + NULL, {NULL}, 0, 0}, + + {IT_STRING | IT_CALL, "TRY MAPPINGS", "Only display the controller for testing.", + NULL, {.routine = M_ProfileTryController}, 0, 0}, }; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 9fd382022..a151345cb 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2723,6 +2723,29 @@ void M_DrawProfileControls(void) } } + if (optionsmenu.trycontroller) + { + optionsmenu.tcontx = BASEVIDWIDTH*2/3 - 10; + optionsmenu.tconty = BASEVIDHEIGHT/2 +70; + + V_DrawCenteredString(160, 180, highlightflags, va("HOLD [X] FOR %d SECONDS TO BACK OUT", optionsmenu.trycontroller/TICRATE)); + return; // Don't draw the rest if we're trying the controller. + } + + // If we're past here, draw some text warnings. + if (gamestate == GS_MENU || PR_GetProfileNum(optionsmenu.profile) == cv_lastprofile[pid].value) + { + if (gamestate != GS_MENU) // If we're in a menu we'll always use the current profile to map controls from regardless. + V_DrawCenteredThinString(229, 180, highlightflags|V_ALLOWLOWERCASE|V_6WIDTHSPACE, "This is your last used profile,"); + + V_DrawCenteredThinString(229, 190, highlightflags|V_ALLOWLOWERCASE|V_6WIDTHSPACE, "Control changes will happen in real time"); + } + else + { + V_DrawCenteredThinString(229, 180, highlightflags|V_ALLOWLOWERCASE|V_6WIDTHSPACE, "This isn't your last used profile,"); + V_DrawCenteredThinString(229, 180, highlightflags|V_ALLOWLOWERCASE|V_6WIDTHSPACE, "Changes will apply on next profile selection."); + } + // Tooltip // The text is slightly shifted hence why we don't just use M_DrawMenuTooltips() V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL); @@ -2754,6 +2777,10 @@ void M_DrawProfileControls(void) y += spacing; break; + case IT_STRING: + V_DrawString(x, y+1, (i == itemOn ? highlightflags : 0), currentMenu->menuitems[i].text); + break; + case IT_STRING2: { boolean drawnpatch = false; diff --git a/src/k_menufunc.c b/src/k_menufunc.c index ad77b37a0..39ff7c2b4 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -4249,12 +4249,36 @@ void M_HandleProfileControls(void) } } +void M_ProfileTryController(INT32 choice) +{ + (void) choice; + + // I managed to softlock myself during testing lol. + if (!optionsmenu.profile->controls[gc_x][0]) + { + M_StartMessage(M_GetText("You need to bind a key to [X]\nto use this feature.\n"), NULL, MM_NOTHING); + return; + } + + optionsmenu.trycontroller = TICRATE*3; +} + boolean M_ProfileControlsInputs(INT32 ch) { const UINT8 pid = 0; (void)ch; // By default, accept all inputs. + if (optionsmenu.trycontroller) + { + if (M_MenuButtonHeld(pid, MBT_X)) + optionsmenu.trycontroller--; + else + optionsmenu.trycontroller = TICRATE*3; + + return true; + } + if (optionsmenu.bindcontrol) return true; // Eat all inputs there. We'll use a stupid hack in M_Responder instead. @@ -4278,22 +4302,10 @@ boolean M_ProfileControlsInputs(INT32 ch) } else if (M_MenuBackPressed(pid)) { - UINT8 i; - UINT8 pnum; - optionsmenu.profile->kickstartaccel = cv_dummyprofilekickstart.value; // Make sure to save kickstart accel. - pnum = PR_GetProfileNum(optionsmenu.profile); - - // Check if this profile is one we have last used on any player: - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) - { - if (cv_lastprofile[i].value == pnum) - { - PR_ApplyProfile(pnum, i); - break; - } - } + // Reapply player 1's real profile. + PR_ApplyProfile(cv_lastprofile[0].value, 0); } return false; @@ -4452,6 +4464,28 @@ void M_MapProfileControl(event_t *ev) optionsmenu.profile->controls[controln][where] = c; optionsmenu.bindcontrol = 0; // not binding anymore + + // If possible, reapply the profile... + if (gamestate == GS_MENU) // In menu? Apply this to P1, no questions asked. + { + // Apply the profile's properties to player 1 but keep the last profile cv to p1's ACTUAL profile to revert once we exit. + UINT8 lastp = cv_lastprofile[0].value; + PR_ApplyProfile(PR_GetProfileNum(optionsmenu.profile), 0); + CV_StealthSetValue(&cv_lastprofile[0], lastp); + } + else // != GS_MENU + { + // ONLY apply the profile if it's in use by anything currently. + UINT8 pnum = PR_GetProfileNum(optionsmenu.profile); + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + if (cv_lastprofile[i].value == pnum) + { + PR_ApplyProfile(pnum, i); + break; + } + } + } } #undef KEYHOLDFOR From 51d21ec3880a25f7bce910e6f76faecd6c8287fa Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Wed, 18 May 2022 02:24:29 +0200 Subject: [PATCH 261/379] Select profile after title screen --- src/d_main.c | 5 +- src/d_netcmd.c | 16 ++- src/d_netcmd.h | 4 + src/f_finale.c | 2 +- src/k_menu.h | 6 ++ src/k_menudef.c | 25 +++++ src/k_menudraw.c | 4 +- src/k_menufunc.c | 275 +++++++++++++++++++++++++++++++++-------------- 8 files changed, 249 insertions(+), 88 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 295130374..4a20ccace 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1023,7 +1023,8 @@ void D_StartTitle(void) advancedemo = false; F_StartTitleScreen(); - currentMenu = &MainDef; // reset the current menu ID + M_InitOptions(0); // Make sure the option menu is ready to go since we need to select a profile. + currentMenu = &MAIN_ProfilesDef; // reset the current menu ID // Reset the palette if (rendermode != render_none) @@ -1178,7 +1179,7 @@ static void IdentifyVersion(void) #ifdef USE_PATCH_FILE D_AddFile(startupiwads, va(pandf,srb2waddir,PATCHNAME)); // SPECIFIC HACK TO NEW-MENUS SO THAT MY DUMBASS STOPS FORGETTING TO ADD THE FILE (rip :youfuckedup:) - D_AddFile(startupiwads, va(pandf,srb2waddir,"newmenus.pk3")); + D_AddFile(startupiwads, va(pandf,srb2waddir,"newmenus.pk3")); #endif //// #undef TEXTURESNAME diff --git a/src/d_netcmd.c b/src/d_netcmd.c index eb2c7388d..ffef4ec32 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -303,15 +303,19 @@ consvar_t cv_followercolor[MAXSPLITSCREENPLAYERS] = { // last selected profile, unaccessible cvar only set internally but is saved. // It's used to know what profile to autoload you to when you get into the character setup. -static CV_PossibleValue_t lastprofile_cons_t[] = {{0, "MIN"}, {MAXPROFILES, "MAX"}, {0, NULL}}; +static CV_PossibleValue_t lastprofile_cons_t[] = {{-1, "MIN"}, {MAXPROFILES, "MAX"}, {0, NULL}}; consvar_t cv_lastprofile[MAXSPLITSCREENPLAYERS] = { - CVAR_INIT ("lastprofile", "1", CV_SAVE|CV_HIDDEN, lastprofile_cons_t, NULL), - CVAR_INIT ("lastprofile2", "1", CV_SAVE|CV_HIDDEN, lastprofile_cons_t, NULL), - CVAR_INIT ("lastprofile3", "1", CV_SAVE|CV_HIDDEN, lastprofile_cons_t, NULL), - CVAR_INIT ("lastprofile4", "1", CV_SAVE|CV_HIDDEN, lastprofile_cons_t, NULL), + CVAR_INIT ("lastprofile", "0", CV_SAVE|CV_HIDDEN, lastprofile_cons_t, NULL), + CVAR_INIT ("lastprofile2", "0", CV_SAVE|CV_HIDDEN, lastprofile_cons_t, NULL), + CVAR_INIT ("lastprofile3", "0", CV_SAVE|CV_HIDDEN, lastprofile_cons_t, NULL), + CVAR_INIT ("lastprofile4", "0", CV_SAVE|CV_HIDDEN, lastprofile_cons_t, NULL), }; +// currently loaded profile for P1 menuing. +// You choose this profile when starting the game, this will also set lastprofile[0] +consvar_t cv_currprofile = CVAR_INIT ("currprofile", "-1", CV_HIDDEN, lastprofile_cons_t, NULL); + consvar_t cv_skipmapcheck = CVAR_INIT ("skipmapcheck", "Off", CV_SAVE, CV_OnOff, NULL); INT32 cv_debug; @@ -876,6 +880,8 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_lastprofile[i]); } + CV_RegisterVar(&cv_currprofile); + // preferred number of players CV_RegisterVar(&cv_splitplayers); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 7a385ba92..bcaa051b9 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -25,6 +25,10 @@ extern consvar_t cv_follower[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_followercolor[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_lastprofile[MAXSPLITSCREENPLAYERS]; +// current profile loaded. +// Used to know how to make the options menu behave among other things. +extern consvar_t cv_currprofile; + // preferred number of players extern consvar_t cv_splitplayers; diff --git a/src/f_finale.c b/src/f_finale.c index d28fa3d9d..14a353bef 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -67,7 +67,7 @@ mobj_t *titlemapcameraref = NULL; // menu presentation state char curbgname[9]; SINT8 curfadevalue; -INT32 curbgcolor; +INT32 curbgcolor = 31; // Please stop assaulting my eyes. INT32 curbgxspeed; INT32 curbgyspeed; boolean curbghide; diff --git a/src/k_menu.h b/src/k_menu.h index 004fc1bef..d2f9a52e7 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -224,6 +224,10 @@ typedef enum extern menuitem_t OPTIONS_Profiles[]; extern menu_t OPTIONS_ProfilesDef; +// Separate menu to avoid spaghetti code etc. +extern menuitem_t MAIN_Profiles[]; +extern menu_t MAIN_ProfilesDef; + extern menuitem_t OPTIONS_EditProfile[]; extern menu_t OPTIONS_EditProfileDef; @@ -710,6 +714,7 @@ extern consvar_t cv_dummyprofilename; extern consvar_t cv_dummyprofileplayername; extern consvar_t cv_dummyprofilekickstart; +void M_ResetOptions(void); void M_InitOptions(INT32 choice); // necessary for multiplayer since there's some options we won't want to access void M_OptionsTick(void); boolean M_OptionsInputs(INT32 ch); @@ -726,6 +731,7 @@ void M_HandleProfileSelect(INT32 ch); // profile edition void M_HandleProfileEdit(void); void M_ProfileDeviceSelect(INT32 choice); +void M_ConfirmProfile(INT32 choice); boolean M_ProfileEditInputs(INT32 ch); void M_HandleProfileControls(void); diff --git a/src/k_menudef.c b/src/k_menudef.c index 068863b43..fb74d2b63 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -431,6 +431,27 @@ menu_t OPTIONS_ProfilesDef = { NULL, }; +// Duplicate for main profile select. +menuitem_t MAIN_Profiles[] = { + {IT_KEYHANDLER | IT_NOTHING, NULL, "Select a profile to use or create a new Profile.", + NULL, {.routine = M_HandleProfileSelect}, 0, 0}, // dummy menuitem for the control func +}; + +menu_t MAIN_ProfilesDef = { + sizeof (MAIN_Profiles) / sizeof (menuitem_t), + NULL, + 0, + MAIN_Profiles, + 32, 80, + SKINCOLOR_ULTRAMARINE, 0, + 2, 10, + M_DrawProfileSelect, + M_OptionsTick, + NULL, + NULL, + NULL, +}; + menuitem_t OPTIONS_EditProfile[] = { {IT_STRING | IT_CVAR | IT_CV_STRING, "Profile Name", "6-character long name to identify this Profile.", @@ -444,6 +465,10 @@ menuitem_t OPTIONS_EditProfile[] = { {IT_STRING | IT_CALL, "Controls", "Select the button mappings for this Profile.", NULL, {.routine = M_ProfileDeviceSelect}, 0, 0}, + + {IT_STRING | IT_CALL, "Confirm", "Confirm changes.", + NULL, {.routine = M_ConfirmProfile}, 0, 0}, + }; menu_t OPTIONS_EditProfileDef = { diff --git a/src/k_menudraw.c b/src/k_menudraw.c index a151345cb..69d952b47 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2622,7 +2622,7 @@ void M_DrawEditProfile(void) V_DrawGamemodeString(x + (menutransition.tics*32), y - 6, V_ALLOWLOWERCASE, colormap, currentMenu->menuitems[i].text); // Cvar specific handling - switch (currentMenu->menuitems[i].status & IT_TYPE) + /*switch (currentMenu->menuitems[i].status & IT_TYPE) { case IT_CVAR: { @@ -2639,7 +2639,7 @@ void M_DrawEditProfile(void) break; } } - } + }*/ y += 34; diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 39ff7c2b4..ccb198196 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -85,7 +85,7 @@ boolean menuactive = false; boolean fromlevelselect = false; // current menudef -menu_t *currentMenu = &MainDef; +menu_t *currentMenu = &OPTIONS_ProfilesDef; char dummystaffname[22]; @@ -976,8 +976,20 @@ void M_StartControlPanel(void) } else if (!Playing()) { - currentMenu = &MainDef; + // options don't need initializing here. + PR_ApplyProfile(0, 0); // apply guest profile to player 0 by default. + // this is to garantee that the controls aren't fucked up. + + currentMenu = &MAIN_ProfilesDef; + optionsmenu.profilen = cv_lastprofile[0].value; + + // make sure we don't overstep that. + if (optionsmenu.profilen > PR_GetNumProfiles()) + optionsmenu.profilen = PR_GetNumProfiles(); + itemOn = 0; + + CV_StealthSetValue(&cv_currprofile, -1); // Make sure to reset that. } else { @@ -3670,6 +3682,26 @@ void M_MPRoomSelectInit(INT32 choice) // Options menu: struct optionsmenu_s optionsmenu; +void M_ResetOptions(void) +{ + optionsmenu.ticker = 0; + optionsmenu.offset = 0; + + optionsmenu.optx = 0; + optionsmenu.opty = 0; + optionsmenu.toptx = 0; + optionsmenu.topty = 0; + + // BG setup: + optionsmenu.currcolour = OPTIONS_MainDef.extra1; + optionsmenu.lastcolour = 0; + optionsmenu.fade = 0; + + // For profiles: + memset(setup_player, 0, sizeof(setup_player)); + optionsmenu.profile = NULL; +} + void M_InitOptions(INT32 choice) { (void)choice; @@ -3689,18 +3721,7 @@ void M_InitOptions(INT32 choice) if (gamestate != GS_MENU) OPTIONS_MainDef.menuitems[mopt_profiles].status = IT_STRING | IT_TRANSTEXT; - optionsmenu.ticker = 0; - optionsmenu.offset = 0; - - optionsmenu.optx = 0; - optionsmenu.opty = 0; - optionsmenu.toptx = 0; - optionsmenu.topty = 0; - - // BG setup: - optionsmenu.currcolour = OPTIONS_MainDef.extra1; - optionsmenu.lastcolour = 0; - optionsmenu.fade = 0; + M_ResetOptions(); // So that pause doesn't go to the main menu... OPTIONS_MainDef.prevMenu = currentMenu; @@ -3711,9 +3732,6 @@ void M_InitOptions(INT32 choice) Moviemode_option_Onchange(); Addons_option_Onchange(); - // For profiles: - memset(setup_player, 0, sizeof(setup_player)); - M_SetupNextMenu(&OPTIONS_MainDef, false); } @@ -3925,6 +3943,65 @@ void M_VideoModeMenu(INT32 choice) M_SetupNextMenu(&OPTIONS_VideoModesDef, false); } +// Select the current profile for menu use and go to maindef. +static void M_FirstPickProfile(INT32 c) +{ + if (c == MA_YES) + { + M_ResetOptions(); // Reset all options variables otherwise things are gonna go reaaal bad lol. + optionsmenu.profile = NULL; // Make sure to get rid of that, too. + + CV_StealthSetValue(&cv_currprofile, optionsmenu.profilen); + PR_ApplyProfile(optionsmenu.profilen, 0); + M_SetupNextMenu(&MainDef, false); + + // Tell the game this is the last profile we picked. + CV_StealthSetValue(&cv_lastprofile[0], optionsmenu.profilen); + + // Save em! + PR_SaveProfiles(); + return; + } +} + +// Start menu edition. Call this with MA_YES if not used with a textbox. +static void M_StartEditProfile(INT32 c) +{ + + const INT32 maxp = PR_GetNumProfiles(); + + if (c == MA_YES) + { + if (optionsmenu.profilen == maxp) + PR_InitNewProfile(); // initialize the new profile. + + optionsmenu.profile = PR_GetProfile(optionsmenu.profilen); + + // This is now used to move the card we've selected. + optionsmenu.optx = 160; + optionsmenu.opty = 35; + optionsmenu.toptx = 130/2; + optionsmenu.topty = 0; + + // setup cvars + if (optionsmenu.profile->version) + { + CV_StealthSet(&cv_dummyprofilename, optionsmenu.profile->profilename); + CV_StealthSet(&cv_dummyprofileplayername, optionsmenu.profile->playername); + CV_StealthSetValue(&cv_dummyprofilekickstart, optionsmenu.profile->kickstartaccel); + } + else + { + CV_StealthSet(&cv_dummyprofilename, ""); + CV_StealthSet(&cv_dummyprofileplayername, ""); + CV_StealthSetValue(&cv_dummyprofilekickstart, 0); // off + } + + M_SetupNextMenu(&OPTIONS_EditProfileDef, false); + return; + } +} + void M_HandleProfileSelect(INT32 ch) { const UINT8 pid = 0; @@ -3964,42 +4041,44 @@ void M_HandleProfileSelect(INT32 ch) else if (M_MenuConfirmPressed(pid)) { - if (optionsmenu.profilen == 0) // Guest profile, you can't edit that one! + // Boot profile setup has already been done. + if (cv_currprofile.value > -1) { - S_StartSound(NULL, sfx_s3k7b); - M_StartMessage(M_GetText("Guest Profile cannot be edited.\nTo change parameters,\ncreate a new Profile."), NULL, MM_NOTHING); - M_SetMenuDelay(pid); - return; - } - S_StartSound(NULL, sfx_menu1); + if (optionsmenu.profilen == 0) // Guest profile, you can't edit that one! + { + S_StartSound(NULL, sfx_s3k7b); + M_StartMessage(M_GetText("Guest Profile cannot be edited.\nTo change parameters,\ncreate a new Profile."), NULL, MM_NOTHING); + M_SetMenuDelay(pid); + return; + } - if (optionsmenu.profilen == maxp) - PR_InitNewProfile(); // initialize the new profile. + S_StartSound(NULL, sfx_menu1); - optionsmenu.profile = PR_GetProfile(optionsmenu.profilen); - - // This is now used to move the card we've selected. - optionsmenu.optx = 160; - optionsmenu.opty = 35; - optionsmenu.toptx = 130/2; - optionsmenu.topty = 0; - - // setup cvars - if (optionsmenu.profile->version) - { - CV_StealthSet(&cv_dummyprofilename, optionsmenu.profile->profilename); - CV_StealthSet(&cv_dummyprofileplayername, optionsmenu.profile->playername); - CV_StealthSetValue(&cv_dummyprofilekickstart, optionsmenu.profile->kickstartaccel); + M_StartEditProfile(MA_YES); } else { - CV_StealthSet(&cv_dummyprofilename, ""); - CV_StealthSet(&cv_dummyprofileplayername, ""); - CV_StealthSetValue(&cv_dummyprofilekickstart, 0); // off + // We're on the profile selection screen. + if (optionsmenu.profilen == 0) + { + M_StartMessage(M_GetText("Are you sure you wish\nto use the Guest Profile?\nThis profile cannot be customised.\nIt is recommended to create\na new Profile instead.\n\n(Press A to confirm)"), FUNCPTRCAST(M_FirstPickProfile), MM_YESNO); + M_SetMenuDelay(pid); + return; + } + else if (optionsmenu.profilen == maxp) + { + M_StartMessage(M_GetText("Create a new Profile?\n\n(Press A to confirm)"), FUNCPTRCAST(M_StartEditProfile), MM_YESNO); + M_SetMenuDelay(pid); + return; + } + else + { + M_StartMessage(M_GetText("Are you sure you wish to\nselect this profile?\n\n(Press A to confirm)"), FUNCPTRCAST(M_FirstPickProfile), MM_YESNO); + M_SetMenuDelay(pid); + return; + } } - - M_SetupNextMenu(&OPTIONS_EditProfileDef, false); } else if (M_MenuBackPressed(pid)) @@ -4015,48 +4094,66 @@ void M_HandleProfileSelect(INT32 ch) } } +// Returns true if the profile can be saved, false otherwise. Also starts messages if necessary. +static boolean M_ProfileEditEnd(const UINT8 pid) +{ + UINT8 i; + + // check if some profiles have the same name + for (i = 0; i < PR_GetNumProfiles(); i++) + { + profile_t *check = PR_GetProfile(i); + + // For obvious reasons don't check if our name is the same as our name.... + if (check != optionsmenu.profile) + { + if (!(strcmp(optionsmenu.profile->profilename, check->profilename))) + { + S_StartSound(NULL, sfx_s3k7b); + M_StartMessage(M_GetText("Another Profile uses the same\nname identifier.\nPlease change the name\nof the Profile to save it."), NULL, MM_NOTHING); + M_SetMenuDelay(pid); + return false; + } + } + } + + if (optionsmenu.profilen == 0) // Guest profile, you can't edit that one! + { + S_StartSound(NULL, sfx_s3k7b); + M_StartMessage(M_GetText("Guest Profile cannot be edited.\nTo change parameters,\ncreate a new Profile."), NULL, MM_NOTHING); + M_SetMenuDelay(pid); + return false; + } + + return true; +} + +static void M_ProfileEditExit(void) +{ + optionsmenu.toptx = 160; + optionsmenu.topty = 35; + optionsmenu.resetprofile = true; // Reset profile after the transition is done. + + PR_SaveProfiles(); // save profiles after we do that. +} + // For profile edit, just make sure going back resets the card to its position, the rest is taken care of automatically. boolean M_ProfileEditInputs(INT32 ch) { - const UINT8 pid = 0; - UINT8 i; + (void) ch; + const UINT8 pid = 0; if (M_MenuBackPressed(pid)) { - // check if some profiles have the same name - for (i = 0; i < PR_GetNumProfiles(); i++) + if (M_ProfileEditEnd(pid)) { - profile_t *check = PR_GetProfile(i); - - // For obvious reasons don't check if our name is the same as our name.... - if (check != optionsmenu.profile) - { - if (!(strcmp(optionsmenu.profile->profilename, check->profilename))) - { - S_StartSound(NULL, sfx_s3k7b); - M_StartMessage(M_GetText("Another Profile uses the same\nname identifier.\nPlease change the name\nof the Profile to save it."), NULL, MM_NOTHING); - M_SetMenuDelay(pid); - return true; - } - } + M_ProfileEditExit(); + if (cv_currprofile.value == -1) + M_SetupNextMenu(&MAIN_ProfilesDef, false); + else + M_GoBack(0); } - - if (optionsmenu.profilen == 0) // Guest profile, you can't edit that one! - { - S_StartSound(NULL, sfx_s3k7b); - M_StartMessage(M_GetText("Guest Profile cannot be edited.\nTo change parameters,\ncreate a new Profile."), NULL, MM_NOTHING); - M_SetMenuDelay(pid); - return true; - } - - optionsmenu.toptx = 160; - optionsmenu.topty = 35; - optionsmenu.resetprofile = true; // Reset profile after the transition is done. - - PR_SaveProfiles(); // save profiles after we do that. - - M_GoBack(0); return true; } @@ -4088,6 +4185,28 @@ void M_HandleProfileEdit(void) strncpy(optionsmenu.profile->playername, cv_dummyprofileplayername.string, MAXPLAYERNAME); } +// Confirm Profile edi via button. +void M_ConfirmProfile(INT32 choice) +{ + const UINT8 pid = 0; + (void) choice; + + if (M_ProfileEditEnd(pid)) + { + if (cv_currprofile.value > -1) + { + M_ProfileEditExit(); + M_GoBack(0); + } + else + { + M_StartMessage(M_GetText("Are you sure you wish to\nselect this profile?\n\n(Press A to confirm)"), FUNCPTRCAST(M_FirstPickProfile), MM_YESNO); + M_SetMenuDelay(pid); + } + } + return; +} + // special menuitem key handler for video mode list void M_HandleVideoModes(INT32 ch) { From d5d34f9b996f1b5c6264346907abdbd2027009ff Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Wed, 18 May 2022 14:20:19 +0200 Subject: [PATCH 262/379] profiles may now be deleted from data options --- src/k_menu.h | 12 ++++++++ src/k_menudef.c | 24 +++++++++++++++ src/k_menudraw.c | 26 ++++++++++++++++ src/k_menufunc.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++- src/k_profiles.c | 19 ++++++++++++ src/k_profiles.h | 5 +++ 6 files changed, 165 insertions(+), 1 deletion(-) diff --git a/src/k_menu.h b/src/k_menu.h index d2f9a52e7..48864c869 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -288,6 +288,9 @@ extern menu_t OPTIONS_DataDiscordDef; extern menuitem_t OPTIONS_DataErase[]; extern menu_t OPTIONS_DataEraseDef; +extern menuitem_t OPTIONS_DataProfileErase[]; +extern menu_t OPTIONS_DataProfileEraseDef; + // EXTRAS extern menuitem_t EXTRAS_Main[]; extern menu_t EXTRAS_MainDef; @@ -449,6 +452,8 @@ extern boolean menuwipe; extern consvar_t cv_showfocuslost; extern consvar_t cv_chooseskin, cv_serversort; +void M_SetMenuDelay(UINT8 i); + void Moviemode_mode_Onchange(void); void Screenshot_option_Onchange(void); void Addons_option_Onchange(void); @@ -702,6 +707,8 @@ extern struct optionsmenu_s { UINT8 erasecontext; + UINT8 eraseprofilen; + // background: INT16 currcolour; INT16 lastcolour; @@ -723,6 +730,7 @@ void M_OptionsChangeBGColour(INT16 newcolour); // changes the background colour void M_HandleItemToggles(INT32 choice); // For item toggling void M_EraseData(INT32 choice); // For data erasing +void M_CheckProfileData(INT32 choice); // check if we have profiles. // profile selection menu void M_ProfileSelectInit(INT32 choice); @@ -745,6 +753,9 @@ void M_ProfileTryController(INT32 choice); void M_VideoModeMenu(INT32 choice); void M_HandleVideoModes(INT32 ch); +// data stuff +void M_HandleProfileErase(INT32 choice); + // Extras menu: #define DF_ENCORE 0x40 @@ -884,6 +895,7 @@ void M_DrawEditProfile(void); void M_DrawProfileControls(void); void M_DrawVideoModes(void); void M_DrawItemToggles(void); +void M_DrawProfileErase(void); extern tic_t shitsfree; // Extras menu: diff --git a/src/k_menudef.c b/src/k_menudef.c index fb74d2b63..3a570b74a 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -1344,6 +1344,12 @@ menuitem_t OPTIONS_DataErase[] = {IT_SPACE | IT_NOTHING, NULL, NULL, NULL, {NULL}, 0, 0}, + {IT_STRING | IT_CALL, "Erase Profile Data...", "Select a Profile to erase.", + NULL, {.routine = M_CheckProfileData}, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, {NULL}, 0, 0}, + {IT_STRING | IT_CALL, "\x85\x45rase all Data", "Be careful! What's deleted is gone forever!", NULL, {.routine = M_EraseData}, 0, 0}, @@ -1364,7 +1370,25 @@ menu_t OPTIONS_DataEraseDef = { NULL, }; +menuitem_t OPTIONS_DataProfileErase[] = +{ + {IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, {.routine = M_HandleProfileErase}, 0, 0}, +}; +menu_t OPTIONS_DataProfileEraseDef = { + sizeof (OPTIONS_DataProfileErase) / sizeof (menuitem_t), + &OPTIONS_DataEraseDef, + 0, + OPTIONS_DataProfileErase, + 48, 80, + SKINCOLOR_BLUEBERRY, 0, + 2, 10, + M_DrawProfileErase, + M_OptionsTick, + NULL, + NULL, + NULL +}; // extras menu menuitem_t EXTRAS_Main[] = diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 69d952b47..9faa1be66 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2554,6 +2554,32 @@ void M_DrawGenericOptions(void) } } +// *Heavily* simplified version of the generic options menu, cattered only towards erasing profiles. +void M_DrawProfileErase(void) +{ + INT32 x = currentMenu->x - menutransition.tics*48, y = currentMenu->y-SMALLLINEHEIGHT, i, cursory = 0; + UINT8 np = PR_GetNumProfiles(); + + M_DrawOptionsCogs(); + M_DrawMenuTooltips(); + M_DrawOptionsMovingButton(); + + for (i = 1; i < np; i++) + { + + profile_t *pr = PR_GetProfile(i); + + if (i == optionsmenu.eraseprofilen) + { + cursory = y; + V_DrawScaledPatch(x - 24, cursory, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); + } + + V_DrawString(x, y, i == optionsmenu.eraseprofilen ? highlightflags : 0, va("PRF%03d - %s (%s)", i, pr->profilename, pr->playername)); + y += SMALLLINEHEIGHT; + } +} + // Draws profile selection void M_DrawProfileSelect(void) { diff --git a/src/k_menufunc.c b/src/k_menufunc.c index ccb198196..08893ad5f 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1132,7 +1132,7 @@ void M_GoBack(INT32 choice) // // M_Ticker // -static void M_SetMenuDelay(UINT8 i) +void M_SetMenuDelay(UINT8 i) { menucmd[i].delayCount++; if (menucmd[i].delayCount < 1) @@ -4727,6 +4727,84 @@ void M_HandleItemToggles(INT32 choice) } } +// Check if we have any profile loaded. +void M_CheckProfileData(INT32 choice) +{ + UINT8 np = PR_GetNumProfiles(); + (void) choice; + + if (np < 2) + { + S_StartSound(NULL, sfx_s3k7b); + M_StartMessage("There are no custom Profiles.\n\n(Press any button)", NULL, MM_NOTHING); + return; + } + + optionsmenu.eraseprofilen = 1; + M_SetupNextMenu(&OPTIONS_DataProfileEraseDef, false); +} + +static void M_EraseProfileResponse(INT32 choice) +{ + if (choice == MA_YES) + { + S_StartSound(NULL, sfx_itrole); // bweh heh heh + + PR_DeleteProfile(optionsmenu.eraseprofilen); + + if (optionsmenu.eraseprofilen == cv_currprofile.value) + { + CV_StealthSetValue(&cv_currprofile, -1); + F_StartIntro(); + M_ClearMenus(true); + } + else + optionsmenu.eraseprofilen = 1; + } +} + +void M_HandleProfileErase(INT32 choice) +{ + const UINT8 pid = 0; + const UINT8 np = PR_GetNumProfiles()-1; + (void) choice; + + if (menucmd[pid].dpad_ud > 0) + { + S_StartSound(NULL, sfx_menu1); + optionsmenu.eraseprofilen++; + + if (optionsmenu.eraseprofilen > np) + optionsmenu.eraseprofilen = 1; + + M_SetMenuDelay(pid); + } + else if (menucmd[pid].dpad_ud < 0) + { + S_StartSound(NULL, sfx_menu1); + + if (optionsmenu.eraseprofilen == 1) + optionsmenu.eraseprofilen = np; + else + optionsmenu.eraseprofilen--; + + M_SetMenuDelay(pid); + } + else if (M_MenuBackPressed(pid)) + { + M_GoBack(0); + M_SetMenuDelay(pid); + } + else if (M_MenuConfirmPressed(pid)) + { + if (optionsmenu.eraseprofilen == cv_currprofile.value) + M_StartMessage("This profile and all of\nits data will be erased.\nAre you sure you want to proceed?\nAs this is your currently loaded Profile,\ndeleting this Profile would also\nreturn you to the Title Screen\n\n(Press A to confirm)", FUNCPTRCAST(M_EraseProfileResponse), MM_YESNO); + else + M_StartMessage("This profile and all of\nits data will be erased.\nAre you sure you want to proceed?\n\n(Press A to confirm)", FUNCPTRCAST(M_EraseProfileResponse), MM_YESNO); + + M_SetMenuDelay(pid); + } +} // Extras menu; // this is copypasted from the options menu but all of these are different functions in case we ever want it to look more unique diff --git a/src/k_profiles.c b/src/k_profiles.c index 3baa18159..b84c5fa40 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -80,6 +80,25 @@ profile_t* PR_GetProfile(INT32 num) return NULL; } +boolean PR_DeleteProfile(INT32 num) +{ + UINT8 i; + if (num <= 0 || num > numprofiles) + return false; + + // If we're deleting inbetween profiles, move everything. + if (num < numprofiles) + for (i = num; i < numprofiles-1; i++) + profilesList[i] = profilesList[i+1]; + + // In any case, delete the last profile as well. + profilesList[numprofiles] = NULL; + numprofiles--; + + PR_SaveProfiles(); + return true; +} + void PR_InitNewProfile(void) { char pname[PROFILENAMELEN+1] = "PRF"; diff --git a/src/k_profiles.h b/src/k_profiles.h index 070587284..7996a21bd 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -90,6 +90,11 @@ boolean PR_AddProfile(profile_t *p); // Returns a pointer to the profile you're asking for or NULL if the profile is uninitialized. profile_t* PR_GetProfile(INT32 num); +// PR_DeleteProfile(INT32 n) +// Deletes the specified profile. n cannot be 0. Returns false if the profile couldn't be deleted, true otherwise. +// This will also move every profile back accordingly to ensure the table has no empty profiles inbetween two valid profiles. +boolean PR_DeleteProfile(INT32 n); + // PR_InitNewProfile(void) // Initializes the first new profile void PR_InitNewProfile(void); From 0ee02ce1e1dc7f200e45aae49ca72674b8e7b6a8 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Wed, 18 May 2022 14:31:05 +0200 Subject: [PATCH 263/379] slight oversights --- src/k_menufunc.c | 9 +++++---- src/k_profiles.c | 4 ++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 08893ad5f..c3fa1c22c 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -976,20 +976,21 @@ void M_StartControlPanel(void) } else if (!Playing()) { + // we need to do this before setting ApplyProfile otherwise funky things are going to happen. + currentMenu = &MAIN_ProfilesDef; + optionsmenu.profilen = cv_lastprofile[0].value; + // options don't need initializing here. PR_ApplyProfile(0, 0); // apply guest profile to player 0 by default. // this is to garantee that the controls aren't fucked up. - currentMenu = &MAIN_ProfilesDef; - optionsmenu.profilen = cv_lastprofile[0].value; - // make sure we don't overstep that. if (optionsmenu.profilen > PR_GetNumProfiles()) optionsmenu.profilen = PR_GetNumProfiles(); itemOn = 0; - CV_StealthSetValue(&cv_currprofile, -1); // Make sure to reset that. + CV_StealthSetValue(&cv_currprofile, -1); // Make sure to reset that as it is set by PR_ApplyProfile which we kind of hack together to force it. } else { diff --git a/src/k_profiles.c b/src/k_profiles.c index b84c5fa40..e215cbc7f 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -182,6 +182,10 @@ void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum) // set memory cvar CV_StealthSetValue(&cv_lastprofile[playernum], profilenum); + + // If we're doing this on P1, also change current profile. + if (!playernum) + CV_StealthSetValue(&cv_currprofile, profilenum); } UINT8 PR_GetProfileNum(profile_t *p) From d0db7e43819f7d2062bb10ddf448eb0038b132cc Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 20 May 2022 01:15:29 +0200 Subject: [PATCH 264/379] rethink input mapping menu (again (again)) --- src/k_menu.h | 13 ++++++ src/k_menudraw.c | 77 +++++++++++++----------------------- src/k_menufunc.c | 100 ++++++++++++++++++++++++++++++++++++++++------- src/k_profiles.c | 14 +++++++ src/k_profiles.h | 5 +++ 5 files changed, 146 insertions(+), 63 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 48864c869..1362b56dd 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -228,6 +228,15 @@ extern menu_t OPTIONS_ProfilesDef; extern menuitem_t MAIN_Profiles[]; extern menu_t MAIN_ProfilesDef; +typedef enum +{ + popt_profilename = 0, + popt_profilepname, + popt_char, + popt_controls, + popt_confirm, +} popt_e; + extern menuitem_t OPTIONS_EditProfile[]; extern menu_t OPTIONS_EditProfileDef; @@ -679,6 +688,10 @@ extern struct optionsmenu_s { boolean resetprofile; // After going back from the edit menu, this tells the profile select menu to kill the profile data after the transition. profile_t *profile; // Pointer to the profile we're editing + INT32 tempcontrols[num_gamecontrols][MAXINPUTMAPPING]; + // Temporary buffer where we're gonna store game controls. + // This is only applied to the profile when you exit out of the controls menu. + INT16 controlscroll; // scrolling for the control menu.... UINT8 bindcontrol; // 0: not binding, 1: binding control #1, 2: binding control #2 INT16 bindtimer; // Timer until binding is cancelled (5s) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 9faa1be66..ebfacbdf5 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2634,44 +2634,37 @@ void M_DrawEditProfile(void) // Draw the menu options... for (i = 0; i < currentMenu->numitems; i++) { - switch (currentMenu->menuitems[i].status & IT_DISPLAY) + + UINT8 *colormap = NULL; + INT32 tflag = (currentMenu->menuitems[i].status & IT_TRANSTEXT) ? V_TRANSLUCENT : 0; + + if (i == itemOn) + colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE); + + // Background + V_DrawFill(0, y, 400 - (menutransition.tics*64), 24, itemOn == i ? 169 : 30); // 169 is the plague colourization + // Text + V_DrawGamemodeString(x + (menutransition.tics*32), y - 6, V_ALLOWLOWERCASE|tflag, colormap, currentMenu->menuitems[i].text); + + // Cvar specific handling + /*switch (currentMenu->menuitems[i].status & IT_TYPE) { - case IT_STRING: { - - UINT8 *colormap = NULL; - if (i == itemOn) - colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE); - - // Background - V_DrawFill(0, y, 400 - (menutransition.tics*64), 24, itemOn == i ? 169 : 30); // 169 is the plague colourization - // Text - V_DrawGamemodeString(x + (menutransition.tics*32), y - 6, V_ALLOWLOWERCASE, colormap, currentMenu->menuitems[i].text); - - // Cvar specific handling - /*switch (currentMenu->menuitems[i].status & IT_TYPE) + case IT_CVAR: + { + consvar_t *cv = currentMenu->menuitems[i].itemaction.cvar; + switch (currentMenu->menuitems[i].status & IT_CVARTYPE) { - case IT_CVAR: - { - consvar_t *cv = currentMenu->menuitems[i].itemaction.cvar; - switch (currentMenu->menuitems[i].status & IT_CVARTYPE) - { - case IT_CV_STRING: - V_DrawFill(0, y+24, 400 - (menutransition.tics*64), 16, itemOn == i ? 169 : 30); // 169 is the plague colourization - V_DrawString(x + 8, y + 29, V_ALLOWLOWERCASE, cv->string); - if (skullAnimCounter < 4 && i == itemOn) - V_DrawCharacter(x + 8 + V_StringWidth(cv->string, 0), y + 29, - '_' | 0x80, false); - y += 16; - break; - } + case IT_CV_STRING: + V_DrawFill(0, y+24, 400 - (menutransition.tics*64), 16, itemOn == i ? 169 : 30); // 169 is the plague colourization + V_DrawString(x + 8, y + 29, V_ALLOWLOWERCASE, cv->string); + if (skullAnimCounter < 4 && i == itemOn) + V_DrawCharacter(x + 8 + V_StringWidth(cv->string, 0), y + 29, '_' | 0x80, false); + y += 16; } - }*/ + } + }*/ - y += 34; - - break; - } - } + y += 34; } // Finally, draw the card ontop @@ -2758,20 +2751,6 @@ void M_DrawProfileControls(void) return; // Don't draw the rest if we're trying the controller. } - // If we're past here, draw some text warnings. - if (gamestate == GS_MENU || PR_GetProfileNum(optionsmenu.profile) == cv_lastprofile[pid].value) - { - if (gamestate != GS_MENU) // If we're in a menu we'll always use the current profile to map controls from regardless. - V_DrawCenteredThinString(229, 180, highlightflags|V_ALLOWLOWERCASE|V_6WIDTHSPACE, "This is your last used profile,"); - - V_DrawCenteredThinString(229, 190, highlightflags|V_ALLOWLOWERCASE|V_6WIDTHSPACE, "Control changes will happen in real time"); - } - else - { - V_DrawCenteredThinString(229, 180, highlightflags|V_ALLOWLOWERCASE|V_6WIDTHSPACE, "This isn't your last used profile,"); - V_DrawCenteredThinString(229, 180, highlightflags|V_ALLOWLOWERCASE|V_6WIDTHSPACE, "Changes will apply on next profile selection."); - } - // Tooltip // The text is slightly shifted hence why we don't just use M_DrawMenuTooltips() V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL); @@ -2841,7 +2820,7 @@ void M_DrawProfileControls(void) // Get userbound controls... for (k = 0; k < MAXINPUTMAPPING; k++) { - keys[k] = optionsmenu.profile->controls[gc][k]; + keys[k] = optionsmenu.tempcontrols[gc][k]; if (keys[k] == KEY_NULL) continue; set++; diff --git a/src/k_menufunc.c b/src/k_menufunc.c index c3fa1c22c..1305b5bcc 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3718,10 +3718,6 @@ void M_InitOptions(INT32 choice) OPTIONS_MainDef.menuitems[mopt_server].status = IT_STRING | IT_TRANSTEXT; } - // disable profiles outside of gs_menu altogether. - if (gamestate != GS_MENU) - OPTIONS_MainDef.menuitems[mopt_profiles].status = IT_STRING | IT_TRANSTEXT; - M_ResetOptions(); // So that pause doesn't go to the main menu... @@ -3977,6 +3973,9 @@ static void M_StartEditProfile(INT32 c) PR_InitNewProfile(); // initialize the new profile. optionsmenu.profile = PR_GetProfile(optionsmenu.profilen); + // copy this profile's controls into optionsmenu so that we can edit controls without changing them directly. + // we do this so that we don't edit a profile's controls in real-time and end up doing really weird shit. + memcpy(&optionsmenu.tempcontrols, optionsmenu.profile->controls, sizeof(gamecontroldefault)); // This is now used to move the card we've selected. optionsmenu.optx = 160; @@ -3998,6 +3997,18 @@ static void M_StartEditProfile(INT32 c) CV_StealthSetValue(&cv_dummyprofilekickstart, 0); // off } + // Setup greyout and stuff. + OPTIONS_EditProfile[popt_profilename].status = IT_STRING | IT_CVAR | IT_CV_STRING; + OPTIONS_EditProfile[popt_profilepname].status = IT_STRING | IT_CVAR | IT_CV_STRING; + OPTIONS_EditProfile[popt_char].status = IT_STRING | IT_CALL; + + if (gamestate != GS_MENU) // If we're modifying things mid game, transtext some of those! + { + OPTIONS_EditProfile[popt_profilename].status |= IT_TRANSTEXT; + OPTIONS_EditProfile[popt_profilepname].status |= IT_TRANSTEXT; + OPTIONS_EditProfile[popt_char].status |= IT_TRANSTEXT; + } + M_SetupNextMenu(&OPTIONS_EditProfileDef, false); return; } @@ -4157,6 +4168,11 @@ boolean M_ProfileEditInputs(INT32 ch) } return true; } + else if (M_MenuConfirmPressed(pid)) + { + if (currentMenu->menuitems[itemOn].status & IT_TRANSTEXT) + return true; // No. + } return false; } @@ -4369,18 +4385,54 @@ void M_HandleProfileControls(void) } } +static void M_ProfileTryControllerResponse(INT32 choice) +{ + if (choice == MA_YES) + { + optionsmenu.trycontroller = TICRATE*3; + // Apply these controls right now on P1's end. + memcpy(&gamecontrol[0], optionsmenu.tempcontrols, sizeof(gamecontroldefault)); + } +} + void M_ProfileTryController(INT32 choice) { (void) choice; // I managed to softlock myself during testing lol. - if (!optionsmenu.profile->controls[gc_x][0]) + if (!optionsmenu.tempcontrols[gc_x][0]) { M_StartMessage(M_GetText("You need to bind a key to [X]\nto use this feature.\n"), NULL, MM_NOTHING); return; } + else + { + M_StartMessage(M_GetText("Your inputs will temporarily be\nremapped to match this Profile's settings.\nThe controller graphic will animate\nto show you what buttons are being pressed.\nIs this okay?\n\n(Press A to continue)"), + FUNCPTRCAST(M_ProfileTryControllerResponse), MM_YESNO); + return; + } +} - optionsmenu.trycontroller = TICRATE*3; +static void M_ProfileControlSaveResponse(INT32 choice) +{ + + if (choice == MA_YES) + { + SINT8 belongsto = PR_ProfileUsedBy(optionsmenu.profile); + // Save the profile + optionsmenu.profile->kickstartaccel = cv_dummyprofilekickstart.value; + memcpy(&optionsmenu.profile->controls, optionsmenu.tempcontrols, sizeof(gamecontroldefault)); + + // If this profile is in-use by anyone, apply the changes immediately upon exiting. + // Don't apply the profile itself as that would lead to issues mid-game. + if (belongsto > -1 && belongsto < MAXSPLITSCREENPLAYERS) + { + memcpy(&gamecontrol[belongsto], optionsmenu.tempcontrols, sizeof(gamecontroldefault)); + CV_StealthSetValue(&cv_kickstartaccel[belongsto], cv_dummyprofilekickstart.value); + } + + M_GoBack(0); + } } boolean M_ProfileControlsInputs(INT32 ch) @@ -4396,13 +4448,21 @@ boolean M_ProfileControlsInputs(INT32 ch) else optionsmenu.trycontroller = TICRATE*3; + if (!optionsmenu.trycontroller) + { + // Reset controls to that of the current profile. + profile_t *cpr = PR_GetProfile(cv_currprofile.value); + memcpy(&gamecontrol[0], cpr->controls, sizeof(gamecontroldefault)); + + M_StartMessage(M_GetText("Your controls have been\nreverted to their previous state.\n\n(Press any key)"), NULL, MM_NOTHING); + } return true; } if (optionsmenu.bindcontrol) return true; // Eat all inputs there. We'll use a stupid hack in M_Responder instead. - SetDeviceOnPress(); // Update device constantly so that we don't stay stuck with otpions saying a device is unavailable just because we're mapping multiple devices... + //SetDeviceOnPress(); // Update device constantly so that we don't stay stuck with otpions saying a device is unavailable just because we're mapping multiple devices... if (M_MenuExtraPressed(pid)) { @@ -4413,7 +4473,7 @@ boolean M_ProfileControlsInputs(INT32 ch) INT32 i; for (i = 0; i < MAXINPUTMAPPING; i++) - optionsmenu.profile->controls[currentMenu->menuitems[itemOn].mvar1][i] = KEY_NULL; + optionsmenu.tempcontrols[currentMenu->menuitems[itemOn].mvar1][i] = KEY_NULL; S_StartSound(NULL, sfx_s3k66); } @@ -4422,10 +4482,20 @@ boolean M_ProfileControlsInputs(INT32 ch) } else if (M_MenuBackPressed(pid)) { + + SINT8 usedby = PR_ProfileUsedBy(optionsmenu.profile); + + if (usedby > -1) + M_StartMessage(M_GetText(va("As this is Player %d's active Profile,\ncontrol changes will be applied \nimmediately upon exiting this menu.\nIs this okay?\n\n(Press A to confirm)", usedby+1)), FUNCPTRCAST(M_ProfileControlSaveResponse), MM_YESNO); + else + M_StartMessage(M_GetText("Exiting will save the control changes\nfor this Profile.\nIs this okay?\n\n(Press A to confirm)"), FUNCPTRCAST(M_ProfileControlSaveResponse), MM_YESNO); + optionsmenu.profile->kickstartaccel = cv_dummyprofilekickstart.value; // Make sure to save kickstart accel. // Reapply player 1's real profile. PR_ApplyProfile(cv_lastprofile[0].value, 0); + + return true; } return false; @@ -4441,7 +4511,7 @@ void M_ProfileSetControl(INT32 ch) for (i = 0; i < MAXINPUTMAPPING; i++) { - if (optionsmenu.profile->controls[controln][i] == KEY_NULL) + if (optionsmenu.tempcontrols[controln][i] == KEY_NULL) { optionsmenu.bindcontrol = i+1; break; @@ -4465,7 +4535,7 @@ void M_MapProfileControl(event_t *ev) UINT8 where = n; // By default, we'll save the bind where we're supposed to map. INT32 i; - SetDeviceOnPress(); // Update cv_usejoystick + //SetDeviceOnPress(); // Update cv_usejoystick // Only consider keydown and joystick events to make sure we ignore ev_mouse and other events // See also G_MapEventsToControls @@ -4572,7 +4642,7 @@ void M_MapProfileControl(event_t *ev) // If that's the case, simply do nothing. for (i = 0; i < MAXINPUTMAPPING; i++) { - if (optionsmenu.profile->controls[controln][i] == c) + if (optionsmenu.tempcontrols[controln][i] == c) { optionsmenu.bindcontrol = 0; return; @@ -4582,11 +4652,13 @@ void M_MapProfileControl(event_t *ev) // With the way we do things, there cannot be instances of 'gaps' within the controls, so we don't need to pretend like we need to handle that. // Unless of course you tamper with the cfg file, but then it's *your* fault, not mine. - optionsmenu.profile->controls[controln][where] = c; + optionsmenu.tempcontrols[controln][where] = c; optionsmenu.bindcontrol = 0; // not binding anymore // If possible, reapply the profile... - if (gamestate == GS_MENU) // In menu? Apply this to P1, no questions asked. + // 19/05/22: Actually, no, don't do that, it just fucks everything up in too many cases. + + /*if (gamestate == GS_MENU) // In menu? Apply this to P1, no questions asked. { // Apply the profile's properties to player 1 but keep the last profile cv to p1's ACTUAL profile to revert once we exit. UINT8 lastp = cv_lastprofile[0].value; @@ -4605,7 +4677,7 @@ void M_MapProfileControl(event_t *ev) break; } } - } + }*/ } #undef KEYHOLDFOR diff --git a/src/k_profiles.c b/src/k_profiles.c index e215cbc7f..af7801c2e 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -198,4 +198,18 @@ UINT8 PR_GetProfileNum(profile_t *p) return i; } return 0; +} + +SINT8 PR_ProfileUsedBy(profile_t *p) +{ + UINT8 i; + UINT8 prn = PR_GetProfileNum(p); + + for (i=0; i < MAXSPLITSCREENPLAYERS; i++) + { + if (prn == cv_lastprofile[i].value) + return i; + } + + return -1; } \ No newline at end of file diff --git a/src/k_profiles.h b/src/k_profiles.h index 7996a21bd..d50d73bb2 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -117,4 +117,9 @@ void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum); // Gets the profile's index # in profilesList UINT8 PR_GetProfileNum(profile_t *p); +// PR_ProfileUsedBy(profile_t *p) +// Returns the player # this profile is used by (if any) +// If the profile belongs to no player, then this returns -1 +SINT8 PR_ProfileUsedBy(profile_t *p); + #endif From 65fcfbbb2ee4059b2deba0b014156df8680cb198 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 20 May 2022 01:20:38 +0200 Subject: [PATCH 265/379] For now, remove visual device warnings on control menu --- src/k_menudraw.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index ebfacbdf5..6d6f6f8f7 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2815,6 +2815,7 @@ void M_DrawProfileControls(void) { UINT32 vflags = V_6WIDTHSPACE; INT32 gc = currentMenu->menuitems[i].mvar1; + UINT8 available = 0, set = 0; // Get userbound controls... @@ -2831,8 +2832,12 @@ void M_DrawProfileControls(void) buf[0] = '\0'; + + // Cool as is this, this doesn't actually help show accurate info because of how some players would set inputs with keyboard and controller at once in a volatile way... + // @TODO: Address that mess, somehow? + // Can't reach any of them? - if (available == 0) + /*if (available == 0) { if (((3*optionsmenu.ticker)/(2*TICRATE)) & 1) // 1.5 seconds { @@ -2870,12 +2875,12 @@ void M_DrawProfileControls(void) { vflags |= V_REDMAP; } - } + }*/ if (buf[0]) ; else if (!set) - strcpy(buf, "NOT BOUND"); + strcpy(buf, "\x85NOT BOUND"); else { for (k = 0; k < MAXINPUTMAPPING; k++) From 26199dc2848c3b6390fb93e9b425b98b83cae0a6 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 20 May 2022 01:28:09 +0200 Subject: [PATCH 266/379] Add enter and esc for menu navigation on default profile --- src/g_input.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/g_input.c b/src/g_input.c index cf33b2b78..6263f9379 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -553,7 +553,7 @@ void G_DefineDefaultControls(void) gamecontroldefault[gc_z ][0] = 'd'; gamecontroldefault[gc_l ][0] = 'q'; gamecontroldefault[gc_r ][0] = 'e'; - gamecontroldefault[gc_start][0] = KEY_ENTER; + gamecontroldefault[gc_start][0] = KEY_ESCAPE; // * // Gamepad controls gamecontroldefault[gc_up ][1] = KEY_HAT1+0; // D-Pad Up @@ -574,6 +574,10 @@ void G_DefineDefaultControls(void) gamecontroldefault[gc_down ][2] = KEY_AXIS1+3; // Axis Y+ gamecontroldefault[gc_left ][2] = KEY_AXIS1+0; // Axis X- gamecontroldefault[gc_right][2] = KEY_AXIS1+1; // Axis X+ + + // Keyboard menu navigation + gamecontroldefault[gc_a ][2] = KEY_ENTER; + gamecontroldefault[gc_x ][2] = KEY_ESCAPE; // * -> Yes, this works, gc_start will take priority to open the menu and allow closing it with gc_x which is also esc :) } void G_CopyControls(INT32 (*setupcontrols)[MAXINPUTMAPPING], INT32 (*fromcontrols)[MAXINPUTMAPPING], const INT32 *gclist, INT32 gclen) From 926b210108c6625faecf5968b332dd72be87341b Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 20 May 2022 01:30:16 +0200 Subject: [PATCH 267/379] Cleanup some leftovers --- src/k_menufunc.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 1305b5bcc..832bd6c0a 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1782,8 +1782,6 @@ void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtyp message = Z_StrDup(string); DEBFILE(message); - CONS_Printf("M_StartMessage()...\n"); - // Rudementary word wrapping. // Simple and effective. Does not handle nonuniform letter sizes, colors, etc. but who cares. strlines = 0; @@ -4317,7 +4315,8 @@ void M_HandleVideoModes(INT32 ch) } // sets whatever device has had its key pressed to the active device. -static void SetDeviceOnPress(void) +// 20/05/22: Commented out for now but not deleted as it might still find some use in the future? +/*static void SetDeviceOnPress(void) { UINT8 i; @@ -4326,11 +4325,11 @@ static void SetDeviceOnPress(void) if (deviceResponding[i]) { CV_SetValue(&cv_usejoystick[0], i); // Force-set this joystick as the current joystick we're using for P1 (which is the only one controlling menus) - CONS_Printf("Using device %d for mappings\n", i); + //CONS_Printf("Using device %d for mappings\n", i); return; } } -} +}*/ // Prompt a device selection window (just tap any button on the device you want) From 54b7e8e78eb4c770c3d521d464d6398396578dda Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 20 May 2022 14:39:26 +0200 Subject: [PATCH 268/379] Disable p1 exclusive keyboard fallback when there are other local players --- src/g_game.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index f902919dd..950440fca 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -705,7 +705,8 @@ retrygetcontrol: } // If you're on controller, try your keyboard-based binds as an immediate backup. - if (p == 0 && deviceID > 0 && !tryingotherID) + // Do not do this if there are more than 1 local player. + if (p == 0 && deviceID > 0 && !tryingotherID && menuPlayers < 2 && !splitscreen) { deviceID = 0; goto retrygetcontrol; From 60449b8a26ac0e595374e3288176852edfe7e99e Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 20 May 2022 16:47:03 +0200 Subject: [PATCH 269/379] Fix crash when editing profile character with existing follower --- src/k_menudraw.c | 33 ++++++++++++++++++--------------- src/k_menufunc.c | 2 +- src/k_profiles.c | 13 +++++++++++++ src/k_profiles.h | 5 +++++ 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 6d6f6f8f7..8b63f9ff0 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1399,24 +1399,27 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) if (M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap)) V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap); - if (M_DrawFollowerSprite(x-44 +12, y+119, 0, V_FLIP, 0, sp)) + if (sp->mdepth >= CSSTEP_FOLLOWER) { - UINT16 col = (unsigned)p->followercolor; - patch_t *ico = W_CachePatchName(followers[sp->followern].icon, PU_CACHE); - UINT8 *fcolormap; - - switch (col) + if (M_DrawFollowerSprite(x-44 +12, y+119, 0, V_FLIP, 0, sp)) { - case FOLLOWERCOLOR_MATCH: // "Match" - col = sp->color; - break; - case FOLLOWERCOLOR_OPPOSITE: // "Opposite" - col = skincolors[sp->color].invcolor; - break; - } + UINT16 col = (unsigned)p->followercolor; + patch_t *ico = W_CachePatchName(followers[sp->followern].icon, PU_CACHE); + UINT8 *fcolormap; - fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); - V_DrawMappedPatch(x+14+18, y+66, 0, ico, fcolormap); + switch (col) + { + case FOLLOWERCOLOR_MATCH: // "Match" + col = sp->color; + break; + case FOLLOWERCOLOR_OPPOSITE: // "Opposite" + col = skincolors[sp->color].invcolor; + break; + } + + fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); + V_DrawMappedPatch(x+14+18, y+66, 0, ico, fcolormap); + } } if (sp->mdepth == CSSTEP_ALTS || sp->mdepth == CSSTEP_COLORS || sp->mdepth == CSSTEP_FOLLOWERCOLORS) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 832bd6c0a..ec47dc549 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2153,7 +2153,7 @@ void M_CharacterSelectInit(void) if (optionsmenu.profile) // In menu, setting up profile character/follower { setup_player[j].profilen = optionsmenu.profilen; - PR_ApplyProfile(setup_player[j].profilen, 0); + PR_ApplyProfileLight(setup_player[j].profilen, 0); } else // gamestate != GS_MENU, in that case, assume this is whatever profile we chose to play with. setup_player[j].profilen = cv_lastprofile[j].value; diff --git a/src/k_profiles.c b/src/k_profiles.c index af7801c2e..9ee9fcf36 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -188,6 +188,19 @@ void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum) CV_StealthSetValue(&cv_currprofile, profilenum); } +void PR_ApplyProfileLight(UINT8 profilenum, UINT8 playernum) +{ + profile_t *p = PR_GetProfile(profilenum); + + CV_StealthSet(&cv_skin[playernum], p->skinname); + CV_StealthSetValue(&cv_playercolor[playernum], p->color); + CV_StealthSet(&cv_playername[playernum], p->playername); + + // Followers + CV_StealthSet(&cv_follower[playernum], p->follower); + CV_StealthSetValue(&cv_followercolor[playernum], p->followercolor); +} + UINT8 PR_GetProfileNum(profile_t *p) { UINT8 i; diff --git a/src/k_profiles.h b/src/k_profiles.h index d50d73bb2..fd05ff1f6 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -113,6 +113,11 @@ void PR_LoadProfiles(void); // Applies the given profile's settings to the given player. void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum); +// PR_ApplyProfileLight(UINT8 profilenum, UINT8 playernum) +// Similar to PR_ApplyProfile but only applies skin and follower values. +// Controls, kickstartaccel and "current profile" data is *not* modified. +void PR_ApplyProfileLight(UINT8 profilenum, UINT8 playernum); + // PR_GetProfileNum(profile_t *p) // Gets the profile's index # in profilesList UINT8 PR_GetProfileNum(profile_t *p); From 3f201c460980c51fd1650703b8647027b49e7ffb Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 20 May 2022 17:10:19 +0200 Subject: [PATCH 270/379] Don't allow creating new profiles mid-game --- src/k_menufunc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index ec47dc549..5233ba67d 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -4062,6 +4062,13 @@ void M_HandleProfileSelect(INT32 ch) M_SetMenuDelay(pid); return; } + else if (optionsmenu.profilen == maxp && gamestate != GS_MENU) + { + S_StartSound(NULL, sfx_s3k7b); + M_StartMessage(M_GetText("Cannot create a New Profile\nmid-game. Return to the\nTitle Screen first."), NULL, MM_NOTHING); + M_SetMenuDelay(pid); + return; + } S_StartSound(NULL, sfx_menu1); From f4b6c551486b0eda90ad891a207626fc4f42b32b Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 20 May 2022 17:18:31 +0200 Subject: [PATCH 271/379] Fix crash trying to draw skins that aren't there on profile charsel --- src/k_menudraw.c | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 8b63f9ff0..96810c19c 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1396,29 +1396,32 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) skinnum = setup_chargrid[sp->gridx][sp->gridy].skinlist[sp->clonenum]; - if (M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap)) - V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap); - - if (sp->mdepth >= CSSTEP_FOLLOWER) + if (skinnum >= 0) { - if (M_DrawFollowerSprite(x-44 +12, y+119, 0, V_FLIP, 0, sp)) + if (M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap)) + V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap); + + if (sp->mdepth >= CSSTEP_FOLLOWER) { - UINT16 col = (unsigned)p->followercolor; - patch_t *ico = W_CachePatchName(followers[sp->followern].icon, PU_CACHE); - UINT8 *fcolormap; - - switch (col) + if (M_DrawFollowerSprite(x-44 +12, y+119, 0, V_FLIP, 0, sp)) { - case FOLLOWERCOLOR_MATCH: // "Match" - col = sp->color; - break; - case FOLLOWERCOLOR_OPPOSITE: // "Opposite" - col = skincolors[sp->color].invcolor; - break; - } + UINT16 col = (unsigned)p->followercolor; + patch_t *ico = W_CachePatchName(followers[sp->followern].icon, PU_CACHE); + UINT8 *fcolormap; - fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); - V_DrawMappedPatch(x+14+18, y+66, 0, ico, fcolormap); + switch (col) + { + case FOLLOWERCOLOR_MATCH: // "Match" + col = sp->color; + break; + case FOLLOWERCOLOR_OPPOSITE: // "Opposite" + col = skincolors[sp->color].invcolor; + break; + } + + fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); + V_DrawMappedPatch(x+14+18, y+66, 0, ico, fcolormap); + } } } From 95e4bc21ff37e93668762ce58d59a4109eb02dc5 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 20 May 2022 20:07:25 +0200 Subject: [PATCH 272/379] Allow P1 to retain controller usage when playing alone even if they started charsel with keyboard --- src/k_menu.h | 5 +++++ src/k_menufunc.c | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/k_menu.h b/src/k_menu.h index 1362b56dd..b39d1c872 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -528,6 +528,11 @@ typedef struct setup_player_s UINT8 color; UINT8 mdepth; + // Hack, save player 1's original device even if they init charsel with keyboard. + // If they play ALONE, allow them to retain that original device, otherwise, ignore this. + // We can allow them to retain the device with no consequence as when P1 is alone, they have exclusive keyboard fallback options. + UINT8 ponedevice; + INT32 followern; INT16 followercolor; tic_t follower_tics; diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 5233ba67d..26e854aba 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2287,6 +2287,13 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num) if (M_DeviceAvailable(i, setup_numplayers) == true) { // Available!! Let's use this one!! + + // if P1 is setting up using keyboard (device 0), save their last used device. + // this is to allow them to retain controller usage when they play alone. + // Because let's face it, when you test mods, you're often lazy to grab your controller for menuing :) + if (!i && !num) + setup_player[num].ponedevice = cv_usejoystick[num].value; + CV_SetValue(&cv_usejoystick[num], i); //CONS_Printf("Device for %d set to %d\n", num, i); //CONS_Printf("========\n"); @@ -2903,6 +2910,14 @@ void M_CharacterSelectTick(void) } CV_StealthSetValue(&cv_splitplayers, setup_numplayers); + + // P1 is alone, set their old device just in case. + if (setup_numplayers < 2) + { + CONS_Printf("Reseting controller device for P1...\n"); + CV_StealthSetValue(&cv_usejoystick[0], setup_player[0].ponedevice); + } + M_SetupNextMenu(&PLAY_MainDef, false); } } From 30aec86479781791493dc19072328a8f91145ad8 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 20 May 2022 21:36:43 +0200 Subject: [PATCH 273/379] Allow P1 to add splitscreen players on command, allow console in menus --- src/d_main.c | 33 ++++++++++++++------------- src/d_netcmd.c | 4 ++++ src/d_netcmd.h | 4 ++++ src/k_menufunc.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 83 insertions(+), 17 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 4a20ccace..55b55c15c 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -206,6 +206,23 @@ void D_ProcessEvents(void) continue; } + // console input +#ifdef HAVE_THREADS + I_lock_mutex(&con_mutex); +#endif + { + eaten = CON_Responder(ev); + } +#ifdef HAVE_THREADS + I_unlock_mutex(con_mutex); +#endif + + if (eaten) + { + hu_keystrokes = true; + continue; // ate the event + } + // Menu input menuresponse = true; #ifdef HAVE_THREADS @@ -228,22 +245,6 @@ void D_ProcessEvents(void) continue; // demo ate the event */ - // console input -#ifdef HAVE_THREADS - I_lock_mutex(&con_mutex); -#endif - { - eaten = CON_Responder(ev); - } -#ifdef HAVE_THREADS - I_unlock_mutex(con_mutex); -#endif - - if (eaten) - { - hu_keystrokes = true; - continue; // ate the event - } G_Responder(ev); } diff --git a/src/d_netcmd.c b/src/d_netcmd.c index ffef4ec32..b4fb2f938 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -316,6 +316,9 @@ consvar_t cv_lastprofile[MAXSPLITSCREENPLAYERS] = { // You choose this profile when starting the game, this will also set lastprofile[0] consvar_t cv_currprofile = CVAR_INIT ("currprofile", "-1", CV_HIDDEN, lastprofile_cons_t, NULL); +// Cvar for using splitscreen with 1 device. +consvar_t cv_splitdevice = CVAR_INIT ("splitdevice", "Off", CV_HIDDEN, CV_OnOff, NULL); + consvar_t cv_skipmapcheck = CVAR_INIT ("skipmapcheck", "Off", CV_SAVE, CV_OnOff, NULL); INT32 cv_debug; @@ -881,6 +884,7 @@ void D_RegisterClientCommands(void) } CV_RegisterVar(&cv_currprofile); + CV_RegisterVar(&cv_splitdevice); // preferred number of players CV_RegisterVar(&cv_splitplayers); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index bcaa051b9..fb3a5ab03 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -29,6 +29,10 @@ extern consvar_t cv_lastprofile[MAXSPLITSCREENPLAYERS]; // Used to know how to make the options menu behave among other things. extern consvar_t cv_currprofile; +// CVar that allows starting as many splitscreens as you want with one device +// Intended for use with testing +extern consvar_t cv_splitdevice; + // preferred number of players extern consvar_t cv_splitplayers; diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 26e854aba..1360185d2 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -991,6 +991,7 @@ void M_StartControlPanel(void) itemOn = 0; CV_StealthSetValue(&cv_currprofile, -1); // Make sure to reset that as it is set by PR_ApplyProfile which we kind of hack together to force it. + CV_StealthSetValue(&cv_splitdevice, 0); // Disable this option by default. } else { @@ -2102,6 +2103,10 @@ void M_CharacterSelectInit(void) } //CONS_Printf("========\n"); + // On main menu, reset that! + if (gamestate == GS_MENU) + CV_StealthSetValue(&cv_splitdevice, 0); + memset(setup_chargrid, -1, sizeof(setup_chargrid)); for (i = 0; i < 9; i++) { @@ -2329,8 +2334,12 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num) static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num) { const UINT8 maxp = PR_GetNumProfiles() -1; + UINT8 realnum = num; // Used for profile when using splitdevice. UINT8 i; + if (cv_splitdevice.value) + num = 0; + if (menucmd[num].dpad_ud > 0) { p->profilen++; @@ -2378,7 +2387,7 @@ static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num) else if (M_MenuConfirmPressed(num)) { // Apply the profile. - PR_ApplyProfile(p->profilen, num); + PR_ApplyProfile(p->profilen, realnum); // Otherwise P1 would inherit the last player's profile in splitdevice and that's not what we want... M_SetupProfileGridPos(p); p->mdepth = CSSTEP_CHARS; @@ -2395,6 +2404,9 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) UINT8 numclones; + if (cv_splitdevice.value) + num = 0; + if (menucmd[num].dpad_ud > 0) { p->gridy++; @@ -2513,6 +2525,9 @@ static void M_HandleCharRotate(setup_player_t *p, UINT8 num) { UINT8 numclones = setup_chargrid[p->gridx][p->gridy].numskins; + if (cv_splitdevice.value) + num = 0; + if (menucmd[num].dpad_lr > 0) { p->clonenum++; @@ -2548,6 +2563,10 @@ static void M_HandleCharRotate(setup_player_t *p, UINT8 num) static void M_HandleColorRotate(setup_player_t *p, UINT8 num) { + + if (cv_splitdevice.value) + num = 0; + if (menucmd[num].dpad_lr > 0) { p->color++; @@ -2619,6 +2638,9 @@ static void M_AnimateFollower(setup_player_t *p) static void M_HandleChooseFollower(setup_player_t *p, UINT8 num) { + if (cv_splitdevice.value) + num = 0; + M_AnimateFollower(p); if (menucmd[num].dpad_lr > 0 && numfollowers) @@ -2667,6 +2689,10 @@ static void M_HandleChooseFollower(setup_player_t *p, UINT8 num) static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num) { + + if (cv_splitdevice.value) + num = 0; + M_AnimateFollower(p); if (menucmd[num].dpad_lr > 0) @@ -2715,6 +2741,24 @@ static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num) } } +// +static void M_HandleSplitDevice(void) +{ + + const UINT8 pid = 0; + setup_player_t *p = &setup_player[setup_numplayers]; + + if (M_MenuButtonPressed(pid, MBT_C)) + { + if (!cv_splitdevice.value) + M_StartMessage(M_GetText("Split device enabled.\nP1 can add additional players with [C].\nP1 must set all Players' parameters.\n\nIntended for use for multiplayer games\non the same device (Keyboard...)\nand testing purposes.\n\nPress any key"), NULL, MM_NOTHING); + + CV_StealthSetValue(&cv_splitdevice, 1); + S_StartSound(NULL, sfx_s3k65); + p->mdepth = CSSTEP_PROFILE; // Ready the player setup. + } +} + boolean M_CharacterSelectHandler(INT32 choice) { INT32 i; @@ -2728,6 +2772,19 @@ boolean M_CharacterSelectHandler(INT32 choice) if (p->delay == 0 && menucmd[i].delay == 0) { + + if (p->mdepth > CSSTEP_NONE && i == 0) + M_HandleSplitDevice(); + + // If splitdevice is true, only do the last non-ready setups. + if (cv_splitdevice.value) + { + // Previous setup isn't ready, go there. + // In any case, do setup 0 first. + if (i > 0 && setup_player[i-1].mdepth < CSSTEP_READY) + continue; + } + switch (p->mdepth) { case CSSTEP_NONE: // Enter Game From 479d1b1ffc6ec31677a9464e91c2e89a5c4cd35a Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 20 May 2022 22:34:04 +0200 Subject: [PATCH 274/379] Fix character switching mid game --- src/k_menufunc.c | 52 +++++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 1360185d2..a6ac43e71 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2093,20 +2093,22 @@ void M_CharacterSelectInit(void) UINT8 i, j; // While we're editing profiles, don't unset the devices for p1 - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + if (gamestate == GS_MENU) { - // Un-set devices for other players. - if (i != 0 || optionsmenu.profile) - CV_SetValue(&cv_usejoystick[i], -1); + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + // Un-set devices for other players. + if (i != 0 || optionsmenu.profile) + CV_SetValue(&cv_usejoystick[i], -1); - //CONS_Printf("Device for %d set to %d\n", i, -1); + //CONS_Printf("Device for %d set to %d\n", i, -1); + } + + // On main menu, reset that! + CV_StealthSetValue(&cv_splitdevice, 0); } //CONS_Printf("========\n"); - // On main menu, reset that! - if (gamestate == GS_MENU) - CV_StealthSetValue(&cv_splitdevice, 0); - memset(setup_chargrid, -1, sizeof(setup_chargrid)); for (i = 0; i < 9; i++) { @@ -2153,7 +2155,8 @@ void M_CharacterSelectInit(void) setup_player[j].color = skins[i].prefcolor; // If we're on prpfile select, skip straight to CSSTEP_CHARS - if ((optionsmenu.profile || gamestate != GS_MENU) && j == 0) + // do the same if we're midgame, but make sure to consider splitscreen properly. + if ((optionsmenu.profile && j == 0) || (gamestate != GS_MENU && j <= splitscreen)) { if (optionsmenu.profile) // In menu, setting up profile character/follower { @@ -2858,36 +2861,30 @@ static void M_MPConfirmCharacterSelection(void) UINT8 i; INT16 col; - char colstr[8]; - char commandnames[][4][MAXSTRINGLENGTH] = { {"skin ", "color ", "follower ", "followercolor "}, {"skin2 ", "color2 ", "follower2 ", "followercolor2 "}, {"skin3 ", "color3 ", "follower3 ", "followercolor3 "}, {"skin4 ", "color4 ", "follower4 ", "followercolor4 "}}; + char commandnames[][MAXSTRINGLENGTH] = { "skin ", "skin2 ", "skin3 ", "skin4 "}; // ^ laziness 100 (we append a space directly so that we don't have to do it later too!!!!) for (i = 0; i < splitscreen +1; i++) { char cmd[MAXSTRINGLENGTH]; - // skin - strcpy(cmd, commandnames[i][0]); - strcat(cmd, skins[setup_player[i].skin].name); - COM_ImmedExecute(cmd); - // colour // (convert the number that's saved to a string we can use) col = setup_player[i].color; - sprintf(colstr, "%d", col); - strcpy(cmd, commandnames[i][1]); - strcat(cmd, colstr); - COM_ImmedExecute(cmd); + CV_StealthSetValue(&cv_playercolor[i], col); // follower - strcpy(cmd, commandnames[i][2]); - strcat(cmd, va("%d", setup_player[i].followern)); - COM_ImmedExecute(cmd); + CV_StealthSetValue(&cv_follower[i], setup_player[i].followern); // follower color - strcpy(cmd, commandnames[i][3]); - strcat(cmd, va("%d", setup_player[i].followercolor)); + CV_StealthSetValue(&cv_followercolor[i], setup_player[i].followercolor); + + // finally, call the skin[x] console command. + // This will call SendNameAndColor which will synch everything we sent here and apply the changes! + strcpy(cmd, commandnames[i]); + strcat(cmd, skins[setup_player[i].skin].name); COM_ImmedExecute(cmd); + } M_ClearMenus(true); } @@ -2970,10 +2967,7 @@ void M_CharacterSelectTick(void) // P1 is alone, set their old device just in case. if (setup_numplayers < 2) - { - CONS_Printf("Reseting controller device for P1...\n"); CV_StealthSetValue(&cv_usejoystick[0], setup_player[0].ponedevice); - } M_SetupNextMenu(&PLAY_MainDef, false); } From c8c07aacc8dcd28fdac3b8a0c0b4db9898dd8389 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 21 May 2022 12:32:38 +0200 Subject: [PATCH 275/379] Add hardcoded keyboard defaults for menu navigation --- src/g_game.c | 75 ++++++++++++++++++++++++++++++++++++++++++++------- src/g_input.c | 4 --- 2 files changed, 66 insertions(+), 13 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 950440fca..521e5cb1b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -660,10 +660,28 @@ INT16 G_SoftwareClipAimingPitch(INT32 *aiming) return (INT16)((*aiming)>>16); } +// Default controls for keyboard. These are hardcoded and cannot be changed. +static INT32 keyboardMenuDefaults[][2] = { + {gc_a, KEY_ENTER}, + {gc_c, KEY_BACKSPACE}, + {gc_x, KEY_ESCAPE}, + {gc_left, KEY_LEFTARROW}, + {gc_right, KEY_RIGHTARROW}, + {gc_up, KEY_UPARROW}, + {gc_down, KEY_DOWNARROW}, + + // special control + {gc_start, KEY_ESCAPE}, + // 8 total controls* +}; + +#define KEYBOARDDEFAULTSSPLIT 7 + + INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers) { INT32 deviceID; - INT32 i; + INT32 i, j; INT32 deadzone = 0; boolean trydefaults = true; boolean tryingotherID = false; @@ -685,22 +703,59 @@ retrygetcontrol: for (i = 0; i < MAXINPUTMAPPING; i++) { INT32 key = controltable[i]; + INT32 menukey = KEY_NULL; INT32 value = 0; + boolean processinput = true; + + + // for menus, keyboards have defaults! + if (deviceID == 0) + { + + // In menus, check indexes 0 through 5 (everything besides gc_start) + // Outside of menus, only consider the hardcoded input for gc_start at index 6 + + INT32 maxj = menuactive ? KEYBOARDDEFAULTSSPLIT : KEYBOARDDEFAULTSSPLIT+1; + j = (!menuactive) ? KEYBOARDDEFAULTSSPLIT : 0; + + for (; j < maxj; j++) // check keyboardMenuDefaults + { + // the gc we're looking for + if (gc == keyboardMenuDefaults[j][0]) + { + menukey = keyboardMenuDefaults[j][1]; + break; + } + + // The key is mapped to *something else*...? + // Then don't process that as it would conflict with our hardcoded inputs. + else if (key == keyboardMenuDefaults[j][1]) + { + processinput = false; + break; + } + } + } // Invalid key number. - if (!G_KeyIsAvailable(key, deviceID)) + if (!G_KeyIsAvailable(key, deviceID) && !G_KeyIsAvailable(menukey, deviceID)) { continue; } - // It's possible to access this control right now, so let's disable the default control backup for later. - trydefaults = false; - - value = gamekeydown[deviceID][key]; - - if (value >= deadzone) + if (processinput) { - return value; + // It's possible to access this control right now, so let's disable the default control backup for later. + trydefaults = false; + + value = gamekeydown[deviceID][key]; + if (menukey && gamekeydown[deviceID][menukey]) + value = gamekeydown[deviceID][menukey]; + + if (value >= deadzone) + { + return value; + } } } @@ -755,6 +810,8 @@ loweringid: return 0; } +#undef KEYBOARDDEFAULTSSPLIT + boolean G_PlayerInputDown(UINT8 p, INT32 gc, UINT8 menuPlayers) { return (G_PlayerInputAnalog(p, gc, menuPlayers) != 0); diff --git a/src/g_input.c b/src/g_input.c index 6263f9379..120dd6a79 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -574,10 +574,6 @@ void G_DefineDefaultControls(void) gamecontroldefault[gc_down ][2] = KEY_AXIS1+3; // Axis Y+ gamecontroldefault[gc_left ][2] = KEY_AXIS1+0; // Axis X- gamecontroldefault[gc_right][2] = KEY_AXIS1+1; // Axis X+ - - // Keyboard menu navigation - gamecontroldefault[gc_a ][2] = KEY_ENTER; - gamecontroldefault[gc_x ][2] = KEY_ESCAPE; // * -> Yes, this works, gc_start will take priority to open the menu and allow closing it with gc_x which is also esc :) } void G_CopyControls(INT32 (*setupcontrols)[MAXINPUTMAPPING], INT32 (*fromcontrols)[MAXINPUTMAPPING], const INT32 *gclist, INT32 gclen) From 13c74efa41d1786835136b785d1e2c816a2b70db Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 21 May 2022 14:57:16 +0200 Subject: [PATCH 276/379] Give split players default controls while sellecting profile # --- src/k_menufunc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index a6ac43e71..792286bdf 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2301,6 +2301,10 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num) // Because let's face it, when you test mods, you're often lazy to grab your controller for menuing :) if (!i && !num) setup_player[num].ponedevice = cv_usejoystick[num].value; + else if (num) + // For any player past player 1, set controls to default profile controls, otherwise it's generally awful to do any menuing... + memcpy(&gamecontrol[num], gamecontroldefault, sizeof(gamecontroldefault)); + CV_SetValue(&cv_usejoystick[num], i); //CONS_Printf("Device for %d set to %d\n", num, i); From 399272946c23861e69bf6b22e94ba2fc898a46d7 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 21 May 2022 17:47:15 +0200 Subject: [PATCH 277/379] Tie POWERLEVEL to Profiles --- src/d_clisrv.c | 28 ++++++++++++++++++++++++++++ src/g_game.c | 3 +++ src/k_menudraw.c | 2 +- src/k_menufunc.c | 10 ++++++++++ src/k_profiles.c | 26 ++++++++++++++++++++++++++ src/k_profiles.h | 2 ++ 6 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 9da78a252..5a947531a 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2246,6 +2246,33 @@ static void Command_connect(void) CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n")); return; } + else if (cv_currprofile.value == 0) + { + CONS_Printf(M_GetText("You cannot connect while using the Guest Profile. Use a Custom Profile to play Online.\n")); + return; + } + else if (cv_currprofile.value == -1) + { + // No profile set, we're attempting to connect from the title screen. (Discord RPC / connect command) + // Automatically apply the last profiles for every potential split player. + // Make sure Player 1's Profile ISN'T the guest profile even if we do that. + + UINT8 i; + + CONS_Printf(M_GetText("No Profile set, attempting to use last used Profiles...\n")); + + for (i = 0; i < cv_splitplayers.value; i++) + { + if (cv_lastprofile[i].value || i > 0) + PR_ApplyProfile(cv_lastprofile[i].value, i); + else + { + CONS_Printf(M_GetText("Player 1's last used Profile is the Guest Profile, which cannot be used to play Online.\n")); + return; + } + } + CONS_Printf(M_GetText("Profiles have been automatically set according to the last used Profiles.\n")); + } // modified game check: no longer handled // we don't request a restart unless the filelist differs @@ -2308,6 +2335,7 @@ static void Command_connect(void) SplitScreen_OnChange(); } + M_ClearMenus(true); CL_ConnectToServer(); } #endif diff --git a/src/g_game.c b/src/g_game.c index 521e5cb1b..56653599a 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4122,6 +4122,9 @@ void G_SaveGameData(void) FIL_WriteFile(va(pandf, srb2home, gamedatafilename), savebuffer, length); free(savebuffer); save_p = savebuffer = NULL; + + // Also save profiles here. + PR_SaveProfiles(); } #define VERSIONSIZE 16 diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 96810c19c..c1fd5f9aa 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1369,7 +1369,7 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) colormap = R_GetTranslationColormap(TC_DEFAULT, p->color, GTC_CACHE); strcpy(pname, p->profilename); skinnum = R_SkinAvailable(p->skinname); - powerlevel = 1000; // Test + powerlevel = p->powerlevels[0]; // Only display race power level. } // check setup_player for colormap for the card. diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 792286bdf..311134a7d 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3565,9 +3565,19 @@ void M_MPOptSelectInit(INT32 choice) { INT16 arrcpy[3][3] = {{0,68,0}, {0,12,0}, {0,64,0}}; UINT8 i = 0, j = 0; // To copy the array into the struct + const UINT8 pid = 0; (void)choice; + // Don't allow guest profile online + if (cv_currprofile.value == 0) + { + M_StartMessage(M_GetText("Cannot play online with\nGuest Profile.\nMake a custom Profile and try again.\n\n(Press any key)"), NULL, MM_NOTHING); + S_StartSound(NULL, sfx_s3k7b); + M_SetMenuDelay(pid); + return; + } + mpmenu.modechoice = 0; mpmenu.ticker = 0; diff --git a/src/k_profiles.c b/src/k_profiles.c index 9ee9fcf36..f951c418c 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -26,6 +26,7 @@ INT32 PR_GetNumProfiles(void) profile_t* PR_MakeProfile(const char *prname, const char *pname, const char *sname, const UINT16 col, const char *fname, UINT16 fcol, INT32 controlarray[num_gamecontrols][MAXINPUTMAPPING]) { profile_t *new = Z_Malloc(sizeof(profile_t), PU_STATIC, NULL); + UINT8 i; new->version = PROFILEVER; @@ -43,6 +44,10 @@ profile_t* PR_MakeProfile(const char *prname, const char *pname, const char *sna // Copy from gamecontrol directly as we'll be setting controls up directly in the profile. memcpy(new->controls, controlarray, sizeof(new->controls)); + // Init both power levels + for (i = 0; i < PWRLV_NUMTYPES; i++) + new->powerlevels[i] = PWRLVRECORD_START; + return new; } @@ -114,6 +119,14 @@ void PR_SaveProfiles(void) { FILE *f = NULL; + // save powerlevel in the current profile. + // granted we're using a profile that isn't guest, that is. + if (cv_currprofile.value > 0) + { + profile_t *pr = PR_GetProfile(cv_currprofile.value); + memcpy(&pr->powerlevels, vspowerlevel, sizeof(vspowerlevel)); + } + f = fopen(va(pandf, srb2home, PROFILESFILE), "w"); if (f != NULL) { @@ -166,6 +179,14 @@ void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum) { profile_t *p = PR_GetProfile(profilenum); + // this CAN happen!! + if (p == NULL) + { + CONS_Printf("Profile '%d' could not be loaded as it does not exist. Guest Profile will be loaded instead.\n", profilenum); + profilenum = 0; // make sure to set this so that the cvar is set properly. + p = PR_GetProfile(0); // Use guest profile instead if things went south somehow. + } + CV_StealthSet(&cv_skin[playernum], p->skinname); CV_StealthSetValue(&cv_playercolor[playernum], p->color); CV_StealthSet(&cv_playername[playernum], p->playername); @@ -184,8 +205,13 @@ void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum) CV_StealthSetValue(&cv_lastprofile[playernum], profilenum); // If we're doing this on P1, also change current profile. + // and update the powerlevel local array. + if (!playernum) + { CV_StealthSetValue(&cv_currprofile, profilenum); + memcpy(&vspowerlevel, p->powerlevels, sizeof(p->powerlevels)); + } } void PR_ApplyProfileLight(UINT8 profilenum, UINT8 playernum) diff --git a/src/k_profiles.h b/src/k_profiles.h index fd05ff1f6..92c0797f1 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -57,6 +57,8 @@ typedef struct profile_s char follower[SKINNAMESIZE+1]; // Follower UINT16 followercolor; // Follower color + UINT16 powerlevels[PWRLV_NUMTYPES]; // PWRLV for race & battle. + // Player-specific consvars. // @TODO: List all of those boolean kickstartaccel; // cv_kickstartaccel From df64ea9ce67413f1139268fb2c899500024667f9 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 21 May 2022 17:50:25 +0200 Subject: [PATCH 278/379] Fix softlock when exiting profile controls when initially making a new Profile --- src/k_menufunc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 311134a7d..84adccf95 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -4571,7 +4571,7 @@ boolean M_ProfileControlsInputs(INT32 ch) SINT8 usedby = PR_ProfileUsedBy(optionsmenu.profile); - if (usedby > -1) + if (usedby > -1 && cv_currprofile.value) M_StartMessage(M_GetText(va("As this is Player %d's active Profile,\ncontrol changes will be applied \nimmediately upon exiting this menu.\nIs this okay?\n\n(Press A to confirm)", usedby+1)), FUNCPTRCAST(M_ProfileControlSaveResponse), MM_YESNO); else M_StartMessage(M_GetText("Exiting will save the control changes\nfor this Profile.\nIs this okay?\n\n(Press A to confirm)"), FUNCPTRCAST(M_ProfileControlSaveResponse), MM_YESNO); @@ -4579,7 +4579,8 @@ boolean M_ProfileControlsInputs(INT32 ch) optionsmenu.profile->kickstartaccel = cv_dummyprofilekickstart.value; // Make sure to save kickstart accel. // Reapply player 1's real profile. - PR_ApplyProfile(cv_lastprofile[0].value, 0); + if (cv_currprofile.value) + PR_ApplyProfile(cv_lastprofile[0].value, 0); return true; } From c58624bca20a53e244d5d1605702e138cf4dc445 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 21 May 2022 18:04:43 +0200 Subject: [PATCH 279/379] Fixed fresh profiles being able to have duplicate names after deleting other Profiles --- src/k_menufunc.c | 2 +- src/k_profiles.c | 26 +++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 84adccf95..680dd9c67 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -4579,7 +4579,7 @@ boolean M_ProfileControlsInputs(INT32 ch) optionsmenu.profile->kickstartaccel = cv_dummyprofilekickstart.value; // Make sure to save kickstart accel. // Reapply player 1's real profile. - if (cv_currprofile.value) + if (cv_currprofile.value > -1) PR_ApplyProfile(cv_lastprofile[0].value, 0); return true; diff --git a/src/k_profiles.c b/src/k_profiles.c index f951c418c..cdbc7b937 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -108,8 +108,32 @@ void PR_InitNewProfile(void) { char pname[PROFILENAMELEN+1] = "PRF"; profile_t *dprofile; + UINT8 usenum = numprofiles-1; + UINT8 i; + boolean nameok = false; - strcpy(pname, va("PRF%c", 'A'+numprofiles-1)); + // When deleting profile, it's possible to do some pretty wacko stuff that would lead a new fresh profile to share the same name as another profile we have never changed the name of. + while (!nameok) + { + strcpy(pname, va("PRF%c", 'A'+usenum-1)); + + for (i = 0; i < numprofiles; i++) + { + profile_t *pr = PR_GetProfile(i); + if (!strcmp(pr->profilename, pname)) + { + usenum++; + if (usenum > 'Z' -1) + usenum = 'A'; + + break; + } + + // if we got here, then it means the name is okay! + if (i == numprofiles-1) + nameok = true; + } + } dprofile = PR_MakeProfile(pname, PROFILEDEFAULTPNAME, PROFILEDEFAULTSKIN, PROFILEDEFAULTCOLOR, PROFILEDEFAULTFOLLOWER, PROFILEDEFAULTFOLLOWERCOLOR, gamecontroldefault); PR_AddProfile(dprofile); From e0754358fe7f4bd1082c5dd93ba21713abcdb939 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 21 May 2022 18:10:05 +0200 Subject: [PATCH 280/379] Shift cv_lastprofile values when deleting profiles --- src/k_profiles.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/k_profiles.c b/src/k_profiles.c index cdbc7b937..593d464f4 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -87,15 +87,29 @@ profile_t* PR_GetProfile(INT32 num) boolean PR_DeleteProfile(INT32 num) { - UINT8 i; + UINT8 i, j; if (num <= 0 || num > numprofiles) return false; // If we're deleting inbetween profiles, move everything. if (num < numprofiles) + { for (i = num; i < numprofiles-1; i++) + { profilesList[i] = profilesList[i+1]; + // Make sure to move cv_lastprofile values as well + for (j = 0; j < MAXSPLITSCREENPLAYERS; j++) + { + if (cv_lastprofile[j].value == num) + CV_StealthSetValue(&cv_lastprofile[j], 0); // If we were on the deleted profile, default back to guest. + + else if (cv_lastprofile[j].value == i+1) // Otherwise, shift our lastprofile number down to match the new order. + CV_StealthSetValue(&cv_lastprofile[j], cv_lastprofile[j].value-1); + } + } + } + // In any case, delete the last profile as well. profilesList[numprofiles] = NULL; numprofiles--; From aa0c6d12eba18032c964934b60c61e77ed4a6255 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 21 May 2022 19:34:59 +0100 Subject: [PATCH 281/379] Fix backing out of the input tester for a new profile causing a crash. --- src/k_menufunc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 680dd9c67..514109dd7 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -4538,6 +4538,8 @@ boolean M_ProfileControlsInputs(INT32 ch) { // Reset controls to that of the current profile. profile_t *cpr = PR_GetProfile(cv_currprofile.value); + if (cpr == NULL) + cpr = PR_GetProfile(0); // Creating a profile at boot, revert to guest profile memcpy(&gamecontrol[0], cpr->controls, sizeof(gamecontroldefault)); M_StartMessage(M_GetText("Your controls have been\nreverted to their previous state.\n\n(Press any key)"), NULL, MM_NOTHING); From 5b13d4f75d6f772fc7869c09e04e166061861a1c Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 21 May 2022 19:35:40 +0100 Subject: [PATCH 282/379] Repair character colors and followercolors via the menu, both visually and mechanically. * Fully reimplement the MenuColor system from 2.2's codebase, so super and emerald colours are now inaccessible again. * Add FOLLOWERCOLOR_ constants and internal loop support to M_GetColorBefore and M_GetColorAfter. * Fix improper initialisation of certain menu colour data. * Repair previously created (or manually-edited) profiles with invalid colours. * Add an actual function to turn followercolor constants to effective values. --- src/doomdef.h | 4 ++ src/k_menu.h | 9 +-- src/k_menudraw.c | 133 +++++++++++-------------------------- src/k_menufunc.c | 164 ++++++++++++++++++++++++++++++++-------------- src/k_profiles.c | 19 ++++++ src/k_profiles.h | 2 +- src/lua_baselib.c | 17 ++++- src/p_user.c | 30 ++++----- src/r_data.h | 2 - 9 files changed, 207 insertions(+), 173 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index 7e1213be2..070544900 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -226,6 +226,10 @@ typedef struct skincolor_s boolean accessible; // Accessible by the color command + setup menu } skincolor_t; +#define FOLLOWERCOLOR_MATCH UINT16_MAX +#define FOLLOWERCOLOR_OPPOSITE (UINT16_MAX-1) +UINT16 K_GetEffectiveFollowerColor(UINT16 followercolor, UINT16 playercolor); + typedef enum { SKINCOLOR_NONE = 0, diff --git a/src/k_menu.h b/src/k_menu.h index b39d1c872..b8648541d 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -488,11 +488,12 @@ void M_StopMessage(INT32 choice); void M_QuitResponse(INT32 ch); void M_QuitSRB2(INT32 choice); +extern UINT16 nummenucolors; void M_AddMenuColor(UINT16 color); void M_MoveColorBefore(UINT16 color, UINT16 targ); void M_MoveColorAfter(UINT16 color, UINT16 targ); -UINT16 M_GetColorBefore(UINT16 color); -UINT16 M_GetColorAfter(UINT16 color); +UINT16 M_GetColorBefore(UINT16 color, UINT16 amount, boolean follower); +UINT16 M_GetColorAfter(UINT16 color, UINT16 amount, boolean follower); void M_InitPlayerSetupColors(void); void M_FreePlayerSetupColors(void); @@ -525,7 +526,7 @@ typedef struct setup_player_s SINT8 clonenum; SINT8 rotate; UINT8 delay; - UINT8 color; + UINT16 color; UINT8 mdepth; // Hack, save player 1's original device even if they init charsel with keyboard. @@ -534,7 +535,7 @@ typedef struct setup_player_s UINT8 ponedevice; INT32 followern; - INT16 followercolor; + UINT16 followercolor; tic_t follower_tics; tic_t follower_timer; UINT8 follower_frame; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index c1fd5f9aa..e443e88cc 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -818,15 +818,15 @@ void M_DrawImageDef(void) static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) { angle_t angamt = ANGLE_MAX; - UINT8 numoptions; - UINT8 i; + UINT16 i, numoptions; + UINT16 l = 0, r = 0; if (p->mdepth == CSSTEP_ALTS) numoptions = setup_chargrid[p->gridx][p->gridy].numskins; else if (p->mdepth == CSSTEP_FOLLOWERCOLORS) - numoptions = numskincolors-1 +2; + numoptions = nummenucolors+2; else - numoptions = numskincolors-1; + numoptions = nummenucolors; angamt /= numoptions; @@ -836,9 +836,9 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) boolean subtract = (i & 1); angle_t ang = ((i+1)/2) * angamt; patch_t *patch = NULL; - UINT8 *colormap; - fixed_t radius; - INT16 n; + UINT8 *colormap = NULL; + fixed_t radius = 28<mdepth == CSSTEP_ALTS) { @@ -865,36 +865,24 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) INT16 diff; UINT16 col; - n = (p->followercolor-1) + numoptions/2; - - if (subtract) - n -= ((i+1)/2); - else - n += ((i+1)/2); - - n %= numoptions; - n++; - - if (!n) - continue; - - col = (unsigned)(n); - switch (col) + if (i == 0) { - case FOLLOWERCOLOR_MATCH: // "Match" - col = p->color; - break; - case FOLLOWERCOLOR_OPPOSITE: // "Opposite" - col = skincolors[p->color].invcolor; - break; + n = l = r = M_GetColorBefore(p->followercolor, numoptions/2, true); } + else if (subtract) + { + n = l = M_GetColorBefore(l, 1, true); + } + else + { + n = r = M_GetColorAfter(r, 1, true); + } + + col = K_GetEffectiveFollowerColor(n, p->color); colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); - if (n > p->followercolor) - diff = n - p->followercolor; - else - diff = p->followercolor - n; + diff = (numoptions - i)/2; // only 0 when i == numoptions-1 if (diff == 0) patch = W_CachePatchName("COLORSP2", PU_CACHE); @@ -903,29 +891,28 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) else patch = W_CachePatchName("COLORSP0", PU_CACHE); - radius = 28<width) << FRACBITS; - cx -= (SHORT(patch->width) << FRACBITS) >> 1; } else { INT16 diff; - n = (p->color-1) + numoptions/2; - if (subtract) - n -= ((i+1)/2); + if (i == 0) + { + n = l = r = M_GetColorBefore(p->color, numoptions/2, false); + } + else if (subtract) + { + n = l = M_GetColorBefore(l, 1, false); + } else - n += ((i+1)/2); - n %= numoptions; - n++; + { + n = r = M_GetColorAfter(r, 1, false); + } colormap = R_GetTranslationColormap(TC_DEFAULT, n, GTC_MENUCACHE); - if (n > p->color) - diff = n - p->color; - else - diff = p->color - n; + diff = (numoptions - i)/2; // only 0 when i == numoptions-1 if (diff == 0) patch = W_CachePatchName("COLORSP2", PU_CACHE); @@ -934,9 +921,6 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) else patch = W_CachePatchName("COLORSP0", PU_CACHE); - radius = 28<width) << FRACBITS; - cx -= (SHORT(patch->width) << FRACBITS) >> 1; } @@ -945,7 +929,7 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) else ang = ANGLE_90 + ang; - if (numoptions % 2) + if (numoptions & 1) ang = (signed)(ang - (angamt/2)); if (p->rotate) @@ -1034,19 +1018,9 @@ static void M_DrawFollowerList(setup_player_t *p, UINT8 num) if (W_LumpExists(fl.icon) && cf >= 0) { - UINT16 col = (unsigned)p->followercolor; + UINT16 col = K_GetEffectiveFollowerColor(p->followercolor, p->color); pp = W_CachePatchName(fl.icon, PU_CACHE); - switch (col) - { - case FOLLOWERCOLOR_MATCH: // "Match" - col = p->color; - break; - case FOLLOWERCOLOR_OPPOSITE: // "Opposite" - col = skincolors[p->color].invcolor; - break; - } - colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); V_DrawMappedPatch(x, y, 0, pp, colormap); if (i == 1) @@ -1137,19 +1111,7 @@ static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, INT32 addflags, { const fixed_t pi = (22<follower_timer)>>ANGLETOFINESHIFT) & FINEMASK); - color = p->followercolor; - - switch (color) - { - case FOLLOWERCOLOR_MATCH: // "Match" - color = p->color; - break; - case FOLLOWERCOLOR_OPPOSITE: // "Opposite" - color = skincolors[p->color].invcolor; - break; - default: - break; - } + color = K_GetEffectiveFollowerColor(p->followercolor, p->color); } colormap = R_GetTranslationColormap(TC_DEFAULT, color, GTC_MENUCACHE); @@ -1405,20 +1367,10 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) { if (M_DrawFollowerSprite(x-44 +12, y+119, 0, V_FLIP, 0, sp)) { - UINT16 col = (unsigned)p->followercolor; + UINT16 col = K_GetEffectiveFollowerColor(sp->followercolor, sp->color);; patch_t *ico = W_CachePatchName(followers[sp->followern].icon, PU_CACHE); UINT8 *fcolormap; - switch (col) - { - case FOLLOWERCOLOR_MATCH: // "Match" - col = sp->color; - break; - case FOLLOWERCOLOR_OPPOSITE: // "Opposite" - col = skincolors[sp->color].invcolor; - break; - } - fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); V_DrawMappedPatch(x+14+18, y+66, 0, ico, fcolormap); } @@ -1433,23 +1385,12 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) else if (skinnum > -1) // otherwise, read from profile. { - UINT16 col = (unsigned)p->followercolor; + UINT16 col = K_GetEffectiveFollowerColor(p->followercolor, p->color);; UINT8 fln = R_FollowerAvailable(p->follower); if (M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap)) V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap); - switch (col) - { - case FOLLOWERCOLOR_MATCH: // "Match" - col = p->color; - break; - case FOLLOWERCOLOR_OPPOSITE: // "Opposite" - col = skincolors[p->color].invcolor; - break; - } - - if (M_DrawFollowerSprite(x-44 +12, y+119, fln, V_FLIP, col, NULL)) { patch_t *ico = W_CachePatchName(followers[fln].icon, PU_CACHE); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 514109dd7..394d8d80b 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -501,16 +501,18 @@ void M_EraseData(INT32 choice) // BASIC MENU HANDLING // ========================================================================= +UINT16 nummenucolors = 0; + void M_AddMenuColor(UINT16 color) { menucolor_t *c; - // SRB2Kart: I do not understand vanilla doesn't need this but WE do???!?!??! - if (!skincolors[color].accessible) { + if (color >= numskincolors) { + CONS_Printf("M_AddMenuColor: color %d does not exist.",color); return; } - if (color >= numskincolors) { - CONS_Printf("M_AddMenuColor: color %d does not exist.",color); + // SRB2Kart: I do not understand vanilla doesn't need this but WE do???!?!??! + if (!skincolors[color].accessible) { return; } @@ -528,6 +530,8 @@ void M_AddMenuColor(UINT16 color) { menucolorhead->prev = c; menucolortail = c; } + + nummenucolors++; } void M_MoveColorBefore(UINT16 color, UINT16 targ) { @@ -614,36 +618,118 @@ void M_MoveColorAfter(UINT16 color, UINT16 targ) { t->next = c; } -UINT16 M_GetColorBefore(UINT16 color) { - menucolor_t *look; +UINT16 M_GetColorBefore(UINT16 color, UINT16 amount, boolean follower) +{ + menucolor_t *look = NULL; - if (color >= numskincolors) { - CONS_Printf("M_GetColorBefore: color %d does not exist.\n",color); - return 0; - } + for (; amount > 0; amount--) + { + if (follower == true) + { + if (color == FOLLOWERCOLOR_OPPOSITE) + { + look = menucolortail; + color = menucolortail->color; + continue; + } - for (look=menucolorhead;;look=look->next) { - if (look->color == color) - return look->prev->color; - if (look==menucolortail) + if (color == FOLLOWERCOLOR_MATCH) + { + look = NULL; + color = FOLLOWERCOLOR_OPPOSITE; + continue; + } + + if (color == menucolorhead->color) + { + look = NULL; + color = FOLLOWERCOLOR_MATCH; + continue; + } + } + + if (color == 0 || color >= numskincolors) + { + CONS_Printf("M_GetColorBefore: color %d does not exist.\n",color); return 0; + } + + if (look == NULL) + { + for (look = menucolorhead;; look = look->next) + { + if (look->color == color) + { + break; + } + if (look == menucolortail) + { + return 0; + } + } + } + + look = look->prev; + color = look->color; } + return color; } -UINT16 M_GetColorAfter(UINT16 color) { - menucolor_t *look; +UINT16 M_GetColorAfter(UINT16 color, UINT16 amount, boolean follower) +{ + menucolor_t *look = NULL; - if (color >= numskincolors) { - CONS_Printf("M_GetColorAfter: color %d does not exist.\n",color); - return 0; - } + for (; amount > 0; amount--) + { + if (follower == true) + { + if (color == menucolortail->color) + { + look = NULL; + color = FOLLOWERCOLOR_OPPOSITE; + continue; + } - for (look=menucolorhead;;look=look->next) { - if (look->color == color) - return look->next->color; - if (look==menucolortail) + if (color == FOLLOWERCOLOR_OPPOSITE) + { + look = NULL; + color = FOLLOWERCOLOR_MATCH; + continue; + } + + if (color == FOLLOWERCOLOR_MATCH) + { + look = menucolorhead; + color = menucolorhead->color; + continue; + } + } + + if (color == 0 || color >= numskincolors) + { + CONS_Printf("M_GetColorAfter: color %d does not exist.\n",color); return 0; + } + + if (look == NULL) + { + for (look = menucolorhead;; look = look->next) + { + if (look->color == color) + { + break; + } + if (look == menucolortail) + { + return 0; + } + } + } + + look = look->next; + color = look->color; } + return color; } void M_InitPlayerSetupColors(void) { @@ -2126,7 +2212,7 @@ void M_CharacterSelectInit(void) { // Default to no follower / match colour. setup_player[i].followern = -1; - setup_player[i].followercolor = -1; + setup_player[i].followercolor = FOLLOWERCOLOR_MATCH; // Set default selected profile to the last used profile for each player: // (Make sure we don't overshoot it somehow if we deleted profiles or whatnot) @@ -2576,18 +2662,14 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num) if (menucmd[num].dpad_lr > 0) { - p->color++; - if (p->color >= numskincolors) - p->color = 1; + p->color = M_GetColorAfter(p->color, 1, false); p->rotate = CSROTATETICS; M_SetMenuDelay(num); //CSROTATETICS S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s } else if (menucmd[num].dpad_lr < 0) { - p->color--; - if (p->color < 1) - p->color = numskincolors-1; + p->color = M_GetColorBefore(p->color, 1, false); p->rotate = -CSROTATETICS; M_SetMenuDelay(num); //CSROTATETICS S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s @@ -2696,7 +2778,6 @@ static void M_HandleChooseFollower(setup_player_t *p, UINT8 num) static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num) { - if (cv_splitdevice.value) num = 0; @@ -2704,29 +2785,14 @@ static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num) if (menucmd[num].dpad_lr > 0) { - p->followercolor++; - - // Go back to -2 (Opposite) - if (p->followercolor >= numskincolors) - p->followercolor = -2; - - // Make sure we skip 0. - if (p->followercolor == 0) - p->followercolor++; - + p->followercolor = M_GetColorAfter(p->followercolor, 1, true); p->rotate = CSROTATETICS; M_SetMenuDelay(num); //CSROTATETICS S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s } else if (menucmd[num].dpad_lr < 0) { - p->followercolor--; - if (p->followercolor < -2) - p->followercolor = numskincolors-1; - - if (p->followercolor == 0) - p->followercolor--; - + p->followercolor = M_GetColorBefore(p->followercolor, 1, true); p->rotate = -CSROTATETICS; M_SetMenuDelay(num); //CSROTATETICS S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s diff --git a/src/k_profiles.c b/src/k_profiles.c index 593d464f4..14fd53885 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -199,6 +199,25 @@ void PR_LoadProfiles(void) { profilesList[i] = Z_Malloc(sizeof(profile_t), PU_STATIC, NULL); fread(profilesList[i], sizeof(profile_t), 1, f); + + // Attempt to correct numerical footguns + if (profilesList[i]->color >= numskincolors + || profilesList[i]->color == 0 + || skincolors[profilesList[i]->color].accessible == false) + { + profilesList[i]->color = PROFILEDEFAULTCOLOR; + } + if (profilesList[i]->followercolor == FOLLOWERCOLOR_MATCH + || profilesList[i]->followercolor == FOLLOWERCOLOR_OPPOSITE) + { + ; // Valid, even outside the bounds + } + else if (profilesList[i]->followercolor >= numskincolors + || profilesList[i]->followercolor == 0 + || skincolors[profilesList[i]->followercolor].accessible == false) + { + profilesList[i]->followercolor = PROFILEDEFAULTFOLLOWERCOLOR; + } } fclose(f); diff --git a/src/k_profiles.h b/src/k_profiles.h index 92c0797f1..c65b3aed6 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -35,7 +35,7 @@ #define PROFILEDEFAULTSKIN "sonic" #define PROFILEDEFAULTCOLOR SKINCOLOR_SAPPHIRE #define PROFILEDEFAULTFOLLOWER "none" -#define PROFILEDEFAULTFOLLOWERCOLOR 255 +#define PROFILEDEFAULTFOLLOWERCOLOR FOLLOWERCOLOR_MATCH // Man I wish I had more than 16 friends!! diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 319448593..510a6f340 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -354,14 +354,26 @@ static int lib_pMoveColorAfter(lua_State *L) static int lib_pGetColorBefore(lua_State *L) { UINT16 color = (UINT16)luaL_checkinteger(L, 1); - lua_pushinteger(L, M_GetColorBefore(color)); + UINT16 amount = (UINT16)luaL_checkinteger(L, 2); + boolean follower = lua_optboolean(L, 3); + lua_pushinteger(L, M_GetColorBefore(color, amount, follower)); return 1; } static int lib_pGetColorAfter(lua_State *L) { UINT16 color = (UINT16)luaL_checkinteger(L, 1); - lua_pushinteger(L, M_GetColorAfter(color)); + UINT16 amount = (UINT16)luaL_checkinteger(L, 2); + boolean follower = lua_optboolean(L, 3); + lua_pushinteger(L, M_GetColorAfter(color, amount, follower)); + return 1; +} + +static int lib_pGetEffectiveFollowerColor(lua_State *L) +{ + UINT16 followercolor = (UINT16)luaL_checkinteger(L, 1); + UINT16 playercolor = (UINT16)luaL_checkinteger(L, 2); + lua_pushinteger(L, K_GetEffectiveFollowerColor(followercolor, playercolor)); return 1; } @@ -3926,6 +3938,7 @@ static luaL_Reg lib[] = { {"P_ReturnThrustX",lib_pReturnThrustX}, {"P_ReturnThrustY",lib_pReturnThrustY}, {"P_NukeEnemies",lib_pNukeEnemies}, + {"K_GetEffectiveFollowerColor",lib_pGetEffectiveFollowerColor}, // p_map {"P_CheckPosition",lib_pCheckPosition}, diff --git a/src/p_user.c b/src/p_user.c index d2ca62ee9..8c1bc28b8 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -3948,6 +3948,16 @@ static void P_SetFollowerState(mobj_t *f, INT32 state) f->tics++; } +UINT16 K_GetEffectiveFollowerColor(UINT16 followercolor, UINT16 playercolor) +{ + if (followercolor < numskincolors) // bog standard + return followercolor; + if (followercolor == FOLLOWERCOLOR_OPPOSITE) // "Opposite" + return skincolors[playercolor].invcolor; + //if (followercolor == FOLLOWERCOLOR_MATCH) -- "Match" + return playercolor; +} + // //P_HandleFollower // @@ -4007,25 +4017,7 @@ static void P_HandleFollower(player_t *player) sz += FixedMul(player->mo->scale, sine)*P_MobjFlip(player->mo); } - // Set follower colour - switch (player->followercolor) - { - case FOLLOWERCOLOR_MATCH: // "Match" - color = player->skincolor; - break; - case FOLLOWERCOLOR_OPPOSITE: // "Opposite" - color = skincolors[player->skincolor].invcolor; - break; - default: - - color = player->followercolor; - if (!color || color > MAXSKINCOLORS+2) // Make sure this isn't garbage - color = player->skincolor; // "Match" as fallback. - - break; - } - - + color = K_GetEffectiveFollowerColor(player->followercolor, player->skincolor); if (!player->follower) // follower doesn't exist / isn't valid { diff --git a/src/r_data.h b/src/r_data.h index 1228f2420..be92c094e 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -41,8 +41,6 @@ extern INT16 *hicolormaps; // remap high colors to high colors.. extern CV_PossibleValue_t Color_cons_t[]; extern CV_PossibleValue_t Followercolor_cons_t[]; // follower colours table, not a duplicate because of the "Match" option. -#define FOLLOWERCOLOR_MATCH UINT16_MAX -#define FOLLOWERCOLOR_OPPOSITE (UINT16_MAX-1) // I/O, setting up the stuff. void R_InitTextureData(void); From 79831b46ccda7c4fa67ad154deb4a65c70f8c306 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Mon, 23 May 2022 23:48:23 +0200 Subject: [PATCH 283/379] Default restrictskinchange to yes, change conditions for changing skins --- src/d_netcmd.c | 59 +++++++++++++++++++----------------------------- src/k_menufunc.c | 26 +-------------------- 2 files changed, 24 insertions(+), 61 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 70faa1eb8..9d6fe0c0b 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -249,7 +249,7 @@ static consvar_t cv_fishcake = CVAR_INIT ("fishcake", "Off", CV_CALL|CV_NOSHOWHE #endif static consvar_t cv_dummyconsvar = CVAR_INIT ("dummyconsvar", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff, DummyConsvar_OnChange); -consvar_t cv_restrictskinchange = CVAR_INIT ("restrictskinchange", "No", CV_NETVAR|CV_CHEAT, CV_YesNo, NULL); +consvar_t cv_restrictskinchange = CVAR_INIT ("restrictskinchange", "Yes", CV_NETVAR|CV_CHEAT, CV_YesNo, NULL); consvar_t cv_allowteamchange = CVAR_INIT ("allowteamchange", "Yes", CV_NETVAR, CV_YesNo, NULL); static CV_PossibleValue_t ingamecap_cons_t[] = {{0, "MIN"}, {MAXPLAYERS-1, "MAX"}, {0, NULL}}; @@ -1315,6 +1315,8 @@ UINT8 CanChangeSkin(INT32 playernum) // Server has skin change restrictions. if (cv_restrictskinchange.value) { + UINT8 i; + // Can change skin during initial countdown. if (leveltime < starttime) return true; @@ -1322,9 +1324,23 @@ UINT8 CanChangeSkin(INT32 playernum) // Not in game, so you can change if (players[playernum].spectator || players[playernum].playerstate == PST_DEAD || players[playernum].playerstate == PST_REBORN) return true; - - return false; + + // Check for freeeplay + for (i = 0; i < MAXPLAYERS; i++) + { + if (i == consoleplayer) + continue; + if (playeringame[i] && !players[i].spectator && gamestate == GS_LEVEL) + return false; // Not freeplay! + } + + // if we've gotten here, then it's freeplay, and switching anytime is fair game. + return true; } + // if restrictskinchange is off and we're trying to change skins, don't allow changing skins while moving after the race has started. + else if (gamestate == GS_LEVEL && leveltime >= starttime) + return (!P_PlayerMoving(playernum)); + return true; } @@ -5523,24 +5539,8 @@ static void Skin_OnChange(void) return; } - if (CanChangeSkin(consoleplayer) && !P_PlayerMoving(consoleplayer)) - { - UINT8 i; - + if (CanChangeSkin(consoleplayer)) SendNameAndColor(0); - // check to see if there's anyone else at all - // even if we're playing splitscreen, if it ain't free play it spectates us if it can. - if (G_GametypeHasSpectators() && !players[consoleplayer].spectator) // Make sure we CAN spectate. - { - for (i = 0; i < MAXPLAYERS; i++) - { - if (i == consoleplayer) - continue; - if (playeringame[i] && !players[i].spectator && gamestate == GS_LEVEL) - COM_ImmedExecute("changeteam spectator"); - } - } - } else { CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n")); @@ -5558,13 +5558,8 @@ static void Skin2_OnChange(void) if (!Playing() || !splitscreen) return; // do whatever you want - if (CanChangeSkin(g_localplayers[1]) && !P_PlayerMoving(g_localplayers[1])) - { + if (CanChangeSkin(g_localplayers[1])) SendNameAndColor(1); - // With how we handle splitscreen, only check for gamestate here. - if (gamestate == GS_LEVEL && G_GametypeHasSpectators() && !players[g_localplayers[1]].spectator) - COM_ImmedExecute("changeteam2 spectator"); - } else { CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n")); @@ -5577,12 +5572,8 @@ static void Skin3_OnChange(void) if (!Playing() || splitscreen < 2) return; // do whatever you want - if (CanChangeSkin(g_localplayers[2]) && !P_PlayerMoving(g_localplayers[2])) - { + if (CanChangeSkin(g_localplayers[2])) SendNameAndColor(2); - if (gamestate == GS_LEVEL && G_GametypeHasSpectators() && !players[g_localplayers[2]].spectator) - COM_ImmedExecute("changeteam3 spectator"); - } else { CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n")); @@ -5595,12 +5586,8 @@ static void Skin4_OnChange(void) if (!Playing() || splitscreen < 3) return; // do whatever you want - if (CanChangeSkin(g_localplayers[3]) && !P_PlayerMoving(g_localplayers[3])) - { + if (CanChangeSkin(g_localplayers[3])) SendNameAndColor(3); - if (gamestate == GS_LEVEL && G_GametypeHasSpectators() && !players[g_localplayers[3]].spectator) - COM_ImmedExecute("changeteam4 spectator"); - } else { CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n")); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index b66550d1f..025020dcb 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3046,31 +3046,7 @@ void M_CharacterSelectTick(void) } else // In a game { - // In the midst of a game, - // 1: warn players that confirming will force-spectate them until next round - // ^ This doesn't apply in FREEPLAY - - // 2: Call the "skin" and "color" commands for all local players. - // This command will force change team to spectate under the proper circumstances. (see d_clisrv.c) - UINT8 j; - - // check to see if there's anyone else at all - if (G_GametypeHasSpectators()) // Make sure we CAN spectate. - { - for (j = 0; j < MAXPLAYERS; j++) - { - if (j == displayplayers[0]) - continue; - if (playeringame[j] && !players[consoleplayer].spectator) - { - // Warn the player! - M_StartMessage(M_GetText("Any player who has changed skin will\nautomatically spectate. Proceed?\n(Press A to confirm)\n"), FUNCPTRCAST(M_MPConfirmCharacterResponse), MM_YESNO); - return; - } - } - } - - // If we made it here then we're in freeplay or something and we can switch for free! + // 23/05/2022: Since there's already restrictskinchange, just allow this to happen regardless. M_MPConfirmCharacterSelection(); } } From 5f8eaaf41ba9c1714abadb78f8d1f59111f562ec Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Mon, 23 May 2022 23:53:25 +0200 Subject: [PATCH 284/379] Fix device being wrongly unset for P1 when beginning charsel with anything besides keyboard, oops --- src/k_menufunc.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 025020dcb..6de1d8efd 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2961,14 +2961,6 @@ static void M_MPConfirmCharacterSelection(void) M_ClearMenus(true); } -static void M_MPConfirmCharacterResponse(INT32 ch) -{ - if (ch == MA_YES) - M_MPConfirmCharacterSelection(); - - M_ClearMenus(true); -} - void M_CharacterSelectTick(void) { UINT8 i; @@ -3038,7 +3030,7 @@ void M_CharacterSelectTick(void) CV_StealthSetValue(&cv_splitplayers, setup_numplayers); // P1 is alone, set their old device just in case. - if (setup_numplayers < 2) + if (setup_numplayers < 2 && setup_player[0].ponedevice) CV_StealthSetValue(&cv_usejoystick[0], setup_player[0].ponedevice); M_SetupNextMenu(&PLAY_MainDef, false); From 348dab6fe2e0b9daebcec992ea039d08ddb82cfc Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Tue, 24 May 2022 00:18:40 +0200 Subject: [PATCH 285/379] Fix being unable to switch followers & colours from the menu without switching skins --- src/k_menufunc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 6de1d8efd..df8696a1d 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2953,6 +2953,10 @@ static void M_MPConfirmCharacterSelection(void) // finally, call the skin[x] console command. // This will call SendNameAndColor which will synch everything we sent here and apply the changes! + + // This is a hack to make sure we call Skin[x]_OnChange afterwards + CV_StealthSetValue(&cv_skin[i], -1); + strcpy(cmd, commandnames[i]); strcat(cmd, skins[setup_player[i].skin].name); COM_ImmedExecute(cmd); From d0a6fe18c4e3905ae2277afe8024e2a18328ef2d Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Tue, 24 May 2022 00:26:34 +0200 Subject: [PATCH 286/379] Fix cursor defaulting to profile values instead of last used values when changing characters mid-game --- src/k_menufunc.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index df8696a1d..236e5637f 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2175,6 +2175,34 @@ static void M_SetupProfileGridPos(setup_player_t *p) } } +static void M_SetupMidGameGridPos(setup_player_t *p, UINT8 num) +{ + INT32 i; + + // While we're here, read follower values. + p->followern = cv_follower[num].value; + p->followercolor = cv_followercolor[num].value; + + // Now position the grid for skin + for (i = 0; i < numskins; i++) + { + if (!(strcmp(cv_skin[num].zstring, skins[i].name))) + { + INT32 alt = 0; // Hey it's my character's name! + p->gridx = skins[i].kartspeed-1; + p->gridy = skins[i].kartweight-1; + + // Now this put our cursor on the good alt + while (setup_chargrid[p->gridx][p->gridy].skinlist[alt] != i) + alt++; + + p->clonenum = alt; + p->color = cv_playercolor[num].value; + return; // we're done here + } + } +} + void M_CharacterSelectInit(void) { @@ -2250,13 +2278,15 @@ void M_CharacterSelectInit(void) { setup_player[j].profilen = optionsmenu.profilen; PR_ApplyProfileLight(setup_player[j].profilen, 0); + M_SetupProfileGridPos(&setup_player[j]); } else // gamestate != GS_MENU, in that case, assume this is whatever profile we chose to play with. + { setup_player[j].profilen = cv_lastprofile[j].value; - // Don't reapply the profile here, it was already applied. + M_SetupMidGameGridPos(&setup_player[j], j); + } - - M_SetupProfileGridPos(&setup_player[j]); + // Don't reapply the profile here, it was already applied. setup_player[j].mdepth = CSSTEP_CHARS; } } From 256ec0517f0db5e2efea9cb610815b59b7d21f1f Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Tue, 24 May 2022 00:30:19 +0200 Subject: [PATCH 287/379] Fix video mode controls being reversed --- src/k_menufunc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 236e5637f..d0872b507 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -4403,7 +4403,7 @@ void M_HandleVideoModes(INT32 ch) else { - if (menucmd[pid].dpad_ud < 0) + if (menucmd[pid].dpad_ud > 0) { S_StartSound(NULL, sfx_menu1); if (++optionsmenu.vidm_selected >= optionsmenu.vidm_nummodes) @@ -4412,7 +4412,7 @@ void M_HandleVideoModes(INT32 ch) M_SetMenuDelay(pid); } - else if (menucmd[pid].dpad_ud > 0) + else if (menucmd[pid].dpad_ud < 0) { S_StartSound(NULL, sfx_menu1); if (--optionsmenu.vidm_selected < 0) From 9eb7ae3cff39e16686ef3c8696fe83bd5c59c518 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Tue, 24 May 2022 00:57:35 +0200 Subject: [PATCH 288/379] Fix ONLINE menus --- src/k_menudef.c | 5 ++++- src/k_menudraw.c | 14 ++++++------ src/k_menufunc.c | 57 ++++++++++++++++++------------------------------ 3 files changed, 32 insertions(+), 44 deletions(-) diff --git a/src/k_menudef.c b/src/k_menudef.c index cbeb1599e..0188f7b34 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -311,9 +311,12 @@ menuitem_t PLAY_MP_JoinIP[] = { //{IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, M_MPOptSelect, 0, 0}, - {IT_STRING | IT_CVAR | IT_CV_STRING, "IP: ", "Type the IPv4 address of the server then press enter to attempt connection.", + {IT_STRING | IT_CVAR | IT_CV_STRING, "IP: ", "Type the IPv4 address of the server.", NULL, {.cvar = &cv_dummyip}, 0, 0}, + {IT_STRING, "CONNECT ", "Attempt to connect to the server you entered the IP for.", + NULL, NULL, 0, 0}, + {IT_STRING | IT_SPACE, "LAST IPs JOINED:", "Kanade best waifu :)", NULL, {NULL}, 0, 0}, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index e443e88cc..da7d99a8f 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2149,12 +2149,12 @@ void M_DrawMPHost(void) void M_DrawMPJoinIP(void) { - patch_t *minibutt = W_CachePatchName("M_SBUTT", PU_CACHE); + //patch_t *minibutt = W_CachePatchName("M_SBUTT", PU_CACHE); // There is no such things as mini butts, only thick thighs to rest your head on. - patch_t *minigo = W_CachePatchName("M_SGO", PU_CACHE); + //patch_t *minigo = W_CachePatchName("M_SGO", PU_CACHE); patch_t *typebar = W_CachePatchName("M_TYPEB", PU_CACHE); - UINT8 *colormap = NULL; + //UINT8 *colormap = NULL; UINT8 *colormapc = NULL; INT32 xp = 73, yp = 133, i = 0; // Starting position for the text drawing. @@ -2185,7 +2185,7 @@ void M_DrawMPJoinIP(void) strcpy(str, "---"); // If that fails too then there's nothing! } - V_DrawString(xp, yp, V_ALLOWLOWERCASE | (i == itemOn ? highlightflags : 0), str); + V_DrawString(xp, yp, V_ALLOWLOWERCASE | ((i == itemOn || currentMenu->menuitems[i].status & IT_SPACE) ? highlightflags : 0), str); // Cvar specific handling switch (currentMenu->menuitems[i].status & IT_TYPE) @@ -2197,7 +2197,7 @@ void M_DrawMPJoinIP(void) { case IT_CV_STRING: - colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_MOSS, GTC_CACHE); + //colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_MOSS, GTC_CACHE); colormapc = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE); V_DrawFixedPatch((xp + 20)<string, 0), yp, '_' | 0x80, false); - // On this specific menu the only time we'll ever see this is for the connect by IP typefield. + /*// On this specific menu the only time we'll ever see this is for the connect by IP typefield. // Draw the small GO button here (and the text which is a separate graphic) V_DrawFixedPatch((xp + 20 + typebar->width -4)<width -4 + (minibutt->width/2))<width -4 + (minibutt->width/2))< copy paste! - else if (ctrldown && (ch == 'v' || ch == 'V')) - { - const char *paste = I_ClipboardPaste(); - UINT16 i; - for (i=0; i < strlen(paste); i++) - M_ChangeStringCvar(paste[i]); // We can afford to do this since we're currently on that cvar. - - return true; // Don't input the V obviously lol. - } - } - else if (currentMenu->numitems - itemOn <= NUMLOGIP && ch == KEY_ENTER) // On one of the last 3 options for IP rejoining + else if (currentMenu->numitems - itemOn <= NUMLOGIP && M_MenuConfirmPressed(pid)) // On one of the last 3 options for IP rejoining { UINT8 index = NUMLOGIP - (currentMenu->numitems - itemOn); + M_SetMenuDelay(pid); // Is there an address at this part of the table? if (joinedIPlist[index][0] && strlen(joinedIPlist[index][0])) @@ -3788,29 +3783,19 @@ boolean M_JoinIPInputs(INT32 ch) void M_MPRoomSelect(INT32 choice) { + const UINT8 pid = 0; + (void) choice; - switch (choice) + if (menucmd[pid].dpad_lr) { - - case KEY_LEFTARROW: - case KEY_RIGHTARROW: - { - - mpmenu.room = (!mpmenu.room) ? 1 : 0; - S_StartSound(NULL, sfx_s3k5b); - - break; - } - - case KEY_ESCAPE: - { - if (currentMenu->prevMenu) - M_SetupNextMenu(currentMenu->prevMenu, false); - else - M_ClearMenus(true); - break; - } - + mpmenu.room = (!mpmenu.room) ? 1 : 0; + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + } + else if (M_MenuBackPressed(pid)) + { + M_GoBack(0); + M_SetMenuDelay(pid); } } From 9b2fe3182d7ea427be679fa1c05c345169960e0e Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Tue, 24 May 2022 01:07:15 +0200 Subject: [PATCH 289/379] Can open chat again. Also fix missing braces in menudef... --- src/hu_stuff.c | 6 ++---- src/k_menudef.c | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 078257701..94b715298 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -1241,8 +1241,7 @@ boolean HU_Responder(event_t *ev) if (!chat_on) { // enter chat mode -#if 0 - if ((ev->data1 == gamecontrol[0][gc_talkkey][0] || ev->data1 == gamecontrol[0][gc_talkkey][1]) + if ((ev->data1 == gamecontrol[0][gc_talk][0] || ev->data1 == gamecontrol[0][gc_talk][1]) && netgame && !OLD_MUTE) // check for old chat mute, still let the players open the chat incase they want to scroll otherwise. { chat_on = true; @@ -1252,7 +1251,7 @@ boolean HU_Responder(event_t *ev) typelines = 1; return true; } - if ((ev->data1 == gamecontrol[0][gc_teamkey][0] || ev->data1 == gamecontrol[0][gc_teamkey][1]) + if ((ev->data1 == gamecontrol[0][gc_teamtalk][0] || ev->data1 == gamecontrol[0][gc_teamtalk][1]) && netgame && !OLD_MUTE) { chat_on = true; @@ -1262,7 +1261,6 @@ boolean HU_Responder(event_t *ev) typelines = 1; return true; } -#endif } else // if chat_on { diff --git a/src/k_menudef.c b/src/k_menudef.c index 0188f7b34..9e1ff9c96 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -315,7 +315,7 @@ menuitem_t PLAY_MP_JoinIP[] = NULL, {.cvar = &cv_dummyip}, 0, 0}, {IT_STRING, "CONNECT ", "Attempt to connect to the server you entered the IP for.", - NULL, NULL, 0, 0}, + NULL, {NULL}, 0, 0}, {IT_STRING | IT_SPACE, "LAST IPs JOINED:", "Kanade best waifu :)", NULL, {NULL}, 0, 0}, From ecf7edd55f9e9d748401d15888c7082cb3dcc25e Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Tue, 24 May 2022 01:22:47 +0200 Subject: [PATCH 290/379] don't let menu inputs slide through the voting screen --- src/y_inter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/y_inter.c b/src/y_inter.c index a759f4a44..935838201 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1510,7 +1510,7 @@ void Y_VoteTicker(void) if ((playeringame[p] && !players[p].spectator) && !voteclient.playerinfo[i].delay - && pickedvote == -1 && votes[p] == -1) + && pickedvote == -1 && votes[p] == -1 && menuactive == false) { if (G_PlayerInputDown(i, gc_up, 0)) { From 1033ddd928243b18994b540717e8be400bf4fe6a Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Tue, 24 May 2022 01:30:00 +0200 Subject: [PATCH 291/379] use gc_x instead of gc_b to cancel connection screen --- src/d_clisrv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 6e63c0b73..35220232d 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1487,7 +1487,7 @@ static void M_ConfirmConnect(event_t *ev) M_ClearMenus(true); } - else if (G_PlayerInputDown(0, gc_b, 1)) + else if (G_PlayerInputDown(0, gc_x, 1)) { cl_mode = CL_ABORTED; M_ClearMenus(true); @@ -1900,7 +1900,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic G_MapEventsToControls(&events[eventtail]); } - if (G_PlayerInputDown(0, gc_b, 1) || cl_mode == CL_ABORTED) + if (G_PlayerInputDown(0, gc_x, 1) || cl_mode == CL_ABORTED) { CONS_Printf(M_GetText("Network game synchronization aborted.\n")); // M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING); From 9364d0b479f917f766b04ccf7aa7555c8616a599 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Tue, 24 May 2022 01:50:00 +0200 Subject: [PATCH 292/379] better defaults in menus to make navigation easier when you just want to go fast --- src/d_netcmd.c | 14 +++++++++----- src/d_netcmd.h | 4 ++++ src/k_menu.h | 9 +++++++++ src/k_menufunc.c | 14 ++++++++++---- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 9d6fe0c0b..9554590b5 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -317,6 +317,9 @@ consvar_t cv_lastprofile[MAXSPLITSCREENPLAYERS] = { // You choose this profile when starting the game, this will also set lastprofile[0] consvar_t cv_currprofile = CVAR_INIT ("currprofile", "-1", CV_HIDDEN, lastprofile_cons_t, NULL); +// This one is used exclusively for the titlescreen +consvar_t cv_ttlprofilen = CVAR_INIT ("ttlprofilen", "0", CV_SAVE, lastprofile_cons_t, NULL); + // Cvar for using splitscreen with 1 device. consvar_t cv_splitdevice = CVAR_INIT ("splitdevice", "Off", CV_HIDDEN, CV_OnOff, NULL); @@ -889,6 +892,7 @@ void D_RegisterClientCommands(void) } CV_RegisterVar(&cv_currprofile); + CV_RegisterVar(&cv_ttlprofilen); CV_RegisterVar(&cv_splitdevice); // preferred number of players @@ -1316,7 +1320,7 @@ UINT8 CanChangeSkin(INT32 playernum) if (cv_restrictskinchange.value) { UINT8 i; - + // Can change skin during initial countdown. if (leveltime < starttime) return true; @@ -1324,23 +1328,23 @@ UINT8 CanChangeSkin(INT32 playernum) // Not in game, so you can change if (players[playernum].spectator || players[playernum].playerstate == PST_DEAD || players[playernum].playerstate == PST_REBORN) return true; - + // Check for freeeplay for (i = 0; i < MAXPLAYERS; i++) { if (i == consoleplayer) continue; - if (playeringame[i] && !players[i].spectator && gamestate == GS_LEVEL) + if (playeringame[i] && !players[i].spectator && gamestate == GS_LEVEL) return false; // Not freeplay! } - + // if we've gotten here, then it's freeplay, and switching anytime is fair game. return true; } // if restrictskinchange is off and we're trying to change skins, don't allow changing skins while moving after the race has started. else if (gamestate == GS_LEVEL && leveltime >= starttime) return (!P_PlayerMoving(playernum)); - + return true; } diff --git a/src/d_netcmd.h b/src/d_netcmd.h index e9c9b8f32..a352f9096 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -29,6 +29,10 @@ extern consvar_t cv_lastprofile[MAXSPLITSCREENPLAYERS]; // Used to know how to make the options menu behave among other things. extern consvar_t cv_currprofile; +// This is used to save the last profile you used on the title screen. +// that way you can mash n all... +extern consvar_t cv_ttlprofilen; + // CVar that allows starting as many splitscreens as you want with one device // Intended for use with testing extern consvar_t cv_splitdevice; diff --git a/src/k_menu.h b/src/k_menu.h index b8648541d..e69c8d907 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -192,6 +192,15 @@ extern menu_t PLAY_TimeAttackDef; extern menuitem_t PLAY_MP_OptSelect[]; extern menu_t PLAY_MP_OptSelectDef; +typedef enum +{ + mhost_sname = 0, + mhost_public, + mhost_maxp, + mhost_gametype, + mhost_go, +} mhost_e; + extern menuitem_t PLAY_MP_Host[]; extern menu_t PLAY_MP_HostDef; diff --git a/src/k_menufunc.c b/src/k_menufunc.c index e7966dfc0..0bb66ec94 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1065,7 +1065,7 @@ void M_StartControlPanel(void) { // we need to do this before setting ApplyProfile otherwise funky things are going to happen. currentMenu = &MAIN_ProfilesDef; - optionsmenu.profilen = cv_lastprofile[0].value; + optionsmenu.profilen = cv_ttlprofilen.value; // options don't need initializing here. PR_ApplyProfile(0, 0); // apply guest profile to player 0 by default. @@ -1074,6 +1074,8 @@ void M_StartControlPanel(void) // make sure we don't overstep that. if (optionsmenu.profilen > PR_GetNumProfiles()) optionsmenu.profilen = PR_GetNumProfiles(); + else if (optionsmenu.profilen < 0) + optionsmenu.profilen = 0; itemOn = 0; @@ -3092,7 +3094,6 @@ void M_SetupDifficultySelect(INT32 choice) // setup the difficulty menu and then remove choices depending on choice PLAY_RaceDifficultyDef.prevMenu = currentMenu; - M_SetupNextMenu(&PLAY_RaceDifficultyDef, false); PLAY_RaceDifficulty[0].status = IT_STRING|IT_CVAR; PLAY_RaceDifficulty[1].status = IT_DISABLED; @@ -3107,15 +3108,17 @@ void M_SetupDifficultySelect(INT32 choice) PLAY_RaceDifficulty[2].status = IT_STRING2|IT_CVAR; // CPUs on/off use string2 to signify not to use the normal gm font drawer PLAY_RaceDifficulty[3].status = IT_STRING2|IT_CVAR; // Encore on/off use string2 to signify not to use the normal gm font drawer PLAY_RaceDifficulty[5].status = IT_STRING|IT_CALL; // Level Select (Match Race) - itemOn = 5; // Select cup select by default. + PLAY_RaceDifficultyDef.lastOn = 5; // Select cup select by default. } else // GP { PLAY_RaceDifficulty[3].status = IT_STRING2|IT_CVAR; // Encore on/off use string2 to signify not to use the normal gm font drawer PLAY_RaceDifficulty[4].status = IT_STRING|IT_CALL; // Level Select (GP) - itemOn = 4; // Select cup select by default. + PLAY_RaceDifficultyDef.lastOn = 4; // Select cup select by default. } + + M_SetupNextMenu(&PLAY_RaceDifficultyDef, false); } // calls the above but changes the cvar we set @@ -3683,6 +3686,7 @@ void M_MPHostInit(INT32 choice) (void)choice; mpmenu.modewinextend[0][0] = 1; M_SetupNextMenu(&PLAY_MP_HostDef, true); + itemOn = mhost_go; } void M_MPSetupNetgameMapSelect(INT32 choice) @@ -4087,6 +4091,7 @@ static void M_FirstPickProfile(INT32 c) // Tell the game this is the last profile we picked. CV_StealthSetValue(&cv_lastprofile[0], optionsmenu.profilen); + CV_StealthSetValue(&cv_ttlprofilen, optionsmenu.profilen); // Save em! PR_SaveProfiles(); @@ -4972,6 +4977,7 @@ static void M_EraseProfileResponse(INT32 choice) if (optionsmenu.eraseprofilen == cv_currprofile.value) { CV_StealthSetValue(&cv_currprofile, -1); + CV_StealthSetValue(&cv_ttlprofilen, 0); F_StartIntro(); M_ClearMenus(true); } From 71372979d8ad5ac0136cc24d4e5d2ac15c13aac0 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 25 May 2022 19:28:17 +0100 Subject: [PATCH 293/379] Fix a few issues with input on the charsel screen. * Reset usejoystick if a second player enters and then backs out. * Make Split Device mode controlled by L and R (aka drift and item) pressed at the same time, not C by itself. * Make pressing the Split Device button combo when you're at max remove all extra keyboard players. * Fix Split Device being possible to activate on Profile char/follower setup. Also: * Fix a crash with exiting from top-level Profile setup. * Allow B (lookback) to be used as a back button on menus, too. * Clean up a bunch of extra whitespace. --- src/k_menufunc.c | 95 ++++++++++++++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 35 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 2026747c0..7761bf52a 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1315,7 +1315,7 @@ static boolean M_MenuConfirmPressed(UINT8 pid) // Returns true if we press the Cancel button static boolean M_MenuBackPressed(UINT8 pid) { - return M_MenuButtonPressed(pid, MBT_X); + return (M_MenuButtonPressed(pid, MBT_B) || M_MenuButtonPressed(pid, MBT_X)); } // Retrurns true if we press the tertiary option button (C) @@ -2503,6 +2503,11 @@ static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num) menucmd[i].buttonsHeld |= MBT_X; } + if (num > 0) + { + CV_StealthSetValue(&cv_usejoystick[num], -1); + } + return true; } else @@ -2691,7 +2696,6 @@ static void M_HandleCharRotate(setup_player_t *p, UINT8 num) static void M_HandleColorRotate(setup_player_t *p, UINT8 num) { - if (cv_splitdevice.value) num = 0; @@ -2761,7 +2765,6 @@ static void M_AnimateFollower(setup_player_t *p) static void M_HandleChooseFollower(setup_player_t *p, UINT8 num) { - if (cv_splitdevice.value) num = 0; @@ -2852,19 +2855,37 @@ static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num) // static void M_HandleSplitDevice(void) { - const UINT8 pid = 0; setup_player_t *p = &setup_player[setup_numplayers]; - if (M_MenuButtonPressed(pid, MBT_C)) - { - if (!cv_splitdevice.value) - M_StartMessage(M_GetText("Split device enabled.\nP1 can add additional players with [C].\nP1 must set all Players' parameters.\n\nIntended for use for multiplayer games\non the same device (Keyboard...)\nand testing purposes.\n\nPress any key"), NULL, MM_NOTHING); + if ((menucmd[pid].buttons & (MBT_L|MBT_R)) != (MBT_L|MBT_R)) + return; - CV_StealthSetValue(&cv_splitdevice, 1); - S_StartSound(NULL, sfx_s3k65); - p->mdepth = CSSTEP_PROFILE; // Ready the player setup. + if (!(menucmd[pid].buttonsHeld & MBT_L) == !(menucmd[pid].buttonsHeld & MBT_R)) + return; + + if (setup_numplayers > 1 && !cv_splitdevice.value) + return; + + if (setup_numplayers == 4) + { + S_StartSound(NULL, sfx_s3k85); + while (setup_numplayers > 1) + { + setup_numplayers--; + setup_player[setup_numplayers].mdepth = CSSTEP_NONE; + CV_StealthSetValue(&cv_usejoystick[setup_numplayers], -1); + } + CV_StealthSetValue(&cv_splitdevice, 0); + return; } + + if (!cv_splitdevice.value) + M_StartMessage(M_GetText("Split device enabled.\nP1 can add extra players with [L]+[R].\nP1 must set all Players' parameters.\n\nIntended for use for multiplayer games\non the same device (Keyboard...)\nand testing purposes.\n\nPress any key"), NULL, MM_NOTHING); + + CV_StealthSetValue(&cv_splitdevice, 1); + S_StartSound(NULL, sfx_s3k65); + p->mdepth = CSSTEP_PROFILE; // Ready the player setup. } boolean M_CharacterSelectHandler(INT32 choice) @@ -2880,17 +2901,19 @@ boolean M_CharacterSelectHandler(INT32 choice) if (p->delay == 0 && menucmd[i].delay == 0) { - - if (p->mdepth > CSSTEP_NONE && i == 0) - M_HandleSplitDevice(); - - // If splitdevice is true, only do the last non-ready setups. - if (cv_splitdevice.value) + if (!optionsmenu.profile) { - // Previous setup isn't ready, go there. - // In any case, do setup 0 first. - if (i > 0 && setup_player[i-1].mdepth < CSSTEP_READY) - continue; + if (p->mdepth > CSSTEP_NONE && i == 0) + M_HandleSplitDevice(); + + // If splitdevice is true, only do the last non-ready setups. + if (cv_splitdevice.value) + { + // Previous setup isn't ready, go there. + // In any case, do setup 0 first. + if (i > 0 && setup_player[i-1].mdepth < CSSTEP_READY) + continue; + } } switch (p->mdepth) @@ -4326,26 +4349,28 @@ boolean M_ProfileEditInputs(INT32 ch) // Handle some actions in profile editing void M_HandleProfileEdit(void) { - M_OptionsTick(); // Keep running that ticker normally. - // Always copy the profile name and player name in the profile. - - // Copy the first 6 chars for profile name - if (strlen(cv_dummyprofilename.string)) + if (optionmenus.profile) { - char *s; - // convert dummyprofilename to uppercase - strncpy(optionsmenu.profile->profilename, cv_dummyprofilename.string, PROFILENAMELEN); - s = optionsmenu.profile->profilename; - while (*s) + // Copy the first 6 chars for profile name + if (strlen(cv_dummyprofilename.string)) { - *s = toupper(*s); - s++; + char *s; + // convert dummyprofilename to uppercase + strncpy(optionsmenu.profile->profilename, cv_dummyprofilename.string, PROFILENAMELEN); + s = optionsmenu.profile->profilename; + while (*s) + { + *s = toupper(*s); + s++; + } } + + if (strlen(cv_dummyprofileplayername.string)) + strncpy(optionsmenu.profile->playername, cv_dummyprofileplayername.string, MAXPLAYERNAME); } - if (strlen(cv_dummyprofileplayername.string)) - strncpy(optionsmenu.profile->playername, cv_dummyprofileplayername.string, MAXPLAYERNAME); + M_OptionsTick(); // Has to be afterwards because this can unset optionsmenu.profile } // Confirm Profile edi via button. From 967a44969344983b78fd798eaaaf3c23157d1966 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 25 May 2022 19:29:08 +0100 Subject: [PATCH 294/379] optionmenus --> optionsmenu typo --- src/k_menufunc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 7761bf52a..2a48d158e 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -4350,7 +4350,7 @@ boolean M_ProfileEditInputs(INT32 ch) void M_HandleProfileEdit(void) { // Always copy the profile name and player name in the profile. - if (optionmenus.profile) + if (optionsmenu.profile) { // Copy the first 6 chars for profile name if (strlen(cv_dummyprofilename.string)) From 9de7f2393b115d9cafdb60ea3f4733bdd1dfe474 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 25 May 2022 20:13:14 +0100 Subject: [PATCH 295/379] Turning music back on while on the menu turns the menu music on, not the title music. --- src/s_sound.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/s_sound.c b/src/s_sound.c index 1214472d7..43545cd6b 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -2497,9 +2497,9 @@ void GameDigiMusic_OnChange(void) { P_RestoreMusic(&players[consoleplayer]); } - else if (S_MusicExists("_title")) + else if (S_MusicExists("menu")) { - S_ChangeMusicInternal("_title", looptitle); + S_ChangeMusicInternal("menu", looptitle); } } else From d118b69c43d2a82bb257cbba935bf8e83d3a1875 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 25 May 2022 20:29:45 +0100 Subject: [PATCH 296/379] Profile name appears over PLAYER on charsel screen when profile is set. --- src/k_menudraw.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 7bfbe0f3e..e3ee37295 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1183,7 +1183,15 @@ static void M_DrawCharSelectPreview(UINT8 num) V_DrawScaledPatch(x+9, y+2, 0, W_CachePatchName("FILEBACK", PU_CACHE)); V_DrawScaledPatch(x, y+2, 0, W_CachePatchName(va("CHARSEL%c", letter), PU_CACHE)); - V_DrawFileString(x+16, y+2, 0, "PLAYER"); + if (p->mdepth > CSSTEP_PROFILE) + { + profile_t *pr = PR_GetProfile(p->profilen); + V_DrawCenteredFileString(x+16+18, y+2, 0, pr->profilename); + } + else + { + V_DrawFileString(x+16, y+2, 0, "PLAYER"); + } // Profile selection if (p->mdepth == CSSTEP_PROFILE) From a1a38ba0220dbc1a62d3193883131453a2c12b6d Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Thu, 26 May 2022 13:32:22 +0200 Subject: [PATCH 297/379] Move SKINNAMESIZE to doomdef.h to prevent issues --- src/doomdef.h | 1 + src/k_follower.h | 1 - src/r_skins.h | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index fe29374f3..78208a25c 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -208,6 +208,7 @@ extern char logfilename[1024]; #define MAXGAMEPADS (MAXSPLITSCREENPLAYERS * 2) // Number of gamepads we'll be allowing #define MAXSKINS UINT8_MAX +#define SKINNAMESIZE 16 // Moved from r_skins.h as including that particular header causes issues later down the line. #define COLORRAMPSIZE 16 #define MAXCOLORNAME 32 diff --git a/src/k_follower.h b/src/k_follower.h index 363b272bc..5f457fe90 100644 --- a/src/k_follower.h +++ b/src/k_follower.h @@ -15,7 +15,6 @@ #include "doomdef.h" #include "doomstat.h" -#include "r_skins.h" #define FOLLOWERCOLOR_MATCH UINT16_MAX #define FOLLOWERCOLOR_OPPOSITE (UINT16_MAX-1) diff --git a/src/r_skins.h b/src/r_skins.h index cffd54da8..ded45a71f 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -22,7 +22,6 @@ #include "r_defs.h" // spritedef_t /// Defaults -#define SKINNAMESIZE 16 #define SKINRIVALS 3 // should be all lowercase!! S_SKIN processing does a strlwr #define DEFAULTSKIN "eggman" From 92c81baf584c928e6322fffe27f8a3a32db6178a Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Thu, 26 May 2022 13:34:59 +0200 Subject: [PATCH 298/379] Allow exiting pause menu by pressing start again --- src/k_menufunc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 2a48d158e..ea44d8b5c 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -5278,9 +5278,10 @@ boolean M_PauseInputs(INT32 ch) return true; } - else if (M_MenuBackPressed(pid)) + else if (M_MenuBackPressed(pid) || M_MenuButtonPressed(pid, MBT_START)) { M_QuitPauseMenu(-1); + M_SetMenuDelay(pid); return true; } return false; From 3b4bb8aea14464bb8109cea0f5002d7d2da1a460 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 11 Jun 2022 16:49:36 -0700 Subject: [PATCH 299/379] Fix always evaluates true warning --- src/k_menudraw.c | 4 ++-- src/k_menufunc.c | 2 +- src/m_misc.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index e3ee37295..4bb1107b0 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2185,9 +2185,9 @@ void M_DrawMPJoinIP(void) if (currentMenu->numitems - i <= NUMLOGIP) { UINT8 index = NUMLOGIP - (currentMenu->numitems - i); - if (joinedIPlist[index][1] && strlen(joinedIPlist[index][1])) // Try drawing server name + if (strlen(joinedIPlist[index][1])) // Try drawing server name strcpy(str, joinedIPlist[index][1]); - else if (joinedIPlist[index][0] && strlen(joinedIPlist[index][0])) // If that fails, get the address + else if (strlen(joinedIPlist[index][0])) // If that fails, get the address strcpy(str, joinedIPlist[index][0]); else strcpy(str, "---"); // If that fails too then there's nothing! diff --git a/src/k_menufunc.c b/src/k_menufunc.c index ea44d8b5c..32c5220ad 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3796,7 +3796,7 @@ boolean M_JoinIPInputs(INT32 ch) M_SetMenuDelay(pid); // Is there an address at this part of the table? - if (joinedIPlist[index][0] && strlen(joinedIPlist[index][0])) + if (strlen(joinedIPlist[index][0])) M_JoinIP(joinedIPlist[index][0]); else S_StartSound(NULL, sfx_lose); diff --git a/src/m_misc.c b/src/m_misc.c index 1760a42db..30fe5b525 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -504,7 +504,7 @@ void M_SaveJoinedIPs(void) UINT8 i; char *filepath; - if (!joinedIPlist[0][0] || !strlen(joinedIPlist[0][0])) + if (!strlen(joinedIPlist[0][0])) return; // Don't bother, there's nothing to save. // append srb2home to beginning of filename @@ -521,7 +521,7 @@ void M_SaveJoinedIPs(void) for (i=0; i < NUMLOGIP; i++) { - if (joinedIPlist[i][0] && strlen(joinedIPlist[i][0])) + if (strlen(joinedIPlist[i][0])) { char savestring[MAXSTRINGLENGTH]; strcpy(savestring, joinedIPlist[i][0]); From f0e5c144ecb74d0988a40e0ea1718df7558cd7e8 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 28 May 2022 00:16:25 +0200 Subject: [PATCH 300/379] don't load newmenus.pk3 --- src/d_main.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index a0185f9f0..cee127eb1 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1154,8 +1154,6 @@ static void IdentifyVersion(void) D_AddFile(startupiwads, va(pandf,srb2waddir,"followers.pk3")); #ifdef USE_PATCH_FILE D_AddFile(startupiwads, va(pandf,srb2waddir,PATCHNAME)); - // SPECIFIC HACK TO NEW-MENUS SO THAT MY DUMBASS STOPS FORGETTING TO ADD THE FILE (rip :youfuckedup:) - D_AddFile(startupiwads, va(pandf,srb2waddir,"newmenus.pk3")); #endif //// #undef TEXTURESNAME @@ -1417,8 +1415,6 @@ void D_SRB2Main(void) mainwads++; // followers.pk3 #ifdef USE_PATCH_FILE mainwads++; // patch.pk3 - // TODO: DON'T FORGET TO REMOVE THIS ONCE WE DON'T NEED IT ANYMORE. - mainwads++; // newmenus.pk3 #endif #endif //ifndef DEVELOP From d5e9283c2e099e6851acb91b6e3d9eae5dc1cced Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 28 May 2022 13:12:42 +0200 Subject: [PATCH 301/379] Allow selecting profile with no changes to speed up charsel process --- src/hu_stuff.c | 7 ++--- src/hu_stuff.h | 2 +- src/k_menu.h | 3 ++ src/k_menudraw.c | 20 ++++++++++++ src/k_menufunc.c | 81 ++++++++++++++++++++++++++++++++++++++---------- 5 files changed, 92 insertions(+), 21 deletions(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 94b715298..da3426844 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -243,6 +243,9 @@ void HU_Init(void) PR ("TNYFN"); REG; + PR ("FILEF"); + REG; + ADIM (LT); PR ("LTFNT"); REG; @@ -287,10 +290,6 @@ void HU_Init(void) PR ("GAMEM"); REG; - ADIM (AZ); - PR ("FILEF"); - REG; - ADIM (LT); PR ("THIFN"); REG; diff --git a/src/hu_stuff.h b/src/hu_stuff.h index 1fc5611d3..cc9959467 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -54,6 +54,7 @@ enum { X (HU), X (TINY), + X (FILE), X (LT), X (CRED), @@ -68,7 +69,6 @@ enum X (KART), X (GM), - X (FILE), X (LSHI), X (LSLOW), }; diff --git a/src/k_menu.h b/src/k_menu.h index e69c8d907..3cbea0d83 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -519,6 +519,7 @@ typedef enum { CSSTEP_NONE = 0, CSSTEP_PROFILE, + CSSTEP_ASKCHANGES, CSSTEP_CHARS, CSSTEP_ALTS, CSSTEP_COLORS, @@ -543,6 +544,8 @@ typedef struct setup_player_s // We can allow them to retain the device with no consequence as when P1 is alone, they have exclusive keyboard fallback options. UINT8 ponedevice; + UINT8 changeselect; + INT32 followern; UINT16 followercolor; tic_t follower_tics; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 4bb1107b0..3bc4e523f 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1227,6 +1227,26 @@ static void M_DrawCharSelectPreview(UINT8 num) } } + // "Changes?" + else if (p->mdepth == CSSTEP_ASKCHANGES) + { + UINT8 i; + char choices[][4] = {"NO", "YES"}; + INT32 xpos = x+8; + INT32 ypos = y+38; + + V_DrawFileString(xpos, ypos, 0, "CHANGES?"); + + for (i = 0; i < 2; i++) + { + UINT8 cy = ypos+16 + (i*10); + + if (p->changeselect == i) + V_DrawScaledPatch(xpos+4, cy, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); + + V_DrawString(xpos+20, cy, p->changeselect == i ? highlightflags : 0, choices[i]); + } + } } static void M_DrawCharSelectExplosions(void) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 32c5220ad..386d06e88 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2351,6 +2351,21 @@ static void M_SetupReadyExplosions(setup_player_t *p) } } + +// Gets the selected follower's state for a given setup player. +static void M_GetFollowerState(setup_player_t *p) +{ + + p->follower_state = &states[followers[p->followern].followstate]; + + if (p->follower_state->frame & FF_ANIMATE) + p->follower_tics = p->follower_state->var2; // support for FF_ANIMATE + else + p->follower_tics = p->follower_state->tics; + + p->follower_frame = p->follower_state->frame & FF_FRAMEMASK; +} + static boolean M_DeviceAvailable(INT32 deviceID, UINT8 numPlayers) { INT32 i; @@ -2523,7 +2538,8 @@ static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num) PR_ApplyProfile(p->profilen, realnum); // Otherwise P1 would inherit the last player's profile in splitdevice and that's not what we want... M_SetupProfileGridPos(p); - p->mdepth = CSSTEP_CHARS; + p->changeselect = 0; + p->mdepth = CSSTEP_ASKCHANGES; S_StartSound(NULL, sfx_s3k63); } @@ -2531,6 +2547,51 @@ static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num) } + +static void M_HandleCharAskChange(setup_player_t *p, UINT8 num) +{ + + if (cv_splitdevice.value) + num = 0; + + // there's only 2 options so lol + if (menucmd[num].dpad_ud != 0) + { + p->changeselect = (p->changeselect == 0) ? 1 : 0; + + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(num); + } + else if (M_MenuBackPressed(num)) + { + p->changeselect = 0; + p->mdepth = CSSTEP_PROFILE; + + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(num); + } + else if (M_MenuConfirmPressed(num)) + { + // no changes + if (!p->changeselect) + { + M_GetFollowerState(p); + p->mdepth = CSSTEP_READY; + p->delay = TICRATE; + + S_StartSound(NULL, sfx_s3k4e); + M_SetupReadyExplosions(p); + } + + // changes + else + p->mdepth = CSSTEP_CHARS; + + M_SetMenuDelay(num); + S_StartSound(NULL, sfx_s3k63); + } +} + static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) { INT32 i; @@ -2639,20 +2700,6 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) return false; } -// Gets the selected follower's state for a given setup player. -static void M_GetFollowerState(setup_player_t *p) -{ - - p->follower_state = &states[followers[p->followern].followstate]; - - if (p->follower_state->frame & FF_ANIMATE) - p->follower_tics = p->follower_state->var2; // support for FF_ANIMATE - else - p->follower_tics = p->follower_state->tics; - - p->follower_frame = p->follower_state->frame & FF_FRAMEMASK; -} - static void M_HandleCharRotate(setup_player_t *p, UINT8 num) { @@ -2800,7 +2847,6 @@ static void M_HandleChooseFollower(setup_player_t *p, UINT8 num) p->delay = TICRATE; M_SetupReadyExplosions(p); S_StartSound(NULL, sfx_s3k4e); - M_SetMenuDelay(num); } S_StartSound(NULL, sfx_s3k63); @@ -2926,6 +2972,9 @@ boolean M_CharacterSelectHandler(INT32 choice) case CSSTEP_PROFILE: playersChanged = M_HandleCSelectProfile(p, i); break; + case CSSTEP_ASKCHANGES: + M_HandleCharAskChange(p, i); + break; case CSSTEP_CHARS: // Character Select grid M_HandleCharacterGrid(p, i); break; From 10e23827c005be1d2b205acd6c690d52511d5ab8 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Sat, 28 May 2022 13:22:02 +0200 Subject: [PATCH 302/379] Fix being unable to go back to profile select unless you were the last active player --- src/k_menufunc.c | 38 +++++++++----------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 386d06e88..2f040d1a5 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2594,8 +2594,6 @@ static void M_HandleCharAskChange(setup_player_t *p, UINT8 num) static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) { - INT32 i; - UINT8 numclones; if (cv_splitdevice.value) @@ -2662,38 +2660,20 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) } else if (M_MenuBackPressed(num)) { - if (num == setup_numplayers-1) + // for profiles / gameplay, exit out of the menu instantly, + // we don't want to go to the input detection menu. + if (optionsmenu.profile || gamestate != GS_MENU) { - // for profiles / gameplay, exit out of the menu instantly, - // we don't want to go to the input detection menu. - if (optionsmenu.profile || gamestate != GS_MENU) - { - memset(setup_player, 0, sizeof(setup_player)); // Reset setup_player otherwise it does some VERY funky things. - M_SetMenuDelay(0); - M_GoBack(0); - return true; - } - else // for the actual player select, go back to device detection. - { - p->mdepth = CSSTEP_PROFILE; - S_StartSound(NULL, sfx_s3k5b); - } - - // Prevent quick presses for multiple players - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) - { - setup_player[i].delay = MENUDELAYTIME; - M_SetMenuDelay(i); - menucmd[i].buttonsHeld |= MBT_X; - } - + memset(setup_player, 0, sizeof(setup_player)); // Reset setup_player otherwise it does some VERY funky things. + M_SetMenuDelay(0); + M_GoBack(0); return true; } - else + else // in main menu { - S_StartSound(NULL, sfx_s3kb2); + p->mdepth = CSSTEP_PROFILE; + S_StartSound(NULL, sfx_s3k5b); } - M_SetMenuDelay(num); } From 3e904e23aaa35c7d793a21a51d1c73995e2ec8c0 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 22 Jul 2022 12:08:24 +0200 Subject: [PATCH 303/379] Make menus much faster --- src/k_menu.h | 4 ++-- src/k_menudef.c | 54 ++++++++++++++++++++++++------------------------ src/k_menudraw.c | 32 ++++++++++++++-------------- src/k_menufunc.c | 17 +++++---------- 4 files changed, 50 insertions(+), 57 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 3cbea0d83..fe104bac5 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -966,7 +966,7 @@ void M_DrawAddons(void); source,\ 0, 0,\ 0, 0, \ - 1, 10,\ + 1, 5,\ M_DrawKartGamemodeMenu,\ NULL,\ NULL,\ @@ -982,7 +982,7 @@ void M_DrawAddons(void); source,\ 0, 0,\ 0, 0, \ - 1, 10,\ + 1, 5,\ M_DrawImageDef,\ NULL,\ NULL,\ diff --git a/src/k_menudef.c b/src/k_menudef.c index 9e1ff9c96..e732b5e75 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -153,7 +153,7 @@ menu_t PLAY_RaceDifficultyDef = { PLAY_RaceDifficulty, 0, 0, 0, 0, - 1, 10, + 1, 5, M_DrawRaceDifficulty, NULL, NULL, @@ -174,7 +174,7 @@ menu_t PLAY_CupSelectDef = { PLAY_CupSelect, 0, 0, 0, 0, - 2, 10, + 2, 5, M_DrawCupSelect, M_CupSelectTick, NULL, @@ -194,7 +194,7 @@ menu_t PLAY_LevelSelectDef = { PLAY_LevelSelect, 0, 0, 0, 0, - 2, 10, + 2, 5, M_DrawLevelSelect, M_LevelSelectTick, NULL, @@ -217,7 +217,7 @@ menu_t PLAY_TimeAttackDef = { PLAY_TimeAttack, 0, 0, 0, 0, - 2, 10, + 2, 5, M_DrawTimeAttack, NULL, NULL, @@ -404,7 +404,7 @@ menu_t OPTIONS_MainDef = { OPTIONS_Main, 0, 0, SKINCOLOR_SLATE, 0, - 2, 10, + 2, 5, M_DrawOptions, M_OptionsTick, NULL, @@ -426,7 +426,7 @@ menu_t OPTIONS_ProfilesDef = { OPTIONS_Profiles, 32, 80, SKINCOLOR_ULTRAMARINE, 0, - 2, 10, + 2, 5, M_DrawProfileSelect, M_OptionsTick, NULL, @@ -447,7 +447,7 @@ menu_t MAIN_ProfilesDef = { MAIN_Profiles, 32, 80, SKINCOLOR_ULTRAMARINE, 0, - 2, 10, + 2, 5, M_DrawProfileSelect, M_OptionsTick, NULL, @@ -481,7 +481,7 @@ menu_t OPTIONS_EditProfileDef = { OPTIONS_EditProfile, 32, 80, SKINCOLOR_ULTRAMARINE, 0, - 2, 10, + 2, 5, M_DrawEditProfile, M_HandleProfileEdit, NULL, @@ -583,7 +583,7 @@ menu_t OPTIONS_ProfileControlsDef = { OPTIONS_ProfileControls, 32, 80, SKINCOLOR_ULTRAMARINE, 0, - 3, 10, + 3, 5, M_DrawProfileControls, M_HandleProfileControls, NULL, @@ -645,7 +645,7 @@ menu_t OPTIONS_VideoDef = { OPTIONS_Video, 32, 80, SKINCOLOR_PLAGUE, 0, - 2, 10, + 2, 5, M_DrawGenericOptions, M_OptionsTick, NULL, @@ -667,7 +667,7 @@ menu_t OPTIONS_VideoModesDef = { OPTIONS_VideoModes, 48, 80, SKINCOLOR_PLAGUE, 0, - 2, 10, + 2, 5, M_DrawVideoModes, M_OptionsTick, NULL, @@ -726,7 +726,7 @@ menu_t OPTIONS_VideoOGLDef = { OPTIONS_VideoOGL, 32, 80, SKINCOLOR_PLAGUE, 0, - 2, 10, + 2, 5, M_DrawGenericOptions, M_OptionsTick, NULL, @@ -790,7 +790,7 @@ menu_t OPTIONS_SoundDef = { OPTIONS_Sound, 48, 80, SKINCOLOR_THUNDER, 0, - 2, 10, + 2, 5, M_DrawGenericOptions, M_OptionsTick, NULL, @@ -846,7 +846,7 @@ menu_t OPTIONS_HUDDef = { OPTIONS_HUD, 48, 80, SKINCOLOR_SUNSLAM, 0, - 2, 10, + 2, 5, M_DrawGenericOptions, M_OptionsTick, NULL, @@ -896,7 +896,7 @@ menu_t OPTIONS_HUDOnlineDef = { OPTIONS_HUDOnline, 48, 80, SKINCOLOR_SUNSLAM, 0, - 2, 10, + 2, 5, M_DrawGenericOptions, M_OptionsTick, NULL, @@ -950,7 +950,7 @@ menu_t OPTIONS_GameplayDef = { OPTIONS_Gameplay, 48, 80, SKINCOLOR_SCARLET, 0, - 2, 10, + 2, 5, M_DrawGenericOptions, M_OptionsTick, NULL, @@ -1009,7 +1009,7 @@ menu_t OPTIONS_GameplayItemsDef = { OPTIONS_GameplayItems, 14, 40, SKINCOLOR_SCARLET, 0, - 2, 10, + 2, 5, M_DrawItemToggles, M_OptionsTick, NULL, @@ -1074,7 +1074,7 @@ menu_t OPTIONS_ServerDef = { OPTIONS_Server, 48, 70, // This menu here is slightly higher because there's a lot of options... SKINCOLOR_VIOLET, 0, - 2, 10, + 2, 5, M_DrawGenericOptions, M_OptionsTick, NULL, @@ -1133,7 +1133,7 @@ menu_t OPTIONS_ServerAdvancedDef = { OPTIONS_ServerAdvanced, 48, 70, // This menu here is slightly higher because there's a lot of options... SKINCOLOR_VIOLET, 0, - 2, 10, + 2, 5, M_DrawGenericOptions, M_OptionsTick, NULL, @@ -1175,7 +1175,7 @@ menu_t OPTIONS_DataDef = { OPTIONS_Data, 48, 80, SKINCOLOR_BLUEBERRY, 0, - 2, 10, + 2, 5, M_DrawGenericOptions, M_OptionsTick, NULL, @@ -1222,7 +1222,7 @@ menu_t OPTIONS_DataAddonDef = { OPTIONS_DataAddon, 48, 80, SKINCOLOR_BLUEBERRY, 0, - 2, 10, + 2, 5, M_DrawGenericOptions, M_OptionsTick, NULL, @@ -1263,7 +1263,7 @@ menu_t OPTIONS_DataScreenshotDef = { OPTIONS_DataScreenshot, 48, 80, SKINCOLOR_BLUEBERRY, 0, - 2, 10, + 2, 5, M_DrawGenericOptions, M_OptionsTick, NULL, @@ -1287,7 +1287,7 @@ menu_t OPTIONS_DataReplayDef = { OPTIONS_DataReplay, 48, 80, SKINCOLOR_BLUEBERRY, 0, - 2, 10, + 2, 5, M_DrawGenericOptions, M_OptionsTick, NULL, @@ -1325,7 +1325,7 @@ menu_t OPTIONS_DataDiscordDef = { OPTIONS_DataDiscord, 48, 80, SKINCOLOR_BLUEBERRY, 0, - 2, 10, + 2, 5, M_DrawGenericOptions, M_OptionsTick, NULL, @@ -1365,7 +1365,7 @@ menu_t OPTIONS_DataEraseDef = { OPTIONS_DataErase, 48, 80, SKINCOLOR_BLUEBERRY, 0, - 2, 10, + 2, 5, M_DrawGenericOptions, M_OptionsTick, NULL, @@ -1385,7 +1385,7 @@ menu_t OPTIONS_DataProfileEraseDef = { OPTIONS_DataProfileErase, 48, 80, SKINCOLOR_BLUEBERRY, 0, - 2, 10, + 2, 5, M_DrawProfileErase, M_OptionsTick, NULL, @@ -1418,7 +1418,7 @@ menu_t EXTRAS_MainDef = { EXTRAS_Main, 0, 0, 0, 0, - 2, 10, + 2, 5, M_DrawExtras, M_ExtrasTick, NULL, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 3bc4e523f..ad9e92738 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -659,7 +659,7 @@ void M_DrawKartGamemodeMenu(void) if (menutransition.tics) { - x += 24 * menutransition.tics; + x += 48 * menutransition.tics; } for (i = 0; i < currentMenu->numitems; i++) @@ -671,7 +671,7 @@ void M_DrawKartGamemodeMenu(void) if (menutransition.tics) { - x += 24 * menutransition.tics; + x += 48 * menutransition.tics; } } @@ -1560,11 +1560,11 @@ void M_DrawRaceDifficulty(void) M_DrawMenuTooltips(); // Draw the box for difficulty... - V_DrawFixedPatch((111 + 24*menutransition.tics)*FRACUNIT, 33*FRACUNIT, FRACUNIT, 0, box, NULL); + V_DrawFixedPatch((111 + 48*menutransition.tics)*FRACUNIT, 33*FRACUNIT, FRACUNIT, 0, box, NULL); if (menutransition.tics) { - x += 24 * menutransition.tics; + x += 48 * menutransition.tics; } for (i = 0; i < currentMenu->numitems; i++) @@ -1584,7 +1584,7 @@ void M_DrawRaceDifficulty(void) if (menutransition.tics) { - x += 24 * menutransition.tics; + x += 48 * menutransition.tics; } } @@ -1597,12 +1597,12 @@ void M_DrawRaceDifficulty(void) INT32 f = (i == itemOn) ? highlightflags : 0; - V_DrawString(140 + 24*menutransition.tics, y, f, currentMenu->menuitems[i].text); + V_DrawString(140 + 48*menutransition.tics, y, f, currentMenu->menuitems[i].text); if (currentMenu->menuitems[i].status & IT_CVAR) { // implicitely we'll only take care of normal cvars - INT32 cx = 260 + 24*menutransition.tics; + INT32 cx = 260 + 48*menutransition.tics; consvar_t *cv = currentMenu->menuitems[i].itemaction.cvar; V_DrawCenteredString(cx, y, f, cv->string); @@ -1640,8 +1640,8 @@ void M_DrawRaceDifficulty(void) if (currentMenu->menuitems[i].status & IT_CVAR) { - INT32 fx = (x - 24*menutransition.tics); - INT32 centx = fx + (320-fx)/2 + (menutransition.tics*24); // undo the menutransition movement to redo it here otherwise the text won't move at the same speed lole. + INT32 fx = (x - 48*menutransition.tics); + INT32 centx = fx + (320-fx)/2 + (menutransition.tics*48); // undo the menutransition movement to redo it here otherwise the text won't move at the same speed lole. // implicitely we'll only take care of normal consvars consvar_t *cv = currentMenu->menuitems[i].itemaction.cvar; @@ -1783,7 +1783,7 @@ void M_DrawCupSelect(void) patch = W_CachePatchName("CUPMON1A", PU_CACHE); x = 14 + (i*42); - y = 20 + (j*44) - (15*menutransition.tics); + y = 20 + (j*44) - (30*menutransition.tics); V_DrawScaledPatch(x, y, 0, patch); @@ -1801,12 +1801,12 @@ void M_DrawCupSelect(void) } V_DrawScaledPatch(14 + (cupgrid.x*42) - 4, - 20 + (cupgrid.y*44) - 1 - (12*menutransition.tics), + 20 + (cupgrid.y*44) - 1 - (24*menutransition.tics), 0, W_CachePatchName("CUPCURS", PU_CACHE) ); - M_DrawCupPreview(146 + (12*menutransition.tics), cup); - M_DrawCupTitle(120 - (12*menutransition.tics), cup); + M_DrawCupPreview(146 + (24*menutransition.tics), cup); + M_DrawCupTitle(120 - (24*menutransition.tics), cup); } static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map) @@ -1942,7 +1942,7 @@ void M_DrawLevelSelect(void) INT16 i; INT16 start = M_GetFirstLevelInList(levellist.newgametype); INT16 map = start; - INT16 t = (32*menutransition.tics), tay = 0; + INT16 t = (64*menutransition.tics), tay = 0; INT16 y = 80 - (12 * levellist.y); boolean tatransition = ((menutransition.startmenu == &PLAY_TimeAttackDef || menutransition.endmenu == &PLAY_TimeAttackDef) && menutransition.tics); @@ -1983,7 +1983,7 @@ void M_DrawLevelSelect(void) void M_DrawTimeAttack(void) { INT16 map = levellist.choosemap; - INT16 t = (24*menutransition.tics); + INT16 t = (48*menutransition.tics); INT16 leftedge = 149+t+16; INT16 rightedge = 149+t+155; INT16 opty = 152; @@ -2561,7 +2561,7 @@ void M_DrawProfileSelect(void) INT32 i; const INT32 maxp = PR_GetNumProfiles(); INT32 x = 160 - optionsmenu.profilen*(128 + 128/8) + optionsmenu.offset; - INT32 y = 35 + menutransition.tics*16; + INT32 y = 35 + menutransition.tics*32; M_DrawOptionsCogs(); M_DrawMenuTooltips(); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 2f040d1a5..a20d88492 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3656,20 +3656,13 @@ void M_LevelSelectHandler(INT32 choice) void M_LevelSelectTick(void) { - UINT8 times = 1 + (abs(levellist.dest - levellist.y) / 21); - while (times) // increase speed as you're farther away - { - if (levellist.y > levellist.dest) - levellist.y--; - else if (levellist.y < levellist.dest) - levellist.y++; + INT16 dist = levellist.dest - levellist.y; - if (levellist.y == levellist.dest) - break; - - times--; - } + if (abs(dist) == 1) // cheating to avoid off by 1 errors with divisions. + levellist.y = levellist.dest; + else + levellist.y += dist/2; } struct mpmenu_s mpmenu; From fc4b0d87767027e4675db3863071bbeea462b4ed Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 22 Jul 2022 13:57:48 +0200 Subject: [PATCH 304/379] Add page system on csel when alone to make alts easier to see --- src/k_menu.h | 4 ++++ src/k_menudraw.c | 25 ++++++++++++++++++------- src/k_menufunc.c | 44 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 63 insertions(+), 10 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index fe104bac5..772f159ab 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -559,6 +559,10 @@ extern setup_player_t setup_player[MAXSPLITSCREENPLAYERS]; extern UINT8 setup_numplayers; extern tic_t setup_animcounter; +// for charsel pages. +extern UINT8 setup_page; +extern UINT8 setup_maxpage; + #define CSROTATETICS 6 // The selection spawns 3 explosions in 4 directions, and there's 4 players -- 3 * 4 * 4 = 48 diff --git a/src/k_menudraw.c b/src/k_menudraw.c index ad9e92738..98e5140fc 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1123,8 +1123,13 @@ static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, INT32 addflags, static void M_DrawCharSelectSprite(UINT8 num, INT16 x, INT16 y) { setup_player_t *p = &setup_player[num]; + UINT8 cnum = p->clonenum; - SINT8 skin = setup_chargrid[p->gridx][p->gridy].skinlist[p->clonenum]; + // for p1 alone don't try to preview things on pages that don't exist lol. + if (p->mdepth == CSSTEP_CHARS && setup_numplayers == 1) + cnum = setup_page; + + INT16 skin = setup_chargrid[p->gridx][p->gridy].skinlist[cnum]; UINT8 color = p->color; UINT8 *colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); INT32 flags = 0; @@ -1449,6 +1454,11 @@ void M_DrawCharacterSelect(void) SINT8 skin; INT32 basex = optionsmenu.profile != NULL ? 64 : 0; + // Draw page num. + // @TODO: make it fancier than the default string lol. + if (setup_numplayers < 2) + V_DrawCenteredString(160, 1, 0, va("%d/%d", setup_page+1, setup_maxpage+1)); + if (setup_numplayers > 0) { priority = setup_animcounter % setup_numplayers; @@ -1459,7 +1469,7 @@ void M_DrawCharacterSelect(void) { for (j = 0; j < 9; j++) { - skin = setup_chargrid[i][j].skinlist[0]; + skin = setup_chargrid[i][j].skinlist[setup_page]; quadx = 4 * (i / 3); quady = 4 * (j / 3); @@ -1467,9 +1477,9 @@ void M_DrawCharacterSelect(void) // Don't draw a shadow if it'll get covered by another icon if ((i % 3 < 2) && (j % 3 < 2)) { - if ((setup_chargrid[i+1][j].skinlist[0] != -1) - && (setup_chargrid[i][j+1].skinlist[0] != -1) - && (setup_chargrid[i+1][j+1].skinlist[0] != -1)) + if ((setup_chargrid[i+1][j].skinlist[setup_page] != -1) + && (setup_chargrid[i][j+1].skinlist[setup_page] != -1) + && (setup_chargrid[i+1][j+1].skinlist[setup_page] != -1)) continue; } @@ -1492,7 +1502,7 @@ void M_DrawCharacterSelect(void) break; // k == setup_numplayers means no one has it selected } - skin = setup_chargrid[i][j].skinlist[0]; + skin = setup_chargrid[i][j].skinlist[setup_page]; quadx = 4 * (i / 3); quady = 4 * (j / 3); @@ -1507,7 +1517,8 @@ void M_DrawCharacterSelect(void) V_DrawMappedPatch(basex + 82 + (i*16) + quadx, 22 + (j*16) + quady, 0, faceprefix[skin][FACE_RANK], colormap); - if (setup_chargrid[i][j].numskins > 1) + // draw dot if there are more alts behind there! + if (setup_page+1 < setup_chargrid[i][j].numskins) V_DrawScaledPatch(basex + 82 + (i*16) + quadx, 22 + (j*16) + quady + 11, 0, W_CachePatchName("ALTSDOT", PU_CACHE)); } } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index a20d88492..ba87477f3 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2148,6 +2148,9 @@ struct setup_explosions_s setup_explosions[48]; UINT8 setup_numplayers = 0; // This variable is very important, it was extended to determine how many players exist in ALL menus. tic_t setup_animcounter = 0; +UINT8 setup_page = 0; +UINT8 setup_maxpage = 0; // For charsel page to identify alts easier... + // sets up the grid pos for the skin used by the profile. static void M_SetupProfileGridPos(setup_player_t *p) { @@ -2210,6 +2213,7 @@ static void M_SetupMidGameGridPos(setup_player_t *p, UINT8 num) void M_CharacterSelectInit(void) { UINT8 i, j; + setup_maxpage = 0; // While we're editing profiles, don't unset the devices for p1 if (gamestate == GS_MENU) @@ -2263,6 +2267,8 @@ void M_CharacterSelectInit(void) { setup_chargrid[x][y].skinlist[setup_chargrid[x][y].numskins] = i; setup_chargrid[x][y].numskins++; + + setup_maxpage = max(setup_maxpage, setup_chargrid[x][y].numskins-1); } for (j = 0; j < MAXSPLITSCREENPLAYERS; j++) @@ -2295,6 +2301,8 @@ void M_CharacterSelectInit(void) } } } + + setup_page = 0; } void M_CharacterSelect(INT32 choice) @@ -2595,6 +2603,7 @@ static void M_HandleCharAskChange(setup_player_t *p, UINT8 num) static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) { UINT8 numclones; + INT32 skin; if (cv_splitdevice.value) num = 0; @@ -2633,6 +2642,9 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) M_SetMenuDelay(num); } + // try to set the clone num to the page # if possible. + p->clonenum = setup_page; + // Process this after possible pad movement, // this makes sure we don't have a weird ghost hover on a character with no clones. numclones = setup_chargrid[p->gridx][p->gridy].numskins; @@ -2642,14 +2654,15 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/) { - if (setup_chargrid[p->gridx][p->gridy].numskins == 0) + skin = setup_chargrid[p->gridx][p->gridy].skinlist[setup_page]; + if (setup_page >= setup_chargrid[p->gridx][p->gridy].numskins || skin == -1) { S_StartSound(NULL, sfx_s3k7b); //sfx_s3kb2 } else { - if (setup_chargrid[p->gridx][p->gridy].numskins == 1) - p->mdepth = CSSTEP_COLORS; // Skip clones menu + if (setup_page+1 == setup_chargrid[p->gridx][p->gridy].numskins) + p->mdepth = CSSTEP_COLORS; // Skip clones menu if there are none on this page. else p->mdepth = CSSTEP_ALTS; @@ -2677,6 +2690,30 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) M_SetMenuDelay(num); } + if (num == 0 && setup_numplayers == 1 && setup_maxpage) // ONLY one player. + { + if (M_MenuButtonPressed(num, MBT_L)) + { + if (setup_page == 0) + setup_page = setup_maxpage; + else + setup_page--; + + S_StartSound(NULL, sfx_s3k63); + M_SetMenuDelay(num); + } + else if (M_MenuButtonPressed(num, MBT_R)) + { + if (setup_page == setup_maxpage) + setup_page = 0; + else + setup_page++; + + S_StartSound(NULL, sfx_s3k63); + M_SetMenuDelay(num); + } + } + return false; } @@ -2994,6 +3031,7 @@ boolean M_CharacterSelectHandler(INT32 choice) if (playersChanged == true) { + setup_page = 0; // reset that. break; } } From fdf57e59db265e96e7c02a5bfe1d73e3b8825895 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 22 Jul 2022 14:13:31 +0200 Subject: [PATCH 305/379] futureproof and use int16 for skins in menu instead of sint8 --- src/k_menu.h | 4 ++-- src/k_menudraw.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 772f159ab..e3d678c0d 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -511,7 +511,7 @@ void M_FreePlayerSetupColors(void); #define MAXCLONES MAXSKINS/8 extern struct setup_chargrid_s { - SINT8 skinlist[MAXCLONES]; + INT16 skinlist[MAXCLONES]; UINT8 numskins; } setup_chargrid[9][9]; @@ -532,7 +532,7 @@ typedef struct setup_player_s { SINT8 gridx, gridy; UINT8 profilen; - SINT8 skin; + INT16 skin; SINT8 clonenum; SINT8 rotate; UINT8 delay; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 98e5140fc..674c4e150 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -843,7 +843,7 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) if (p->mdepth == CSSTEP_ALTS) { - SINT8 skin; + INT16 skin; n = (p->clonenum) + numoptions/2; if (subtract) @@ -946,7 +946,7 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) } // returns false if the character couldn't be rendered -static boolean M_DrawCharacterSprite(INT16 x, INT16 y, SINT8 skin, INT32 addflags, UINT8 *colormap) +static boolean M_DrawCharacterSprite(INT16 x, INT16 y, INT16 skin, INT32 addflags, UINT8 *colormap) { UINT8 spr; spritedef_t *sprdef; @@ -1451,7 +1451,7 @@ void M_DrawCharacterSelect(void) UINT8 i, j, k; UINT8 priority = 0; INT16 quadx, quady; - SINT8 skin; + INT16 skin; INT32 basex = optionsmenu.profile != NULL ? 64 : 0; // Draw page num. From 2a4c4f86fce38189dc73e34ab3acf5a321f719f1 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Tue, 26 Jul 2022 23:50:23 +0200 Subject: [PATCH 306/379] WIP: server browser (only shows fake servers you can't connect to rn) --- src/doomdef.h | 2 +- src/k_menu.h | 58 ++++- src/k_menudef.c | 28 +++ src/k_menudraw.c | 110 ++++++++- src/k_menufunc.c | 573 +++++++++++++++++++++++++++++++++++++++++------ 5 files changed, 697 insertions(+), 74 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index 78208a25c..08c8fb137 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -162,7 +162,7 @@ extern char logfilename[1024]; // Comment out this line to completely disable update alerts (recommended for testing, but not for release) #ifndef BETAVERSION -#define UPDATE_ALERT +//#define UPDATE_ALERT #endif // The string used in the alert that pops up in the event of an update being available. diff --git a/src/k_menu.h b/src/k_menu.h index e3d678c0d..c018c7614 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -21,6 +21,10 @@ #include "g_demo.h" //menudemo_t #include "k_profiles.h" // profile data & functions #include "g_input.h" // gc_ +#include "i_threads.h" +#include "mserv.h" + +#define SERVERLISTDEBUG // flags for items in the menu // menu handle (what we do when key is pressed @@ -83,6 +87,18 @@ extern I_mutex k_menu_mutex; #endif +// for server threads etc. +typedef enum +{ + M_NOT_WAITING, + + M_WAITING_VERSION, + M_WAITING_SERVERS, +} +M_waiting_mode_t; + +extern M_waiting_mode_t m_waiting_mode; + typedef union { struct menu_s *submenu; // IT_SUBMENU @@ -210,6 +226,9 @@ extern menu_t PLAY_MP_JoinIPDef; extern menuitem_t PLAY_MP_RoomSelect[]; extern menu_t PLAY_MP_RoomSelectDef; +extern menuitem_t PLAY_MP_ServerBrowser[]; +extern menu_t PLAY_MP_ServerBrowserDef; + extern menuitem_t PLAY_BattleGamemodesMenu[]; extern menu_t PLAY_BattleGamemodesDef; @@ -638,6 +657,9 @@ void M_DifficultySelectInputs(INT32 choice); // Keep track of multiplayer menu related data // We'll add more stuff here as we need em... +#define SERVERSPERPAGE 8 +#define SERVERSPACE 18 + extern struct mpmenu_s { UINT8 modechoice; INT16 modewinextend[3][3]; // Used to "extend" the options in the mode select screen. @@ -645,8 +667,14 @@ extern struct mpmenu_s { // See M_OptSelectTick, it'll make more sense there. Sorry if this is a bit of a mess! UINT8 room; - tic_t ticker; + + UINT8 servernum; + UINT8 scrolln; + // max scrolln is always going to be serverlistcount-4 as we can display 8 servers at any time and we start scrolling at half. + + INT16 slide; + } mpmenu; // MP selection @@ -672,6 +700,33 @@ void M_MPRoomSelect(INT32 choice); void M_MPRoomSelectTick(void); void M_MPRoomSelectInit(INT32 choice); +// Server browser hell with threads... +void M_SetWaitingMode(int mode); +int M_GetWaitingMode(void); + +void M_MPServerBrowserTick(void); +boolean M_ServerBrowserInputs(INT32 ch); + +#ifdef MASTERSERVER +#ifdef HAVE_THREADS + +void Spawn_masterserver_thread (const char *name, void (*thread)(int*)); +int Same_instance (int id); + +#endif /*HAVE_THREADS*/ + +void Fetch_servers_thread (int *id); + +#endif /*MASTERSERVER*/ + +void M_RefreshServers(INT32 choice); +void M_ServersMenu(INT32 choice); + +// for debugging purposes... +#ifdef SERVERLISTDEBUG +void M_ServerListFillDebug(void); +#endif + // Options menu: // mode descriptions for video mode menu @@ -914,6 +969,7 @@ void M_DrawMPOptSelect(void); void M_DrawMPHost(void); void M_DrawMPJoinIP(void); void M_DrawMPRoomSelect(void); +void M_DrawMPServerBrowser(void); // Pause menu: void M_DrawPause(void); diff --git a/src/k_menudef.c b/src/k_menudef.c index e732b5e75..bd810c9e0 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -367,6 +367,34 @@ menu_t PLAY_MP_RoomSelectDef = { NULL }; +// SERVER BROWSER +menuitem_t PLAY_MP_ServerBrowser[] = +{ + + {IT_STRING | IT_CVAR, "SORT BY", NULL, // tooltip MUST be null. + NULL, {.cvar = &cv_serversort}, 0, 0}, + + {IT_STRING, "REFRESH", NULL, + NULL, {NULL}, 0, 0}, + + {IT_NOTHING, NULL, NULL, NULL, {NULL}, 0, 0}, +}; + +menu_t PLAY_MP_ServerBrowserDef = { + sizeof (PLAY_MP_ServerBrowser) / sizeof (menuitem_t), + &PLAY_MP_RoomSelectDef, + 0, + PLAY_MP_ServerBrowser, + 32, 36, + 0, 0, + 0, 0, + M_DrawMPServerBrowser, + M_MPServerBrowserTick, + NULL, + NULL, + M_ServerBrowserInputs +}; + // options menu menuitem_t OPTIONS_Main[] = { diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 674c4e150..5fb105090 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -462,10 +462,9 @@ void M_Drawer(void) // static void M_DrawMenuTooltips(void) { - V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL); - if (currentMenu->menuitems[itemOn].tooltip != NULL) { + V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL); V_DrawCenteredThinString(BASEVIDWIDTH/2, 12, V_ALLOWLOWERCASE|V_6WIDTHSPACE, currentMenu->menuitems[itemOn].tooltip); } } @@ -509,6 +508,7 @@ void M_DrawGenericMenu(void) { if (i == itemOn) cursory = y; + switch (currentMenu->menuitems[i].status & IT_DISPLAY) { case IT_PATCH: @@ -625,6 +625,9 @@ void M_DrawGenericMenu(void) } } + if ((currentMenu->menuitems[itemOn].status & IT_DISPLAY) == 0) + cursory = 300; // Put the cursor off screen if we can't even display that option and we're on it, it makes no sense otherwise... + // DRAW THE SKULL CURSOR if (((currentMenu->menuitems[itemOn].status & IT_DISPLAY) == IT_PATCH) || ((currentMenu->menuitems[itemOn].status & IT_DISPLAY) == IT_NOTHING)) @@ -2317,6 +2320,109 @@ void M_DrawMPRoomSelect(void) V_DrawFixedPatch(160<height)*FRACUNIT; + fixed_t text2loop = SHORT(text2->width)*FRACUNIT; + + const UINT8 startx = 18; + const UINT8 basey = 56; + const INT32 starty = basey - 18*mpmenu.scrolln + mpmenu.slide; + INT32 ypos = 0; + UINT8 i; + + // background stuff + V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("BG_MPS3", PU_CACHE), NULL); + + V_DrawFixedPatch(0, (BASEVIDHEIGHT + 16) * FRACUNIT, FRACUNIT, V_TRANSLUCENT, W_CachePatchName("MENUBG2", PU_CACHE), NULL); + + V_DrawFixedPatch(-bgText2Scroll, (BASEVIDHEIGHT-8) * FRACUNIT, + FRACUNIT, V_TRANSLUCENT, text2, NULL); + V_DrawFixedPatch(-bgText2Scroll + text2loop, (BASEVIDHEIGHT-8) * FRACUNIT, + FRACUNIT, V_TRANSLUCENT, text2, NULL); + + V_DrawFixedPatch(8 * FRACUNIT, -bgText1Scroll, + FRACUNIT, V_TRANSLUCENT, text1, NULL); + V_DrawFixedPatch(8 * FRACUNIT, -bgText1Scroll + text1loop, + FRACUNIT, V_TRANSLUCENT, text1, NULL); + + // bgTextScroll is already handled by the menu background. + + // the actual server list. + for (i = 0; i < serverlistcount; i++) + { + + boolean racegt = strcmp(serverlist[i].info.gametypename, "Race") == 0; + INT32 transflag = 0; + INT32 basetransflag = 0; + + if (serverlist[i].info.numberofplayer >= serverlist[i].info.maxplayer) + basetransflag = 5; + + /*if (i < mpmenu.servernum) + // if slide > 0, then we went DOWN in the list and have to fade that server out. + if (mpmenu.slide > 0) + transflag = basetransflag + (18-mpmenu.slide)/2; + else if (mpmenu.slide < 0) + transflag = 10 - basetransflag - (18-mpmenu.slide)/2;*/ + + transflag = basetransflag; + + if (transflag >= 0 && transflag < 10) + { + transflag = transflag << V_ALPHASHIFT; // shift the translucency flag. + + if (itemOn == 2 && mpmenu.servernum == i) + V_DrawFixedPatch(startx*FRACUNIT, (starty + ypos)*FRACUNIT, FRACUNIT, transflag, racegt ? racehs : batlhs, NULL); + else + V_DrawFixedPatch(startx*FRACUNIT, (starty + ypos)*FRACUNIT, FRACUNIT, transflag, racegt ? raceh : batlh, NULL); + + // Server name: + V_DrawString(startx+11, starty + ypos + 6, V_ALLOWLOWERCASE|transflag, serverlist[i].info.servername); + + // Ping: + V_DrawThinString(startx + 191, starty + ypos + 7, V_6WIDTHSPACE|transflag, va("%03d", serverlist[i].info.time)); + + // Playercount + V_DrawThinString(startx + 214, starty + ypos + 7, V_6WIDTHSPACE|transflag, va("%02d/%02d", serverlist[i].info.numberofplayer, serverlist[i].info.maxplayer)); + + // Power Level + V_DrawThinString(startx + 248, starty + ypos, V_6WIDTHSPACE|transflag, va("%04d PLv", serverlist[i].info.avgpwrlv)); + + // game speed if applicable: + if (racegt) + { + UINT8 speed = serverlist[i].info.kartvars & SV_SPEEDMASK; + patch_t *pp = W_CachePatchName(va("M_SDIFF%d", speed), PU_CACHE); + + V_DrawFixedPatch((startx + 251)*FRACUNIT, (starty + ypos + 9)*FRACUNIT, FRACUNIT, transflag, pp, NULL); + } + } + ypos += SERVERSPACE; + } + + // Draw genericmenu ontop! + V_DrawFill(0, 0, 320, 52, 31); + V_DrawFill(0, 53, 320, 1, 31); + V_DrawFill(0, 55, 320, 1, 31); + + V_DrawCenteredGamemodeString(160, 2, V_ALLOWLOWERCASE, 0, "Server Browser"); + + // normal menu options + M_DrawGenericMenu(); + +} + // OPTIONS MENU // Draws the cogs and also the options background! diff --git a/src/k_menufunc.c b/src/k_menufunc.c index ba87477f3..925552aed 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -158,7 +158,7 @@ CV_PossibleValue_t gametype_cons_t[NUMGAMETYPES+1]; static CV_PossibleValue_t serversort_cons_t[] = { {0,"Ping"}, - {1,"Modified State"}, + {1,"AVG. Power Level"}, {2,"Most Players"}, {3,"Least Players"}, {4,"Max Player Slots"}, @@ -233,6 +233,9 @@ static CV_PossibleValue_t dummymatchbots_cons_t[] = { consvar_t cv_dummymatchbots = CVAR_INIT ("dummymatchbots", "Off", CV_HIDDEN|CV_SAVE, dummymatchbots_cons_t, NULL); // Save this one if you wanna test your stuff without bots for instance +// for server fetch threads... +M_waiting_mode_t m_waiting_mode = M_NOT_WAITING; + // ========================================================================== // CVAR ONCHANGE EVENTS GO HERE // ========================================================================== @@ -426,35 +429,6 @@ void Addons_option_Onchange(void) (cv_addons_option.value == 3 ? IT_CVAR|IT_STRING|IT_CV_STRING : IT_DISABLED); } -void M_SortServerList(void) -{ -#if 0 -#ifndef NONET - switch(cv_serversort.value) - { - case 0: // Ping. - qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_time); - break; - case 1: // Modified state. - qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_modified); - break; - case 2: // Most players. - qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer_reverse); - break; - case 3: // Least players. - qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer); - break; - case 4: // Max players. - qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_maxplayer_reverse); - break; - case 5: // Gametype. - qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametype); - break; - } -#endif -#endif -} - static void M_EraseDataResponse(INT32 ch) { UINT8 i; @@ -1415,7 +1389,7 @@ static void M_MenuTypingInput(INT32 key) M_UpdateKeyboardX(); M_SetMenuDelay(pid); - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); } else if (menucmd[pid].dpad_ud < 0) // up { @@ -1425,7 +1399,7 @@ static void M_MenuTypingInput(INT32 key) M_UpdateKeyboardX(); M_SetMenuDelay(pid); - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); } else if (menucmd[pid].dpad_lr > 0) // right { @@ -1434,7 +1408,7 @@ static void M_MenuTypingInput(INT32 key) menutyping.keyboardx = 0; M_SetMenuDelay(pid); - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); } else if (menucmd[pid].dpad_lr < 0) // left { @@ -1445,7 +1419,7 @@ static void M_MenuTypingInput(INT32 key) M_UpdateKeyboardX(); } M_SetMenuDelay(pid); - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); } else if (M_MenuConfirmPressed(pid)) { @@ -1470,7 +1444,7 @@ static void M_MenuTypingInput(INT32 key) } M_SetMenuDelay(pid); - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); } } } @@ -2497,7 +2471,7 @@ static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num) if (p->profilen > maxp) p->profilen = 0; - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); M_SetMenuDelay(num); } else if (menucmd[num].dpad_ud < 0) @@ -2507,7 +2481,7 @@ static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num) else p->profilen--; - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); M_SetMenuDelay(num); } else if (M_MenuBackPressed(num)) @@ -2841,7 +2815,7 @@ static void M_HandleChooseFollower(setup_player_t *p, UINT8 num) p->followern = -1; M_SetMenuDelay(num); - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); M_GetFollowerState(p); } else if (menucmd[num].dpad_lr < 0 && numfollowers) @@ -2851,7 +2825,7 @@ static void M_HandleChooseFollower(setup_player_t *p, UINT8 num) p->followern = numfollowers-1; M_SetMenuDelay(num); - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); M_GetFollowerState(p); } else if (M_MenuConfirmPressed(num)) @@ -3885,6 +3859,12 @@ void M_MPRoomSelect(INT32 choice) M_GoBack(0); M_SetMenuDelay(pid); } + + else if (M_MenuConfirmPressed(pid)) + { + M_ServersMenu(0); + M_SetMenuDelay(pid); + } } void M_MPRoomSelectTick(void) @@ -3897,10 +3877,463 @@ void M_MPRoomSelectInit(INT32 choice) (void)choice; mpmenu.room = 0; mpmenu.ticker = 0; + mpmenu.servernum = 0; + mpmenu.scrolln = 0; + mpmenu.slide = 0; M_SetupNextMenu(&PLAY_MP_RoomSelectDef, false); } +// MULTIPLAYER ROOM FETCH / REFRESH THREADS + +// depending on mpmenu.room, either allows only unmodded servers or modded ones. Remove others from the list. +// we do this by iterating backwards. +static void M_CleanServerList(void) +{ + UINT8 i = serverlistcount; + + while (i) + { + + if (serverlist[i].info.modifiedgame != mpmenu.room) + { + // move everything after this index 1 slot down... + if (i != serverlistcount) + memcpy(&serverlist[i], &serverlist[i+1], sizeof(serverelem_t)*(serverlistcount-i)); + + serverlistcount--; + } + + i--; + } +} + +void +M_SetWaitingMode (int mode) +{ +#ifdef HAVE_THREADS + I_lock_mutex(&k_menu_mutex); +#endif + { + m_waiting_mode = mode; + } +#ifdef HAVE_THREADS + I_unlock_mutex(k_menu_mutex); +#endif +} + +int +M_GetWaitingMode (void) +{ + int mode; + +#ifdef HAVE_THREADS + I_lock_mutex(&k_menu_mutex); +#endif + { + mode = m_waiting_mode; + } +#ifdef HAVE_THREADS + I_unlock_mutex(k_menu_mutex); +#endif + + return mode; +} + +#ifdef MASTERSERVER +#ifdef HAVE_THREADS +void +Spawn_masterserver_thread (const char *name, void (*thread)(int*)) +{ + int *id = malloc(sizeof *id); + + I_lock_mutex(&ms_QueryId_mutex); + { + *id = ms_QueryId; + } + I_unlock_mutex(ms_QueryId_mutex); + + I_spawn_thread(name, (I_thread_fn)thread, id); +} + +int +Same_instance (int id) +{ + int okay; + + I_lock_mutex(&ms_QueryId_mutex); + { + okay = ( id == ms_QueryId ); + } + I_unlock_mutex(ms_QueryId_mutex); + + return okay; +} +#endif/*HAVE_THREADS*/ + +void +Fetch_servers_thread (int *id) +{ + msg_server_t * server_list; + + (void)id; + + M_SetWaitingMode(M_WAITING_SERVERS); + +#ifdef HAVE_THREADS + server_list = GetShortServersList(*id); +#else + server_list = GetShortServersList(0); +#endif + + if (server_list) + { +#ifdef HAVE_THREADS + if (Same_instance(*id)) +#endif + { + M_SetWaitingMode(M_NOT_WAITING); + +#ifdef HAVE_THREADS + I_lock_mutex(&ms_ServerList_mutex); + { + ms_ServerList = server_list; + } + I_unlock_mutex(ms_ServerList_mutex); +#else + CL_QueryServerList(server_list); + free(server_list); +#endif + } +#ifdef HAVE_THREADS + else + { + free(server_list); + } +#endif + } + +#ifdef HAVE_THREADS + free(id); +#endif +} +#endif/*MASTERSERVER*/ + +// updates serverlist +void M_RefreshServers(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 + +#ifdef MASTERSERVER +#ifdef HAVE_THREADS + Spawn_masterserver_thread("fetch-servers", Fetch_servers_thread); +#else/*HAVE_THREADS*/ + Fetch_servers_thread(NULL); +#endif/*HAVE_THREADS*/ +#else/*MASTERSERVER*/ + CL_UpdateServerList(); +#endif/*MASTERSERVER*/ + +#ifdef SERVERLISTDEBUG + M_ServerListFillDebug(); +#endif + M_CleanServerList(); + M_SortServerList(); + +} + +#ifndef NONET +#ifdef UPDATE_ALERT +static void M_CheckMODVersion(int id) +{ + char updatestring[500]; + const char *updatecheck = GetMODVersion(id); + if(updatecheck) + { + sprintf(updatestring, UPDATE_ALERT_STRING, VERSIONSTRING, updatecheck); +#ifdef HAVE_THREADS + I_lock_mutex(&k_menu_mutex); +#endif + M_StartMessage(updatestring, NULL, MM_NOTHING); +#ifdef HAVE_THREADS + I_unlock_mutex(k_menu_mutex); +#endif + } +} +#endif/*UPDATE_ALERT*/ + +#if defined (UPDATE_ALERT) && defined (HAVE_THREADS) +static void +Check_new_version_thread (int *id) +{ + M_SetWaitingMode(M_WAITING_VERSION); + + M_CheckMODVersion(*id); + + if (Same_instance(*id)) + { + Fetch_servers_thread(id); + } + else + { + free(id); + } +} +#endif/*defined (UPDATE_ALERT) && defined (HAVE_THREADS)*/ + + +// Initializes serverlist when entering the menu... +void M_ServersMenu(INT32 choice) +{ + (void)choice; + // modified game check: no longer handled + // we don't request a restart unless the filelist differs + + mpmenu.servernum = 0; + mpmenu.scrolln = 0; + mpmenu.slide = 0; + + M_SetupNextMenu(&PLAY_MP_ServerBrowserDef, false); + itemOn = 0; + +#if defined (MASTERSERVER) && defined (HAVE_THREADS) + I_lock_mutex(&ms_QueryId_mutex); + { + ms_QueryId++; + } + I_unlock_mutex(ms_QueryId_mutex); + + I_lock_mutex(&ms_ServerList_mutex); + { + if (ms_ServerList) + { + free(ms_ServerList); + ms_ServerList = NULL; + } + } + I_unlock_mutex(ms_ServerList_mutex); + +#ifdef UPDATE_ALERT + Spawn_masterserver_thread("check-new-version", Check_new_version_thread); +#else/*UPDATE_ALERT*/ + Spawn_masterserver_thread("fetch-servers", Fetch_servers_thread); +#endif/*UPDATE_ALERT*/ +#else/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/ +#ifdef UPDATE_ALERT + M_CheckMODVersion(0); +#endif/*UPDATE_ALERT*/ + M_RefreshServers(0); +#endif/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/ + +#ifdef SERVERLISTDEBUG + M_ServerListFillDebug(); +#endif + + M_CleanServerList(); + M_SortServerList(); +} + +#ifdef SERVERLISTDEBUG + +// Fill serverlist with a bunch of garbage to make our life easier in debugging +void M_ServerListFillDebug(void) +{ + UINT8 i = 0; + + serverlistcount = 10; + memset(serverlist, 0, sizeof(serverlist)); // zero out the array for convenience... + + for (i = 0; i < serverlistcount; i++) + { + // We don't really care about the server node for this, let's just fill in the info so that we have a visual... + serverlist[i].info.numberofplayer = min(i, 8); + serverlist[i].info.maxplayer = 8; + + serverlist[i].info.avgpwrlv = P_RandomRange(500, 1500); + serverlist[i].info.time = P_RandomRange(16, 500); // ping + + strcpy(serverlist[i].info.servername, va("Serv %d", i+1)); + + strcpy(serverlist[i].info.gametypename, i & 1 ? "Race" : "Battle"); + + P_RandomRange(0, 5); // change results... + serverlist[i].info.kartvars = P_RandomRange(0, 3) & SV_SPEEDMASK; + + serverlist[i].info.modifiedgame = P_RandomRange(0, 1); + + CONS_Printf("Serv %d | %d...\n", i, serverlist[i].info.modifiedgame); + } +} + +#endif // SERVERLISTDEBUG + +#endif //NONET + +// Ascending order, not descending. +// The casts are safe as long as the caller doesn't do anything stupid. +#define SERVER_LIST_ENTRY_COMPARATOR(key) \ +static int ServerListEntryComparator_##key(const void *entry1, const void *entry2) \ +{ \ + const serverelem_t *sa = (const serverelem_t*)entry1, *sb = (const serverelem_t*)entry2; \ + if (sa->info.key != sb->info.key) \ + return sa->info.key - sb->info.key; \ + return strcmp(sa->info.servername, sb->info.servername); \ +} + +// This does descending instead of ascending. +#define SERVER_LIST_ENTRY_COMPARATOR_REVERSE(key) \ +static int ServerListEntryComparator_##key##_reverse(const void *entry1, const void *entry2) \ +{ \ + const serverelem_t *sa = (const serverelem_t*)entry1, *sb = (const serverelem_t*)entry2; \ + if (sb->info.key != sa->info.key) \ + return sb->info.key - sa->info.key; \ + return strcmp(sb->info.servername, sa->info.servername); \ +} + +SERVER_LIST_ENTRY_COMPARATOR(time) +SERVER_LIST_ENTRY_COMPARATOR(numberofplayer) +SERVER_LIST_ENTRY_COMPARATOR_REVERSE(numberofplayer) +SERVER_LIST_ENTRY_COMPARATOR_REVERSE(maxplayer) +SERVER_LIST_ENTRY_COMPARATOR(avgpwrlv) + + +static int ServerListEntryComparator_gametypename(const void *entry1, const void *entry2) +{ + const serverelem_t *sa = (const serverelem_t*)entry1, *sb = (const serverelem_t*)entry2; + int c; + if (( c = strcasecmp(sa->info.gametypename, sb->info.gametypename) )) + return c; + return strcmp(sa->info.servername, sb->info.servername); \ +} + +void M_SortServerList(void) +{ +#ifndef NONET + switch(cv_serversort.value) + { + case 0: // Ping. + qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_time); + break; + case 1: // AVG. Power Level + qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_avgpwrlv); + break; + case 2: // Most players. + qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer_reverse); + break; + case 3: // Least players. + qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer); + break; + case 4: // Max players. + qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_maxplayer_reverse); + break; + case 5: // Gametype. + qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametypename); + break; + } +#endif +} + + +// Server browser inputs & ticker +void M_MPServerBrowserTick(void) +{ + mpmenu.slide /= 2; +} + +// Input handler for server browser. +boolean M_ServerBrowserInputs(INT32 ch) +{ + UINT8 pid = 0; + UINT8 maxscroll = serverlistcount-(SERVERSPERPAGE/2); + (void) ch; + + if (!itemOn && menucmd[pid].dpad_ud < 0) + { + M_PrevOpt(); // go to itemOn 2 + if (serverlistcount) + { + UINT8 prevscroll = mpmenu.scrolln; + + mpmenu.servernum = serverlistcount; + mpmenu.scrolln = maxscroll; + mpmenu.slide = SERVERSPACE * (prevscroll - mpmenu.scrolln); + } + else + { + itemOn = 1; // Sike! If there are no servers, go to refresh instead. + } + + return true; // overwrite behaviour. + } + else if (itemOn == 2) // server browser itself... + { + // we have to manually do that here. + if (M_MenuBackPressed(pid)) + { + M_GoBack(0); + M_SetMenuDelay(pid); + } + + else if (menucmd[pid].dpad_ud > 0) // down + { + if (mpmenu.servernum >= serverlistcount-1) + { + UINT8 prevscroll = mpmenu.scrolln; + mpmenu.servernum = 0; + mpmenu.scrolln = 0; + mpmenu.slide = SERVERSPACE * (prevscroll - mpmenu.scrolln); + + M_NextOpt(); // Go back to the top of the real menu. + } + else + { + mpmenu.servernum++; + if (mpmenu.scrolln < maxscroll && mpmenu.servernum > SERVERSPERPAGE/2) + { + mpmenu.scrolln++; + mpmenu.slide += SERVERSPACE; + } + } + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + + } + else if (menucmd[pid].dpad_ud < 0) + { + + if (!mpmenu.servernum) + { + M_PrevOpt(); + } + else + { + if (mpmenu.servernum <= serverlistcount-(SERVERSPERPAGE/2) && mpmenu.scrolln) + { + mpmenu.scrolln--; + mpmenu.slide -= SERVERSPACE; + } + + mpmenu.servernum--; + } + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + + } + return true; // Overwrite behaviour. + } + return false; // use normal behaviour. +} + + // Options menu: struct optionsmenu_s optionsmenu; @@ -4037,7 +4470,7 @@ boolean M_OptionsInputs(INT32 ch) M_SetMenuDelay(pid); optionsmenu.offset += 48; M_NextOpt(); - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); if (itemOn == 0) optionsmenu.offset -= currentMenu->numitems*48; @@ -4050,7 +4483,7 @@ boolean M_OptionsInputs(INT32 ch) M_SetMenuDelay(pid); optionsmenu.offset -= 48; M_PrevOpt(); - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); if (itemOn == currentMenu->numitems-1) optionsmenu.offset += currentMenu->numitems*48; @@ -4253,7 +4686,7 @@ void M_HandleProfileSelect(INT32 ch) optionsmenu.offset -= (128 + 128/8)*(maxp+1); } - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); M_SetMenuDelay(pid); } @@ -4268,7 +4701,7 @@ void M_HandleProfileSelect(INT32 ch) optionsmenu.offset += (128 + 128/8)*(maxp+1); } - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); M_SetMenuDelay(pid); } @@ -4294,7 +4727,7 @@ void M_HandleProfileSelect(INT32 ch) return; } - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); M_StartEditProfile(MA_YES); } @@ -4472,7 +4905,7 @@ void M_HandleVideoModes(INT32 ch) } else if (M_MenuConfirmPressed(pid)) { - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); optionsmenu.vidm_testingmode = 0; // stop testing } } @@ -4481,7 +4914,7 @@ void M_HandleVideoModes(INT32 ch) { if (menucmd[pid].dpad_ud > 0) { - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); if (++optionsmenu.vidm_selected >= optionsmenu.vidm_nummodes) optionsmenu.vidm_selected = 0; @@ -4490,7 +4923,7 @@ void M_HandleVideoModes(INT32 ch) else if (menucmd[pid].dpad_ud < 0) { - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); if (--optionsmenu.vidm_selected < 0) optionsmenu.vidm_selected = optionsmenu.vidm_nummodes - 1; @@ -4499,7 +4932,7 @@ void M_HandleVideoModes(INT32 ch) else if (menucmd[pid].dpad_lr < 0) { - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); optionsmenu.vidm_selected -= optionsmenu.vidm_column_size; if (optionsmenu.vidm_selected < 0) optionsmenu.vidm_selected = (optionsmenu.vidm_column_size*3) + optionsmenu.vidm_selected; @@ -4511,7 +4944,7 @@ void M_HandleVideoModes(INT32 ch) else if (menucmd[pid].dpad_lr > 0) { - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); optionsmenu.vidm_selected += optionsmenu.vidm_column_size; if (optionsmenu.vidm_selected >= (optionsmenu.vidm_column_size*3)) optionsmenu.vidm_selected %= optionsmenu.vidm_column_size; @@ -4524,7 +4957,7 @@ void M_HandleVideoModes(INT32 ch) else if (M_MenuConfirmPressed(pid)) { M_SetMenuDelay(pid); - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); if (vid.modenum == optionsmenu.modedescs[optionsmenu.vidm_selected].modenum) SCR_SetDefaultMode(); else @@ -4929,7 +5362,7 @@ void M_HandleItemToggles(INT32 choice) if (menucmd[pid].dpad_lr > 0) { - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); column++; if (((column*height)+row) >= currentMenu->numitems) column = 0; @@ -4941,7 +5374,7 @@ void M_HandleItemToggles(INT32 choice) else if (menucmd[pid].dpad_lr < 0) { - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); column--; if (column < 0) column = width-1; @@ -4957,7 +5390,7 @@ void M_HandleItemToggles(INT32 choice) else if (menucmd[pid].dpad_ud > 0) { - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); row = (row+1) % height; if (((column*height)+row) >= currentMenu->numitems) row = 0; @@ -4969,7 +5402,7 @@ void M_HandleItemToggles(INT32 choice) else if (menucmd[pid].dpad_ud < 0) { - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); row = (row-1) % height; if (row < 0) row = height-1; @@ -5080,7 +5513,7 @@ void M_HandleProfileErase(INT32 choice) if (menucmd[pid].dpad_ud > 0) { - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); optionsmenu.eraseprofilen++; if (optionsmenu.eraseprofilen > np) @@ -5090,7 +5523,7 @@ void M_HandleProfileErase(INT32 choice) } else if (menucmd[pid].dpad_ud < 0) { - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); if (optionsmenu.eraseprofilen == 1) optionsmenu.eraseprofilen = np; @@ -5180,7 +5613,7 @@ boolean M_ExtrasInputs(INT32 ch) { extrasmenu.offset += 48; M_NextOpt(); - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); if (itemOn == 0) extrasmenu.offset -= currentMenu->numitems*48; @@ -5193,7 +5626,7 @@ boolean M_ExtrasInputs(INT32 ch) { extrasmenu.offset -= 48; M_PrevOpt(); - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); if (itemOn == currentMenu->numitems-1) extrasmenu.offset += currentMenu->numitems*48; @@ -5324,7 +5757,7 @@ boolean M_PauseInputs(INT32 ch) { M_SetMenuDelay(pid); pausemenu.offset -= 50; // Each item is spaced by 50 px - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); M_PrevOpt(); return true; } @@ -5332,7 +5765,7 @@ boolean M_PauseInputs(INT32 ch) else if (menucmd[pid].dpad_ud > 0) { pausemenu.offset += 50; // Each item is spaced by 50 px - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); M_NextOpt(); M_SetMenuDelay(pid); return true; @@ -5604,7 +6037,7 @@ void M_HandleReplayHutList(INT32 choice) return; //M_PrevOpt(); - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); M_SetMenuDelay(pid); extrasmenu.replayScrollTitle = 0; extrasmenu.replayScrollDelay = TICRATE; extrasmenu.replayScrollDir = 1; } @@ -5617,7 +6050,7 @@ void M_HandleReplayHutList(INT32 choice) return; //itemOn = 0; // Not M_NextOpt because that would take us to the extra dummy item - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); M_SetMenuDelay(pid); extrasmenu.replayScrollTitle = 0; extrasmenu.replayScrollDelay = TICRATE; extrasmenu.replayScrollDir = 1; } @@ -5654,7 +6087,7 @@ void M_HandleReplayHutList(INT32 choice) } else { - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); dir_on[menudepthleft] = 1; M_PrepReplayList(); } @@ -5667,7 +6100,7 @@ void M_HandleReplayHutList(INT32 choice) } break; case EXT_UP: - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); menupath[menupathindex[++menudepthleft]] = 0; if (!preparefilemenu(false, true)) { @@ -5967,14 +6400,14 @@ void M_HandleAddons(INT32 choice) { if (dir_on[menudepthleft] < sizedirmenu-1) dir_on[menudepthleft]++; - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); M_SetMenuDelay(pid); } else if (menucmd[pid].dpad_ud < 0) { if (dir_on[menudepthleft]) dir_on[menudepthleft]--; - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); M_SetMenuDelay(pid); } @@ -5984,7 +6417,7 @@ void M_HandleAddons(INT32 choice) for (i = numaddonsshown; i && (dir_on[menudepthleft] < sizedirmenu-1); i--) dir_on[menudepthleft]++; - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); M_SetMenuDelay(pid); } @@ -5994,7 +6427,7 @@ void M_HandleAddons(INT32 choice) for (i = numaddonsshown; i && (dir_on[menudepthleft]); i--) dir_on[menudepthleft]--; - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); M_SetMenuDelay(pid); } @@ -6030,7 +6463,7 @@ void M_HandleAddons(INT32 choice) } else { - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); dir_on[menudepthleft] = 1; } refresh = false; @@ -6044,7 +6477,7 @@ void M_HandleAddons(INT32 choice) break; case EXT_UP: - S_StartSound(NULL, sfx_menu1); + S_StartSound(NULL, sfx_s3k5b); menupath[menupathindex[++menudepthleft]] = 0; if (!preparefilemenu(false, false)) { From a8d847227d1efad133e7c80ece12cdd5614b28d8 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Wed, 27 Jul 2022 12:04:52 +0200 Subject: [PATCH 307/379] WIP: time attack, kinda works but crashes when you finish lol --- src/k_menu.h | 15 ++++++++ src/k_menudef.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++-- src/k_menudraw.c | 52 +++++++++++++++++++++----- src/k_menufunc.c | 85 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 234 insertions(+), 14 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index c018c7614..eb6e166cc 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -205,6 +205,15 @@ extern menu_t PLAY_LevelSelectDef; extern menuitem_t PLAY_TimeAttack[]; extern menu_t PLAY_TimeAttackDef; +extern menuitem_t PLAY_TAReplay[]; +extern menu_t PLAY_TAReplayDef; + +extern menuitem_t PLAY_TAReplayGuest[]; +extern menu_t PLAY_TAReplayGuestDef; + +extern menuitem_t PLAY_TAGhosts[]; +extern menu_t PLAY_TAGhostsDef; + extern menuitem_t PLAY_MP_OptSelect[]; extern menu_t PLAY_MP_OptSelectDef; @@ -677,6 +686,12 @@ extern struct mpmenu_s { } mpmenu; +// Time Attack +void M_StartTimeAttack(INT32 choice); +void M_ReplayTimeAttack(INT32 choice); +void M_HandleStaffReplay(INT32 choice); +void M_SetGuestReplay(INT32 choice); + // MP selection void M_MPOptSelect(INT32 choice); void M_MPOptSelectInit(INT32 choice); diff --git a/src/k_menudef.c b/src/k_menudef.c index bd810c9e0..fc9439e14 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -204,10 +204,11 @@ menu_t PLAY_LevelSelectDef = { menuitem_t PLAY_TimeAttack[] = { - {IT_STRING, "Replay...", NULL, NULL, {NULL}, 0, 0}, - {IT_STRING, "Ghosts...", NULL, NULL, {NULL}, 0, 0}, - {IT_SPACE, NULL, NULL, NULL, {NULL}, 0, 0}, - {IT_STRING, "Start", NULL, NULL, {NULL}, 0, 0}, + {IT_STRING | IT_SUBMENU, "Replay...", NULL, NULL, {.submenu = &PLAY_TAReplayDef}, 0, 0}, + {IT_STRING | IT_SUBMENU, "Guest...", NULL, NULL, {.submenu = &PLAY_TAReplayGuestDef}, 0, 0}, + {IT_STRING | IT_SUBMENU, "Ghosts...", NULL, NULL, {.submenu = &PLAY_TAGhostsDef}, 0, 0}, + {IT_HEADERTEXT|IT_HEADER, "", NULL, NULL, {NULL}, 0, 0}, + {IT_STRING | IT_CALL, "Start", NULL, NULL, {.routine = M_StartTimeAttack}, 0, 0}, }; menu_t PLAY_TimeAttackDef = { @@ -225,6 +226,93 @@ menu_t PLAY_TimeAttackDef = { NULL }; + +menuitem_t PLAY_TAReplay[] = +{ + {IT_STRING | IT_CALL, "Replay Best Time", NULL, NULL, {.routine = M_ReplayTimeAttack}, 0, 0}, + {IT_STRING | IT_CALL, "Replay Best Lap", NULL, NULL, {.routine = M_ReplayTimeAttack}, 0, 0}, + {IT_HEADERTEXT|IT_HEADER, "", NULL, NULL, {NULL}, 0, 0}, + {IT_STRING | IT_CALL, "Replay Last", NULL, NULL, {.routine = M_ReplayTimeAttack}, 0, 0}, + {IT_STRING | IT_CALL, "Replay Guest", NULL, NULL, {.routine = M_ReplayTimeAttack}, 0, 0}, + {IT_STRING | IT_CALL, "Replay Staff", NULL, NULL, {.routine = M_HandleStaffReplay}, 0, 0}, + {IT_HEADERTEXT|IT_HEADER, "", NULL, NULL, {NULL}, 0, 0}, + + {IT_STRING | IT_SUBMENU, "Back", NULL, NULL, {.submenu = &PLAY_TimeAttackDef}, 0, 0}, +}; + +menu_t PLAY_TAReplayDef = { + sizeof(PLAY_TAReplay) / sizeof(menuitem_t), + &PLAY_TimeAttackDef, + 0, + PLAY_TAReplay, + 0, 0, + 0, 0, + 2, 5, + M_DrawTimeAttack, + NULL, + NULL, + NULL, + NULL +}; + +menuitem_t PLAY_TAReplayGuest[] = +{ + {IT_HEADERTEXT|IT_HEADER, "Save as guest...", NULL, NULL, {NULL}, 0, 0}, + + {IT_STRING | IT_CALL, "Best Time", NULL, NULL, {.routine = M_SetGuestReplay}, 0, 0}, + {IT_STRING | IT_CALL, "Best Lap", NULL, NULL, {.routine = M_SetGuestReplay}, 0, 0}, + {IT_STRING | IT_CALL, "Last Run", NULL, NULL, {.routine = M_SetGuestReplay}, 0, 0}, + + {IT_HEADERTEXT|IT_HEADER, "", NULL, NULL, {NULL}, 0, 0}, + {IT_STRING | IT_CALL, "Delete Guest", NULL, NULL, {.routine = M_SetGuestReplay}, 0, 0}, + + {IT_HEADERTEXT|IT_HEADER, "", NULL, NULL, {NULL}, 0, 0}, + {IT_STRING | IT_SUBMENU, "Back", NULL, NULL, {.submenu = &PLAY_TimeAttackDef}, 0, 0}, + +}; + +menu_t PLAY_TAReplayGuestDef = { + sizeof(PLAY_TAReplayGuest) / sizeof(menuitem_t), + &PLAY_TimeAttackDef, + 0, + PLAY_TAReplayGuest, + 0, 0, + 0, 0, + 2, 5, + M_DrawTimeAttack, + NULL, + NULL, + NULL, + NULL +}; + +menuitem_t PLAY_TAGhosts[] = +{ + {IT_STRING | IT_CVAR, "Best Time", NULL, NULL, {.cvar = &cv_ghost_besttime}, 0, 0}, + {IT_STRING | IT_CVAR, "Best Lap", NULL, NULL, {.cvar = &cv_ghost_bestlap}, 0, 0}, + {IT_STRING | IT_CVAR, "Last", NULL, NULL, {.cvar = &cv_ghost_last}, 0, 0}, + {IT_DISABLED, "Guest", NULL, NULL, {.cvar = &cv_ghost_guest}, 0, 0}, + {IT_DISABLED, "Staff", NULL, NULL, {.cvar = &cv_ghost_staff}, 0, 0}, + + {IT_HEADERTEXT|IT_HEADER, "", NULL, NULL, {NULL}, 0, 0}, + {IT_STRING | IT_SUBMENU, "Back", NULL, NULL, {.submenu = &PLAY_TimeAttackDef}, 0, 0}, +}; + +menu_t PLAY_TAGhostsDef = { + sizeof(PLAY_TAGhosts) / sizeof(menuitem_t), + &PLAY_TimeAttackDef, + 0, + PLAY_TAGhosts, + 0, 0, + 0, 0, + 2, 5, + M_DrawTimeAttack, + NULL, + NULL, + NULL, + NULL +}; + // BATTLE menuitem_t PLAY_BattleGamemodesMenu[] = diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 5fb105090..ce4f93e49 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2000,38 +2000,70 @@ void M_DrawTimeAttack(void) INT16 t = (48*menutransition.tics); INT16 leftedge = 149+t+16; INT16 rightedge = 149+t+155; - INT16 opty = 152; + INT16 opty = 140; + INT32 w; lumpnum_t lumpnum; UINT8 i; + consvar_t *cv; M_DrawLevelSelectBlock(0, 2, map, true, false); //V_DrawFill(24-t, 82, 100, 100, 36); // size test - lumpnum = W_CheckNumForName(va("%sR", G_BuildMapName(map+1))); - if (lumpnum != LUMPERROR) - V_DrawScaledPatch(24-t, 82, 0, W_CachePatchNum(lumpnum, PU_CACHE)); - V_DrawScaledPatch(149+t, 70, 0, W_CachePatchName("BESTTIME", PU_CACHE)); - V_DrawRightAlignedString(rightedge-12, 82, highlightflags, "BEST LAP:"); - K_drawKartTimestamp(0, 162+t, 88, 0, 2); + if (currentMenu == &PLAY_TimeAttackDef) + { + lumpnum = W_CheckNumForName(va("%sR", G_BuildMapName(map+1))); + if (lumpnum != LUMPERROR) + V_DrawScaledPatch(24-t, 82, 0, W_CachePatchNum(lumpnum, PU_CACHE)); - V_DrawRightAlignedString(rightedge-12, 112, highlightflags, "BEST TIME:"); - K_drawKartTimestamp(0, 162+t, 118, map, 1); + V_DrawRightAlignedString(rightedge-12, 82, highlightflags, "BEST LAP:"); + K_drawKartTimestamp(0, 162+t, 88, 0, 2); + + V_DrawRightAlignedString(rightedge-12, 112, highlightflags, "BEST TIME:"); + K_drawKartTimestamp(0, 162+t, 118, map, 1); + } + else + opty = 80; for (i = 0; i < currentMenu->numitems; i++) { - UINT32 f = (i == itemOn) ? recommendedflags : highlightflags; + UINT32 f = (i == itemOn) ? highlightflags : 0; switch (currentMenu->menuitems[i].status & IT_DISPLAY) { + + case IT_HEADERTEXT: + + V_DrawString(leftedge, opty, highlightflags, currentMenu->menuitems[i].text); + opty += 10; + break; + case IT_STRING: + if (i >= currentMenu->numitems-1) V_DrawRightAlignedString(rightedge, opty, f, currentMenu->menuitems[i].text); else V_DrawString(leftedge, opty, f, currentMenu->menuitems[i].text); opty += 10; + + // Cvar specific handling + + if (currentMenu->menuitems[i].status & IT_CVAR) + { + cv = currentMenu->menuitems[i].itemaction.cvar; + + w = V_StringWidth(cv->string, 0); + V_DrawString(leftedge, opty, f, cv->string); + if (i == itemOn) + { + V_DrawCharacter(leftedge - 10 - (skullAnimCounter/5), opty, '\x1C' | f, false); // left arrow + V_DrawCharacter(leftedge + w + 2+ (skullAnimCounter/5), opty, '\x1D' | f, false); // right arrow + } + opty += 10; + } + break; case IT_SPACE: opty += 4; diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 925552aed..f2b9133fb 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3454,6 +3454,9 @@ void M_CupSelectHandler(INT32 choice) if (cupgrid.grandprix == true) { + + UINT8 ssplayers = cv_splitplayers.value-1; + S_StartSound(NULL, sfx_s3k63); // Early fadeout to let the sound finish playing @@ -3464,6 +3467,15 @@ void M_CupSelectHandler(INT32 choice) memset(&grandprixinfo, 0, sizeof(struct grandprixinfo)); + if (cv_maxplayers.value < ssplayers+1) + CV_SetValue(&cv_maxplayers, ssplayers+1); + + if (splitscreen != ssplayers) + { + splitscreen = ssplayers; + SplitScreen_OnChange(); + } + // read our dummy cvars grandprixinfo.gamespeed = min(KARTSPEED_HARD, cv_dummygpdifficulty.value); @@ -3677,6 +3689,79 @@ void M_LevelSelectTick(void) levellist.y += dist/2; } + +// time attack stuff... +void M_HandleStaffReplay(INT32 choice) +{ + // @TODO: + (void) choice; +} + +void M_ReplayTimeAttack(INT32 choice) +{ + // @TODO: + (void) choice; +} + +void M_SetGuestReplay(INT32 choice) +{ + // @TODO: + (void) choice; +} + +void M_StartTimeAttack(INT32 choice) +{ + char *gpath; + const size_t glen = strlen("media")+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1; + char nameofdemo[256]; + (void)choice; + emeralds = 0; + + modeattacking = ATTACKING_TIME; + + // Still need to reset devmode + cv_debug = 0; + + if (demo.playback) + G_StopDemo(); + if (metalrecording) + G_StopMetalDemo(); + + splitscreen = 0; + SplitScreen_OnChange(); + + S_StartSound(NULL, sfx_s3k63); + + paused = false; + + // Early fadeout to let the sound finish playing + F_WipeStartScreen(); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeEndScreen(); + F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); + + SV_StartSinglePlayerServer(); + + gpath = va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s", + srb2home, timeattackfolder); + M_MkdirEach(gpath, M_PathParts(gpath) - 3, 0755); + + if ((gpath = malloc(glen)) == NULL) + I_Error("Out of memory for replay filepath\n"); + + sprintf(gpath,"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", timeattackfolder, G_BuildMapName(levellist.choosemap+1)); + snprintf(nameofdemo, sizeof nameofdemo, "%s-%s-last", gpath, cv_skin[0].string); + + if (!cv_autorecord.value) + remove(va("%s"PATHSEP"%s.lmp", srb2home, nameofdemo)); + else + G_RecordDemo(nameofdemo); + + M_ClearMenus(true); + D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_dummygpencore.value == 1), 1, 1, false, false); +} + + struct mpmenu_s mpmenu; // MULTIPLAYER OPTION SELECT From 85a132c149381ff903db0beb6b0d08a136aad689 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Tue, 23 Aug 2022 00:05:50 -0500 Subject: [PATCH 308/379] Port Ediolon's SDL GameController work Basically instantly solved all of the issues that made this branch completely unusable --- src/g_input.c | 161 ++++++++++++++++++++------------------------- src/g_input.h | 15 +++-- src/k_menufunc.c | 97 +++++++++++++-------------- src/sdl/i_system.c | 126 +++++++++++++++-------------------- src/sdl/i_video.c | 108 +++++++++--------------------- src/sdl/sdlmain.h | 14 ++-- 6 files changed, 220 insertions(+), 301 deletions(-) diff --git a/src/g_input.c b/src/g_input.c index 120dd6a79..ead8cab27 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -173,7 +173,7 @@ void G_MapEventsToControls(event_t *ev) break; case ev_joystick: // buttons are virtual keys - if (ev->data1 >= JOYAXISSET) + if (ev->data1 >= JOYAXISSETS) { #ifdef PARANOIA CONS_Debug(DBG_GAMELOGIC, "Bad joystick axis event %d\n", ev->data1); @@ -181,37 +181,56 @@ void G_MapEventsToControls(event_t *ev) break; } - i = ev->data1 * 4; + i = ev->data1; - if (ev->data2 != INT32_MAX) + if (i >= JOYANALOGS) { - if (ev->data2 < 0) + // The trigger axes are handled specially. + i -= JOYANALOGS; + + if (ev->data2 != INT32_MAX) { - // Left - gamekeydown[ev->device][KEY_AXIS1 + i] = abs(ev->data2); - gamekeydown[ev->device][KEY_AXIS1 + i + 1] = 0; + gamekeydown[ev->device][KEY_AXIS1 + (JOYANALOGS * 4) + (i * 2)] = max(0, ev->data2); } - else + + if (ev->data3 != INT32_MAX) { - // Right - gamekeydown[ev->device][KEY_AXIS1 + i] = 0; - gamekeydown[ev->device][KEY_AXIS1 + i + 1] = abs(ev->data2); + gamekeydown[ev->device][KEY_AXIS1 + (JOYANALOGS * 4) + (i * 2) + 1] = max(0, ev->data3); } } - - if (ev->data3 != INT32_MAX) + else { - if (ev->data3 < 0) + // Actual analog sticks + if (ev->data2 != INT32_MAX) { - // Up - gamekeydown[ev->device][KEY_AXIS1 + i + 2] = abs(ev->data3); - gamekeydown[ev->device][KEY_AXIS1 + i + 3] = 0; + if (ev->data2 < 0) + { + // Left + gamekeydown[ev->device][KEY_AXIS1 + (i * 4)] = abs(ev->data2); + gamekeydown[ev->device][KEY_AXIS1 + (i * 4) + 1] = 0; + } + else + { + // Right + gamekeydown[ev->device][KEY_AXIS1 + (i * 4)] = 0; + gamekeydown[ev->device][KEY_AXIS1 + (i * 4) + 1] = abs(ev->data2); + } } - else + + if (ev->data3 != INT32_MAX) { - // Down - gamekeydown[ev->device][KEY_AXIS1 + i + 2] = 0; - gamekeydown[ev->device][KEY_AXIS1 + i + 3] = abs(ev->data3); + if (ev->data3 < 0) + { + // Up + gamekeydown[ev->device][KEY_AXIS1 + (i * 4) + 2] = abs(ev->data3); + gamekeydown[ev->device][KEY_AXIS1 + (i * 4) + 3] = 0; + } + else + { + // Down + gamekeydown[ev->device][KEY_AXIS1 + (i * 4) + 2] = 0; + gamekeydown[ev->device][KEY_AXIS1 + (i * 4) + 3] = abs(ev->data3); + } } } break; @@ -316,76 +335,38 @@ static keyname_t keynames[] = {KEY_MOUSEWHEELUP, "Wheel Up"}, {KEY_MOUSEWHEELDOWN, "Wheel Down"}, - {KEY_JOY1+0, "JOY1"}, - {KEY_JOY1+1, "JOY2"}, - {KEY_JOY1+2, "JOY3"}, - {KEY_JOY1+3, "JOY4"}, - {KEY_JOY1+4, "JOY5"}, - {KEY_JOY1+5, "JOY6"}, - {KEY_JOY1+6, "JOY7"}, - {KEY_JOY1+7, "JOY8"}, - {KEY_JOY1+8, "JOY9"}, -#if !defined (NOMOREJOYBTN_1S) - // we use up to 32 buttons in DirectInput - {KEY_JOY1+9, "JOY10"}, - {KEY_JOY1+10, "JOY11"}, - {KEY_JOY1+11, "JOY12"}, - {KEY_JOY1+12, "JOY13"}, - {KEY_JOY1+13, "JOY14"}, - {KEY_JOY1+14, "JOY15"}, - {KEY_JOY1+15, "JOY16"}, - {KEY_JOY1+16, "JOY17"}, - {KEY_JOY1+17, "JOY18"}, - {KEY_JOY1+18, "JOY19"}, - {KEY_JOY1+19, "JOY20"}, - {KEY_JOY1+20, "JOY21"}, - {KEY_JOY1+21, "JOY22"}, - {KEY_JOY1+22, "JOY23"}, - {KEY_JOY1+23, "JOY24"}, - {KEY_JOY1+24, "JOY25"}, - {KEY_JOY1+25, "JOY26"}, - {KEY_JOY1+26, "JOY27"}, - {KEY_JOY1+27, "JOY28"}, - {KEY_JOY1+28, "JOY29"}, - {KEY_JOY1+29, "JOY30"}, - {KEY_JOY1+30, "JOY31"}, - {KEY_JOY1+31, "JOY32"}, -#endif + {KEY_JOY1+0, "A BUTTON"}, + {KEY_JOY1+1, "B BUTTON"}, + {KEY_JOY1+2, "X BUTTON"}, + {KEY_JOY1+3, "Y BUTTON"}, + {KEY_JOY1+4, "BACK BUTTON"}, + {KEY_JOY1+5, "GUIDE BUTTON"}, + {KEY_JOY1+6, "START BUTTON"}, + {KEY_JOY1+7, "L-STICK CLICK"}, + {KEY_JOY1+8, "R-STICK CLICK"}, + {KEY_JOY1+9, "L BUMPER"}, + {KEY_JOY1+10, "R BUMPER"}, + {KEY_JOY1+11, "D-PAD UP"}, + {KEY_JOY1+12, "D-PAD DOWN"}, + {KEY_JOY1+13, "D-PAD LEFT"}, + {KEY_JOY1+14, "D-PAD RIGHT"}, + {KEY_JOY1+15, "MISC. BUTTON"}, + {KEY_JOY1+16, "PADDLE1 BUTTON"}, + {KEY_JOY1+17, "PADDLE2 BUTTON"}, + {KEY_JOY1+18, "PADDLE3 BUTTON"}, + {KEY_JOY1+19, "PADDLE4 BUTTON"}, + {KEY_JOY1+20, "TOUCHPAD"}, - // the DOS version uses Allegro's joystick support - {KEY_HAT1+0, "HATUP"}, - {KEY_HAT1+1, "HATDOWN"}, - {KEY_HAT1+2, "HATLEFT"}, - {KEY_HAT1+3, "HATRIGHT"}, - {KEY_HAT1+4, "HATUP2"}, - {KEY_HAT1+5, "HATDOWN2"}, - {KEY_HAT1+6, "HATLEFT2"}, - {KEY_HAT1+7, "HATRIGHT2"}, - {KEY_HAT1+8, "HATUP3"}, - {KEY_HAT1+9, "HATDOWN3"}, - {KEY_HAT1+10, "HATLEFT3"}, - {KEY_HAT1+11, "HATRIGHT3"}, - {KEY_HAT1+12, "HATUP4"}, - {KEY_HAT1+13, "HATDOWN4"}, - {KEY_HAT1+14, "HATLEFT4"}, - {KEY_HAT1+15, "HATRIGHT4"}, - - {KEY_AXIS1+0, "AXISX-"}, - {KEY_AXIS1+1, "AXISX+"}, - {KEY_AXIS1+2, "AXISY-"}, - {KEY_AXIS1+3, "AXISY+"}, - {KEY_AXIS1+4, "AXISZ-"}, - {KEY_AXIS1+5, "AXISZ+"}, - {KEY_AXIS1+6, "AXISXRUDDER-"}, - {KEY_AXIS1+7, "AXISXRUDDER+"}, - {KEY_AXIS1+8, "AXISYRUDDER-"}, - {KEY_AXIS1+9, "AXISYRUDDER+"}, - {KEY_AXIS1+10, "AXISZRUDDER-"}, - {KEY_AXIS1+11, "AXISZRUDDER+"}, - {KEY_AXIS1+12, "AXISU-"}, - {KEY_AXIS1+13, "AXISU+"}, - {KEY_AXIS1+14, "AXISV-"}, - {KEY_AXIS1+15, "AXISV+"}, + {KEY_AXIS1+0, "L-STICK LEFT"}, + {KEY_AXIS1+1, "L-STICK RIGHT"}, + {KEY_AXIS1+2, "L-STICK UP"}, + {KEY_AXIS1+3, "L-STICK DOWN"}, + {KEY_AXIS1+4, "R-STICK LEFT"}, + {KEY_AXIS1+5, "R-STICK RIGHT"}, + {KEY_AXIS1+6, "R-STICK UP"}, + {KEY_AXIS1+7, "R-STICK DOWN"}, + {KEY_AXIS1+8, "L TRIGGER"}, + {KEY_AXIS1+9, "R TRIGGER"}, }; static const char *gamecontrolname[num_gamecontrols] = diff --git a/src/g_input.h b/src/g_input.h index ae9b130bc..14aa5aba3 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -23,9 +23,12 @@ #define NUMKEYS 256 #define MOUSEBUTTONS 8 -#define JOYBUTTONS 32 // 32 buttons -#define JOYHATS 4 // 4 hats -#define JOYAXISSET 4 // 4 Sets of 2 axises + +#define JOYBUTTONS 21 // 21 buttons, to match SDL_GameControllerButton +#define JOYANALOGS 2 // 2 sets of analog stick axes, with positive and negative each +#define JOYTRIGGERS 1 // 1 set of trigger axes, positive only +#define JOYAXISSETS (JOYANALOGS + JOYTRIGGERS) +#define JOYAXES ((4 * JOYANALOGS) + (2 * JOYTRIGGERS)) #define MAXINPUTMAPPING 4 @@ -35,9 +38,9 @@ typedef enum { KEY_JOY1 = NUMKEYS, - KEY_HAT1 = KEY_JOY1 + JOYBUTTONS, - KEY_AXIS1 = KEY_HAT1 + JOYHATS*4, - JOYINPUTEND = KEY_AXIS1 + JOYAXISSET*2*2, // 4 sets of 2 axes, each with positive & negative + KEY_HAT1 = KEY_JOY1 + 11, // macro for SDL_CONTROLLER_BUTTON_DPAD_UP + KEY_AXIS1 = KEY_JOY1 + JOYBUTTONS, + JOYINPUTEND = KEY_AXIS1 + JOYAXES, KEY_MOUSE1 = JOYINPUTEND, KEY_MOUSEMOVE = KEY_MOUSE1 + MOUSEBUTTONS, diff --git a/src/k_menufunc.c b/src/k_menufunc.c index f2b9133fb..fe62e82b5 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2418,10 +2418,14 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num) // this is to allow them to retain controller usage when they play alone. // Because let's face it, when you test mods, you're often lazy to grab your controller for menuing :) if (!i && !num) + { setup_player[num].ponedevice = cv_usejoystick[num].value; + } else if (num) + { // For any player past player 1, set controls to default profile controls, otherwise it's generally awful to do any menuing... memcpy(&gamecontrol[num], gamecontroldefault, sizeof(gamecontroldefault)); + } CV_SetValue(&cv_usejoystick[num], i); @@ -3132,7 +3136,9 @@ void M_CharacterSelectTick(void) // P1 is alone, set their old device just in case. if (setup_numplayers < 2 && setup_player[0].ponedevice) + { CV_StealthSetValue(&cv_usejoystick[0], setup_player[0].ponedevice); + } M_SetupNextMenu(&PLAY_MainDef, false); } @@ -5307,7 +5313,7 @@ void M_MapProfileControl(event_t *ev) #endif break; case ev_joystick: - if (ev->data1 >= JOYAXISSET) + if (ev->data1 >= JOYAXES) { #ifdef PARANOIA CONS_Debug(DBG_GAMELOGIC, "Bad joystick axis event %d\n", ev->data1); @@ -5320,65 +5326,58 @@ void M_MapProfileControl(event_t *ev) boolean responsivelr = ((ev->data2 != INT32_MAX) && (abs(ev->data2) >= deadzone)); boolean responsiveud = ((ev->data3 != INT32_MAX) && (abs(ev->data3) >= deadzone)); - // Only consider unambiguous assignment. - if (responsivelr == responsiveud) - return; + i = ev->data1; - i = (ev->data1 * 4); - - if (responsivelr) + if (i >= JOYANALOGS) { - if (ev->data2 < 0) + // The trigger axes are handled specially. + i -= JOYANALOGS; + + if (responsivelr) { - // Left - c = KEY_AXIS1 + i; + c = KEY_AXIS1 + (JOYANALOGS * 4) + (i * 2); } - else + else if (responsiveud) { - // Right - c = KEY_AXIS1 + i + 1; + c = KEY_AXIS1 + (JOYANALOGS * 4) + (i * 2) + 1; } } - else //if (responsiveud) + else { - if (ev->data3 < 0) + // Actual analog sticks + + // Only consider unambiguous assignment. + if (responsivelr == responsiveud) + return; + + if (responsivelr) { - // Up - c = KEY_AXIS1 + i + 2; + if (ev->data2 < 0) + { + // Left + c = KEY_AXIS1 + (i * 4); + } + else + { + // Right + c = KEY_AXIS1 + (i * 4) + 1; + } } - else + else //if (responsiveud) { - // Down - c = KEY_AXIS1 + i + 3; + if (ev->data3 < 0) + { + // Up + c = KEY_AXIS1 + (i * 4) + 2; + } + else + { + // Down + c = KEY_AXIS1 + (i * 4) + 3; + } } } } - - /* I hate this. - I shouldn't have to do this. - But we HAVE to because of some controllers, INCLUDING MINE. - - Triggers on X360 controllers go from -1024 when not held - To 1023 when pressed. - The result is that pressing the trigger makes the game THINK the input is for a negative value. - Which then means that the input is considered pressed when the trigger is released. - - So what we're going to do is make sure that the detected 'c' key is the same for multiple rolls of ev_joystick. - NOTE: We need the player to press the key all the way down otherwise this might just not work... - - @TODO: This isn't entirely consistent because we only check for events..... maybe check continuously for gamekeydown[]? - but this seems messy... - */ - - //CONS_Printf("mapping joystick ... attempt (%d)\n", optionsmenu.keyheldfor); - - if (c != optionsmenu.lastkey) - { - optionsmenu.lastkey = c; - optionsmenu.keyheldfor = 0; - return; - } - break; default: return; @@ -5411,7 +5410,8 @@ void M_MapProfileControl(event_t *ev) // If possible, reapply the profile... // 19/05/22: Actually, no, don't do that, it just fucks everything up in too many cases. - /*if (gamestate == GS_MENU) // In menu? Apply this to P1, no questions asked. + /* + if (gamestate == GS_MENU) // In menu? Apply this to P1, no questions asked. { // Apply the profile's properties to player 1 but keep the last profile cv to p1's ACTUAL profile to revert once we exit. UINT8 lastp = cv_lastprofile[0].value; @@ -5430,7 +5430,8 @@ void M_MapProfileControl(event_t *ev) break; } } - }*/ + } + */ } #undef KEYHOLDFOR diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 6ad02df07..a89dd649c 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -200,12 +200,12 @@ static char returnWadPath[256]; #include "../byteptr.h" #endif -void I_StoreExJoystick(SDL_Joystick *dev) +void I_StoreExJoystick(SDL_GameController *dev) { // ExJoystick is a massive hack to avoid needing to completely // rewrite pretty much all of the controller support from scratch... - // Used in favor of most instances of SDL_JoystickClose. + // Used in favor of most instances of SDL_GameControllerClose. // If a joystick would've been discarded, then save it in an array, // because we want it have it for the joystick input screen. @@ -217,12 +217,12 @@ void I_StoreExJoystick(SDL_Joystick *dev) return; } - index = SDL_JoystickInstanceID(dev); + index = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(dev)); if (index >= MAXGAMEPADS || index < 0) { // Not enough space to save this joystick, completely discard. - SDL_JoystickClose(dev); + SDL_GameControllerClose(dev); return; } @@ -235,7 +235,7 @@ void I_StoreExJoystick(SDL_Joystick *dev) if (ExJoystick[index] != NULL) { // Discard joystick in the old slot. - SDL_JoystickClose(ExJoystick[index]); + SDL_GameControllerClose(ExJoystick[index]); } // Keep for safe-keeping. @@ -267,7 +267,7 @@ static INT32 joystick_started[MAXSPLITSCREENPLAYERS] = {0,0,0,0}; /** \brief SDL info about joystick 1 */ SDLJoyInfo_t JoyInfo[MAXSPLITSCREENPLAYERS]; -SDL_Joystick *ExJoystick[MAXGAMEPADS]; +SDL_GameController *ExJoystick[MAXGAMEPADS]; SDL_bool consolevent = SDL_FALSE; SDL_bool framebuffer = SDL_FALSE; @@ -989,29 +989,16 @@ void I_JoyScale4(void) JoyInfo[3].scale = Joystick[3].bGamepadStyle?1:cv_joyscale[1].value; } -// Cheat to get the device index for a joystick handle -INT32 I_GetJoystickDeviceIndex(SDL_Joystick *dev) +// Cheat to get the device index for a game controller handle +INT32 I_GetJoystickDeviceIndex(SDL_GameController *dev) { - INT32 i, count = SDL_NumJoysticks(); + SDL_Joystick *joystick = NULL; - for (i = 0; dev && i < count; i++) + joystick = SDL_GameControllerGetJoystick(dev); + + if (joystick) { - SDL_Joystick *test = SDL_JoystickOpen(i); - if (test && test == dev) - return i; - else - { - UINT8 j; - - for (j = 0; j < MAXSPLITSCREENPLAYERS; j++) - { - if (JoyInfo[j].dev == test) - break; - } - - if (j == MAXSPLITSCREENPLAYERS) - I_StoreExJoystick(test); - } + return SDL_JoystickInstanceID(joystick); } return -1; @@ -1082,7 +1069,7 @@ void I_UpdateJoystickDeviceIndices(UINT8 excludePlayer) */ void I_ShutdownJoystick(UINT8 index) { - INT32 i, j; + INT32 i; event_t event; event.device = I_GetJoystickDeviceIndex(JoyInfo[index].dev); @@ -1093,23 +1080,13 @@ void I_ShutdownJoystick(UINT8 index) // emulate the up of all joystick buttons for (i = 0; i < JOYBUTTONS; i++) { - event.data1=KEY_JOY1+i; + event.data1 = KEY_JOY1+i; D_PostEvent(&event); } - // emulate the up of all joystick hats - for (i = 0; i < JOYHATS*4; i++) - { - for (j = 0; j < 4; j++) - { - event.data1 = KEY_HAT1 + (i * 4) + j; - D_PostEvent(&event); - } - } - // reset joystick position event.type = ev_joystick; - for (i = 0; i < JOYAXISSET; i++) + for (i = 0; i < JOYAXES; i++) { event.data1 = i; D_PostEvent(&event); @@ -1131,7 +1108,7 @@ void I_ShutdownJoystick(UINT8 index) */ static int joy_open(int playerIndex, int joyIndex) { - SDL_Joystick *newdev = NULL; + SDL_GameController *newdev = NULL; int num_joy = 0; if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) @@ -1140,6 +1117,12 @@ static int joy_open(int playerIndex, int joyIndex) return -1; } + if (SDL_WasInit(SDL_INIT_GAMECONTROLLER) == 0) + { + CONS_Printf(M_GetText("Game Controller subsystem not started\n")); + return -1; + } + if (joyIndex <= 0) return -1; @@ -1151,7 +1134,7 @@ static int joy_open(int playerIndex, int joyIndex) return -1; } - newdev = SDL_JoystickOpen(joyIndex-1); + newdev = SDL_GameControllerOpen(joyIndex-1); // Handle the edge case where the device <-> joystick index assignment can change due to hotplugging // This indexing is SDL's responsibility and there's not much we can do about it. @@ -1166,9 +1149,9 @@ static int joy_open(int playerIndex, int joyIndex) if (JoyInfo[playerIndex].dev) { if (JoyInfo[playerIndex].dev == newdev // same device, nothing to do - || (newdev == NULL && SDL_JoystickGetAttached(JoyInfo[playerIndex].dev))) // we failed, but already have a working device + || (newdev == NULL && SDL_GameControllerGetAttached(JoyInfo[playerIndex].dev))) // we failed, but already have a working device { - return JoyInfo[playerIndex].axises; + return SDL_CONTROLLER_AXIS_MAX; } // Else, we're changing devices, so send neutral joy events @@ -1185,29 +1168,12 @@ static int joy_open(int playerIndex, int joyIndex) } else { - CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick%d: %s\n"), playerIndex+1, SDL_JoystickName(JoyInfo[playerIndex].dev)); + CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick%d: %s\n"), playerIndex+1, SDL_GameControllerName(JoyInfo[playerIndex].dev)); - JoyInfo[playerIndex].axises = SDL_JoystickNumAxes(JoyInfo[playerIndex].dev); - if (JoyInfo[playerIndex].axises > JOYAXISSET*2) - JoyInfo[playerIndex].axises = JOYAXISSET*2; - - /* - if (joyaxes<2) - { - I_OutputMsg("Not enought axes?\n"); - return 0; - } - */ - - JoyInfo[playerIndex].buttons = SDL_JoystickNumButtons(JoyInfo[playerIndex].dev); - if (JoyInfo[playerIndex].buttons > JOYBUTTONS) - JoyInfo[playerIndex].buttons = JOYBUTTONS; - - JoyInfo[playerIndex].hats = SDL_JoystickNumHats(JoyInfo[playerIndex].dev); - if (JoyInfo[playerIndex].hats > JOYHATS) - JoyInfo[playerIndex].hats = JOYHATS; - - JoyInfo[playerIndex].balls = SDL_JoystickNumBalls(JoyInfo[playerIndex].dev); + JoyInfo[playerIndex].axises = SDL_CONTROLLER_AXIS_MAX; + JoyInfo[playerIndex].buttons = SDL_CONTROLLER_BUTTON_MAX; + JoyInfo[playerIndex].hats = 1; + JoyInfo[playerIndex].balls = 0; //JoyInfo[playerIndex].bGamepadStyle = !stricmp(SDL_JoystickName(JoyInfo[playerIndex].dev), "pad"); @@ -1220,7 +1186,7 @@ static int joy_open(int playerIndex, int joyIndex) // void I_InitJoystick(UINT8 index) { - SDL_Joystick *newjoy = NULL; + SDL_GameController *newcontroller = NULL; UINT8 i; //I_ShutdownJoystick(); @@ -1245,23 +1211,32 @@ void I_InitJoystick(UINT8 index) } } + if (SDL_WasInit(SDL_INIT_GAMECONTROLLER) == 0) + { + if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == -1) + { + CONS_Printf(M_GetText("Couldn't initialize gamepads: %s\n"), SDL_GetError()); + return; + } + } + if (cv_usejoystick[index].value) - newjoy = SDL_JoystickOpen(cv_usejoystick[index].value-1); + newcontroller = SDL_GameControllerOpen(cv_usejoystick[index].value-1); for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { if (i == index) continue; - if (JoyInfo[i].dev == newjoy) + if (JoyInfo[i].dev == newcontroller) break; } - if (newjoy && i < MAXSPLITSCREENPLAYERS) // don't override an active device + if (newcontroller && i < MAXSPLITSCREENPLAYERS) // don't override an active device { cv_usejoystick[index].value = I_GetJoystickDeviceIndex(JoyInfo[index].dev) + 1; } - else if (newjoy && joy_open(index, cv_usejoystick[index].value) != -1) + else if (newcontroller && joy_open(index, cv_usejoystick[index].value) != -1) { // SDL's device indexes are unstable, so cv_usejoystick may not match // the actual device index. So let's cheat a bit and find the device's current index. @@ -1278,14 +1253,14 @@ void I_InitJoystick(UINT8 index) for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { - if (JoyInfo[i].dev == newjoy) + if (JoyInfo[i].dev == newcontroller) break; } if (i == MAXSPLITSCREENPLAYERS) { // Joystick didn't end up being used - I_StoreExJoystick(newjoy); + I_StoreExJoystick(newcontroller); } } @@ -1320,6 +1295,13 @@ static void I_ShutdownInput(void) for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) I_ShutdownJoystick(i); + if (SDL_WasInit(SDL_INIT_GAMECONTROLLER) == SDL_INIT_GAMECONTROLLER) + { + CONS_Printf("Shutting down gamecontroller system\n"); + SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER); + I_OutputMsg("I_Joystick: SDL's Game Controller system has been shutdown\n"); + } + if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) { CONS_Printf("Shutting down joy system\n"); diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index aa40721e6..79b5473d0 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -770,9 +770,10 @@ static void Impl_HandleMouseWheelEvent(SDL_MouseWheelEvent evt) } } -static void Impl_HandleJoystickAxisEvent(SDL_JoyAxisEvent evt) +static void Impl_HandleControllerAxisEvent(SDL_ControllerAxisEvent evt) { event_t event; + INT32 value; event.type = ev_joystick; @@ -782,78 +783,32 @@ static void Impl_HandleJoystickAxisEvent(SDL_JoyAxisEvent evt) return; } - evt.axis++; event.data1 = event.data2 = event.data3 = INT32_MAX; //axis - if (evt.axis > JOYAXISSET*2) + if (evt.axis > 2 * JOYAXISSETS) { return; } //vaule[sic] - if (evt.axis % 2) + value = SDLJoyAxis(evt.value, evt.which); + + if (evt.axis & 1) { - event.data1 = evt.axis / 2; - event.data2 = SDLJoyAxis(evt.value, 0); // TODO: replace 0 with pid + event.data3 = value; } else { - evt.axis--; - event.data1 = evt.axis / 2; - event.data3 = SDLJoyAxis(evt.value, 0); // TODO: replace 0 with pid + event.data2 = value; } + event.data1 = evt.axis / 2; + D_PostEvent(&event); } -static void Impl_SendHatEvent(SDL_JoyHatEvent evt, UINT64 hatFlag, UINT8 keyOffset) -{ - event_t event; - - event.device = 1 + evt.which; - if (event.device == INT32_MAX) - { - return; - } - - event.data1 = KEY_HAT1 + keyOffset; - - if (evt.hat < JOYHATS) - { - event.data1 += (evt.hat * 4); - } - else - { - return; - } - - if (evt.value & hatFlag) - { - event.type = ev_keydown; - } - else - { - event.type = ev_keyup; - } - - SDLJoyRemap(&event); - - if (event.type != ev_console) - { - D_PostEvent(&event); - } -} - -static void Impl_HandleJoystickHatEvent(SDL_JoyHatEvent evt) -{ - Impl_SendHatEvent(evt, SDL_HAT_UP, 0); - Impl_SendHatEvent(evt, SDL_HAT_DOWN, 1); - Impl_SendHatEvent(evt, SDL_HAT_LEFT, 2); - Impl_SendHatEvent(evt, SDL_HAT_RIGHT, 3); -} - -static void Impl_HandleJoystickButtonEvent(SDL_JoyButtonEvent evt, Uint32 type) +static void Impl_HandleControllerButtonEvent(SDL_ControllerButtonEvent evt, Uint32 type) { event_t event; @@ -866,11 +821,11 @@ static void Impl_HandleJoystickButtonEvent(SDL_JoyButtonEvent evt, Uint32 type) event.data1 = KEY_JOY1; - if (type == SDL_JOYBUTTONUP) + if (type == SDL_CONTROLLERBUTTONUP) { event.type = ev_keyup; } - else if (type == SDL_JOYBUTTONDOWN) + else if (type == SDL_CONTROLLERBUTTONDOWN) { event.type = ev_keydown; } @@ -936,26 +891,23 @@ void I_GetEvent(void) case SDL_MOUSEWHEEL: Impl_HandleMouseWheelEvent(evt.wheel); break; - case SDL_JOYAXISMOTION: - Impl_HandleJoystickAxisEvent(evt.jaxis); + case SDL_CONTROLLERAXISMOTION: + Impl_HandleControllerAxisEvent(evt.caxis); break; - case SDL_JOYHATMOTION: - Impl_HandleJoystickHatEvent(evt.jhat); - break; - case SDL_JOYBUTTONUP: - case SDL_JOYBUTTONDOWN: - Impl_HandleJoystickButtonEvent(evt.jbutton, evt.type); + case SDL_CONTROLLERBUTTONUP: + case SDL_CONTROLLERBUTTONDOWN: + Impl_HandleControllerButtonEvent(evt.cbutton, evt.type); break; //////////////////////////////////////////////////////////// - case SDL_JOYDEVICEADDED: + case SDL_CONTROLLERDEVICEADDED: { // OH BOY are you in for a good time! #abominationstation - SDL_Joystick *newjoy = SDL_JoystickOpen(evt.jdevice.which); + SDL_GameController *newcontroller = SDL_GameControllerOpen(evt.cdevice.which); - CONS_Debug(DBG_GAMELOGIC, "Joystick device index %d added\n", evt.jdevice.which + 1); + CONS_Debug(DBG_GAMELOGIC, "Controller device index %d added\n", evt.cdevice.which + 1); //////////////////////////////////////////////////////////// // Because SDL's device index is unstable, we're going to cheat here a bit: @@ -970,7 +922,7 @@ void I_GetEvent(void) for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { - if (newjoy && (!JoyInfo[i].dev || !SDL_JoystickGetAttached(JoyInfo[i].dev))) + if (newcontroller && (!JoyInfo[i].dev || !SDL_GameControllerGetAttached(JoyInfo[i].dev))) { UINT8 j; @@ -979,14 +931,14 @@ void I_GetEvent(void) if (i == j) continue; - if (JoyInfo[j].dev == newjoy) + if (JoyInfo[j].dev == newcontroller) break; } if (j == MAXSPLITSCREENPLAYERS) { // ensures we aren't overriding a currently active device - cv_usejoystick[i].value = evt.jdevice.which + 1; + cv_usejoystick[i].value = evt.cdevice.which + 1; I_UpdateJoystickDeviceIndices(0); } } @@ -1028,21 +980,21 @@ void I_GetEvent(void) for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { - if (JoyInfo[i].dev == newjoy) + if (JoyInfo[i].dev == newcontroller) break; } if (i == MAXSPLITSCREENPLAYERS) - I_StoreExJoystick(newjoy); + I_StoreExJoystick(newcontroller); } break; //////////////////////////////////////////////////////////// - case SDL_JOYDEVICEREMOVED: + case SDL_CONTROLLERDEVICEREMOVED: for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { - if (JoyInfo[i].dev && !SDL_JoystickGetAttached(JoyInfo[i].dev)) + if (JoyInfo[i].dev && !SDL_GameControllerGetAttached(JoyInfo[i].dev)) { CONS_Debug(DBG_GAMELOGIC, "Joystick%d removed, device index: %d\n", i+1, JoyInfo[i].oldjoy); I_ShutdownJoystick(i); @@ -1146,8 +1098,8 @@ void I_OsPolling(void) if (consolevent) I_GetConsoleEvents(); - if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) - SDL_JoystickUpdate(); + if (SDL_WasInit(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) == (SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER)) + SDL_GameControllerUpdate(); I_GetEvent(); diff --git a/src/sdl/sdlmain.h b/src/sdl/sdlmain.h index 391614bcd..86d584632 100644 --- a/src/sdl/sdlmain.h +++ b/src/sdl/sdlmain.h @@ -41,8 +41,8 @@ extern SDL_bool framebuffer; */ typedef struct SDLJoyInfo_s { - /// Joystick handle - SDL_Joystick *dev; + /// Controller handle + SDL_GameController *dev; /// number of old joystick int oldjoy; /// number of axies @@ -58,12 +58,12 @@ typedef struct SDLJoyInfo_s } SDLJoyInfo_t; -/** \brief SDL info about joysticks +/** \brief SDL info about controllers */ extern SDLJoyInfo_t JoyInfo[MAXSPLITSCREENPLAYERS]; -extern SDL_Joystick *ExJoystick[MAXGAMEPADS]; +extern SDL_GameController *ExJoystick[MAXGAMEPADS]; -void I_StoreExJoystick(SDL_Joystick *dev); +void I_StoreExJoystick(SDL_GameController *dev); /** \brief joystick axis deadzone */ @@ -75,8 +75,8 @@ void I_GetConsoleEvents(void); // So we can call this from i_video event loop void I_ShutdownJoystick(UINT8 index); -// Cheat to get the device index for a joystick handle -INT32 I_GetJoystickDeviceIndex(SDL_Joystick *dev); +// Cheat to get the device index for a game controller handle +INT32 I_GetJoystickDeviceIndex(SDL_GameController *dev); // Quick thing to make SDL_JOYDEVICEADDED events less of an abomination void I_UpdateJoystickDeviceIndex(UINT8 player); From 6697fec70e416f4bd700257d9e719ee244a04b7e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 27 Aug 2022 22:47:11 -0400 Subject: [PATCH 309/379] These cvars were removed --- src/k_menudef.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/k_menudef.c b/src/k_menudef.c index fc9439e14..b0d3fe54e 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -825,9 +825,6 @@ menuitem_t OPTIONS_VideoOGL[] = {IT_SPACE | IT_NOTHING, NULL, NULL, NULL, {NULL}, 0, 0}, - {IT_STRING | IT_CVAR, "Wall Contrast Style", "Allows faking or not Software wall colour contrast.", - NULL, {.cvar = &cv_glfakecontrast}, 0, 0}, - {IT_STRING | IT_CVAR, "Sprite Billboarding", "Adjusts sprites when viewed from above or below to not make them appear flat.", NULL, {.cvar = &cv_glspritebillboarding}, 0, 0}, @@ -1028,7 +1025,7 @@ menuitem_t OPTIONS_Gameplay[] = NULL, {.cvar = &cv_kartspeed}, 0, 0}, {IT_STRING | IT_CVAR, "Base Lap Count", "Change how many laps must be completed per race.", - NULL, {.cvar = &cv_basenumlaps}, 0, 0}, + NULL, {.cvar = &cv_numlaps}, 0, 0}, {IT_STRING | IT_CVAR, "Frantic Items", "Make item odds crazier with more powerful items!", NULL, {.cvar = &cv_kartfrantic}, 0, 0}, From 6e5c9555014a6ef15e963047d3d1a19efce0f127 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 28 Aug 2022 06:08:30 -0400 Subject: [PATCH 310/379] Only allow Time Attack for 1P Battle gametype submenu no longer exists. --- src/k_menu.h | 5 ++++- src/k_menudef.c | 30 +++++++++--------------------- src/k_menudraw.c | 25 ++++++++++++++++++++++--- src/k_menufunc.c | 41 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 25 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index eb6e166cc..86d0d2bfe 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -187,7 +187,7 @@ extern menu_t PLAY_CharSelectDef; extern menuitem_t PLAY_MainMenu[]; extern menu_t PLAY_MainDef; -extern menuitem_t PLAY_Gamemodes[]; +extern menuitem_t PLAY_GamemodesMenu[]; extern menu_t PLAY_GamemodesDef; extern menuitem_t PLAY_RaceGamemodesMenu[]; @@ -617,6 +617,9 @@ boolean M_CharacterSelectHandler(INT32 choice); void M_CharacterSelectTick(void); boolean M_CharacterSelectQuit(void); +void M_SetupGametypeMenu(INT32 choice); +void M_SetupRaceMenu(INT32 choice); + #define CUPMENU_COLUMNS 7 #define CUPMENU_ROWS 2 #define CUPMENU_CURSORID (cupgrid.x + (cupgrid.y * CUPMENU_COLUMNS) + (cupgrid.pageno * (CUPMENU_COLUMNS * CUPMENU_ROWS))) diff --git a/src/k_menudef.c b/src/k_menudef.c index b0d3fe54e..e62a8c0e0 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -73,8 +73,8 @@ menu_t PLAY_CharSelectDef = { menuitem_t PLAY_MainMenu[] = { - {IT_STRING | IT_SUBMENU, "Local Play", "Play only on this computer.", - NULL, {.submenu = &PLAY_GamemodesDef}, 0, 0}, + {IT_STRING | IT_CALL, "Local Play", "Play only on this computer.", + NULL, {.routine = M_SetupGametypeMenu}, 0, 0}, {IT_STRING | IT_CALL, "Online", "Connect to other computers.", NULL, {.routine = M_MPOptSelectInit}, /*M_MPRoomSelectInit,*/ 0, 0}, @@ -86,11 +86,14 @@ menu_t PLAY_MainDef = KARTGAMEMODEMENU(PLAY_MainMenu, &PLAY_CharSelectDef); menuitem_t PLAY_GamemodesMenu[] = { - {IT_STRING | IT_SUBMENU, "Race", "A contest to see who's the fastest of them all!", - NULL, {.submenu = &PLAY_RaceGamemodesDef}, 0, 0}, + {IT_STRING | IT_CALL, "Race", "A contest to see who's the fastest of them all!", + NULL, {.routine = M_SetupRaceMenu}, 0, 0}, - {IT_STRING | IT_SUBMENU, "Battle", "Sharpen your item usage in these special Battle zones!", - NULL, {.submenu = &PLAY_BattleGamemodesDef}, 0, 0}, + {IT_STRING | IT_CALL, "Battle", "It's last hedgehog standing in this free-for-all!", + "MENIMG00", {.routine = M_LevelSelectInit}, 0, GT_BATTLE}, + + {IT_STRING | IT_CALL, "Capsules", "Bust up all of the capsules in record time!", + NULL, {.routine = M_LevelSelectInit}, 1, GT_BATTLE}, {IT_STRING | IT_CALL, "Back", NULL, NULL, {.routine = M_GoBack}, 0, 0}, }; @@ -313,21 +316,6 @@ menu_t PLAY_TAGhostsDef = { NULL }; -// BATTLE - -menuitem_t PLAY_BattleGamemodesMenu[] = -{ - {IT_STRING | IT_CALL, "Survival", "It's last hedgehog standing in this free-for-all!", - "MENIMG00", {.routine = M_LevelSelectInit}, 0, GT_BATTLE}, - - {IT_STRING | IT_CALL, "Time Attack", "Bust up all of the capsules in record time!", - NULL, {.routine = M_LevelSelectInit}, 1, GT_BATTLE}, - - {IT_STRING | IT_CALL, "Back", NULL, NULL, {.routine = M_GoBack}, 0, 0}, -}; - -menu_t PLAY_BattleGamemodesDef = KARTGAMEMODEMENU(PLAY_BattleGamemodesMenu, &PLAY_GamemodesDef); - // MULTIPLAYER OPTION SELECT menuitem_t PLAY_MP_OptSelect[] = { diff --git a/src/k_menudraw.c b/src/k_menudraw.c index ce4f93e49..f83f5a455 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -655,8 +655,22 @@ void M_DrawGenericMenu(void) // void M_DrawKartGamemodeMenu(void) { - UINT8 n = currentMenu->numitems-1; - INT32 i, x = GM_STARTX - ((GM_XOFFSET / 2) * (n-1)), y = GM_STARTY - ((GM_YOFFSET / 2) * (n-1)); + UINT8 n = 0; + INT32 i, x, y; + + for (i = 0; i < currentMenu->numitems; i++) + { + if (currentMenu->menuitems[i].status == IT_DISABLED) + { + continue; + } + + n++; + } + + n--; + x = GM_STARTX - ((GM_XOFFSET / 2) * (n-1)); + y = GM_STARTY - ((GM_YOFFSET / 2) * (n-1)); M_DrawMenuTooltips(); @@ -667,7 +681,12 @@ void M_DrawKartGamemodeMenu(void) for (i = 0; i < currentMenu->numitems; i++) { - if (i >= n) + if (currentMenu->menuitems[i].status == IT_DISABLED) + { + continue; + } + + if (i >= currentMenu->numitems-1) { x = GM_STARTX + (GM_XOFFSET * 5 / 2); y = GM_STARTY + (GM_YOFFSET * 5 / 2); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index fe62e82b5..410f65817 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3156,6 +3156,47 @@ boolean M_CharacterSelectQuit(void) return true; } +void M_SetupGametypeMenu(INT32 choice) +{ + (void)choice; + + PLAY_GamemodesDef.prevMenu = currentMenu; + + if (cv_splitplayers.value <= 1) + { + // Remove Battle, add Capsules + PLAY_GamemodesMenu[1].status = IT_DISABLED; + PLAY_GamemodesMenu[2].status = IT_STRING | IT_CALL; + } + else + { + // Add Battle, remove Capsules + PLAY_GamemodesMenu[1].status = IT_STRING | IT_CALL; + PLAY_GamemodesMenu[2].status = IT_DISABLED; + } + + M_SetupNextMenu(&PLAY_GamemodesDef, false); +} + +void M_SetupRaceMenu(INT32 choice) +{ + (void)choice; + + PLAY_RaceGamemodesDef.prevMenu = currentMenu; + + // Time Attack is 1P only + if (cv_splitplayers.value <= 1) + { + PLAY_RaceGamemodesMenu[2].status = IT_STRING | IT_CALL; + } + else + { + PLAY_RaceGamemodesMenu[2].status = IT_DISABLED; + } + + M_SetupNextMenu(&PLAY_RaceGamemodesDef, false); +} + // DIFFICULTY SELECT void M_SetupDifficultySelect(INT32 choice) From 196697f5445675b0058c03ea60f5e2cb78a2090e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 28 Aug 2022 06:48:43 -0400 Subject: [PATCH 311/379] Fix Time Attack exiting The menu option to exit the game now works in Time Attack, and the run properly gets ended at the end of intermission --- src/k_menufunc.c | 173 ++++++++++++++--------------------------------- src/y_inter.c | 49 +++++++------- 2 files changed, 72 insertions(+), 150 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 410f65817..35bdce6e2 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -140,15 +140,6 @@ static void Dummystaff_OnChange(void); consvar_t cv_showfocuslost = CVAR_INIT ("showfocuslost", "Yes", CV_SAVE, CV_YesNo, NULL); -#if 0 -static CV_PossibleValue_t map_cons_t[] = { - {0,"MIN"}, - {NUMMAPS, "MAX"}, - {0, NULL} -}; -consvar_t cv_nextmap = CVAR_INIT ("nextmap", "1", CV_HIDEN|CV_CALL, map_cons_t, Nextmap_OnChange); -#endif - static CV_PossibleValue_t skins_cons_t[MAXSKINS+1] = {{1, DEFAULTSKIN}}; consvar_t cv_chooseskin = CVAR_INIT ("chooseskin", DEFAULTSKIN, CV_HIDDEN, skins_cons_t, NULL); @@ -215,7 +206,7 @@ consvar_t cv_dummyprofilekickstart = CVAR_INIT ("dummyprofilekickstart", "Off", consvar_t cv_dummygpdifficulty = CVAR_INIT ("dummygpdifficulty", "Normal", CV_HIDDEN, dummygpdifficulty_cons_t, NULL); consvar_t cv_dummykartspeed = CVAR_INIT ("dummykartspeed", "Auto", CV_HIDDEN, dummykartspeed_cons_t, NULL); -consvar_t cv_dummygpencore = CVAR_INIT ("dummygpdifficulty", "No", CV_HIDDEN, CV_YesNo, NULL); +consvar_t cv_dummygpencore = CVAR_INIT ("dummygpencore", "No", CV_HIDDEN, CV_YesNo, NULL); static CV_PossibleValue_t dummymatchbots_cons_t[] = { {0, "Off"}, @@ -228,10 +219,13 @@ static CV_PossibleValue_t dummymatchbots_cons_t[] = { {7, "Lv.7"}, {8, "Lv.8"}, {9, "Lv.9"}, + {10, "Lv.10"}, + {11, "Lv.11"}, + {12, "Lv.12"}, + {13, "Lv.MAX"}, {0, NULL} }; - -consvar_t cv_dummymatchbots = CVAR_INIT ("dummymatchbots", "Off", CV_HIDDEN|CV_SAVE, dummymatchbots_cons_t, NULL); // Save this one if you wanna test your stuff without bots for instance +consvar_t cv_dummymatchbots = CVAR_INIT ("dummymatchbots", "Off", CV_HIDDEN, dummymatchbots_cons_t, NULL); // for server fetch threads... M_waiting_mode_t m_waiting_mode = M_NOT_WAITING; @@ -241,101 +235,6 @@ M_waiting_mode_t m_waiting_mode = M_NOT_WAITING; // ========================================================================== // (there's only a couple anyway) -#if 0 -// Nextmap. Used for Time Attack. -static void Nextmap_OnChange(void) -{ - char *leveltitle; - - // 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; - UINT8 active; - - 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); - } -} -#endif - static void Dummymenuplayer_OnChange(void) { if (cv_dummymenuplayer.value < 1) @@ -3207,7 +3106,7 @@ void M_SetupDifficultySelect(INT32 choice) // setup the difficulty menu and then remove choices depending on choice PLAY_RaceDifficultyDef.prevMenu = currentMenu; - PLAY_RaceDifficulty[0].status = IT_STRING|IT_CVAR; + PLAY_RaceDifficulty[0].status = IT_DISABLED; PLAY_RaceDifficulty[1].status = IT_DISABLED; PLAY_RaceDifficulty[2].status = IT_DISABLED; PLAY_RaceDifficulty[3].status = IT_DISABLED; @@ -3217,6 +3116,7 @@ void M_SetupDifficultySelect(INT32 choice) if (choice) // Match Race { + PLAY_RaceDifficulty[1].status = IT_STRING|IT_CVAR; // Kart Speed PLAY_RaceDifficulty[2].status = IT_STRING2|IT_CVAR; // CPUs on/off use string2 to signify not to use the normal gm font drawer PLAY_RaceDifficulty[3].status = IT_STRING2|IT_CVAR; // Encore on/off use string2 to signify not to use the normal gm font drawer PLAY_RaceDifficulty[5].status = IT_STRING|IT_CALL; // Level Select (Match Race) @@ -3225,6 +3125,7 @@ void M_SetupDifficultySelect(INT32 choice) } else // GP { + PLAY_RaceDifficulty[0].status = IT_STRING|IT_CVAR; // Difficulty PLAY_RaceDifficulty[3].status = IT_STRING2|IT_CVAR; // Encore on/off use string2 to signify not to use the normal gm font drawer PLAY_RaceDifficulty[4].status = IT_STRING|IT_CALL; // Level Select (GP) PLAY_RaceDifficultyDef.lastOn = 4; // Select cup select by default. @@ -3699,17 +3600,16 @@ void M_LevelSelectHandler(INT32 choice) multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... netgame = levellist.netgame; // ^ ditto. - // this is considered to be CV_CHEAT however... - CV_StealthSet(&cv_kartbot, cv_dummymatchbots.string); // Match the kartbot value to the dummy match bots value. - - if (netgame) // check for the dummy kartspeed value - CV_StealthSet(&cv_kartspeed, cv_dummykartspeed.string); - + CV_StealthSet(&cv_kartbot, cv_dummymatchbots.string); + CV_StealthSet(&cv_kartspeed, cv_dummykartspeed.string); D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_dummygpencore.value == 1), 1, 1, false, false); } - else // directly do the map change + else + { + // directly do the map change D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false); + } M_ClearMenus(true); } @@ -3761,13 +3661,22 @@ void M_StartTimeAttack(INT32 choice) char *gpath; const size_t glen = strlen("media")+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1; char nameofdemo[256]; - (void)choice; - emeralds = 0; - modeattacking = ATTACKING_TIME; + (void)choice; + + switch (levellist.newgametype) + { + case GT_BATTLE: + modeattacking = ATTACKING_CAPSULES; + break; + default: + modeattacking = ATTACKING_TIME; + break; + } // Still need to reset devmode cv_debug = 0; + emeralds = 0; if (demo.playback) G_StopDemo(); @@ -5782,9 +5691,17 @@ boolean M_ExtrasInputs(INT32 ch) // ===================== void M_EndModeAttackRun(void) { -#if 0 - M_ModeAttackEndGame(0); -#endif + G_CheckDemoStatus(); // Cancel recording + + if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) + Command_ExitGame_f(); + + M_StartControlPanel(); + currentMenu = &PLAY_TimeAttackDef; + itemOn = currentMenu->lastOn; + G_SetGamestate(GS_MENU); + S_ChangeMusicInternal("menu", true); + modeattacking = ATTACKING_NONE; } struct pausemenu_s pausemenu; @@ -5930,10 +5847,18 @@ void M_ConfirmEnterGame(INT32 choice) static void M_ExitGameResponse(INT32 ch) { - if (ch == MA_YES) - G_SetExitGameFlag(); + if (ch != MA_YES) + return; - M_ClearMenus(true); + if (modeattacking) + { + M_EndModeAttackRun(); + } + else + { + G_SetExitGameFlag(); + M_ClearMenus(true); + } } void M_EndGame(INT32 choice) diff --git a/src/y_inter.c b/src/y_inter.c index 905791aef..b005f1f01 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -575,49 +575,46 @@ skiptallydrawer: if (!LUA_HudEnabled(hud_intermissionmessages)) return; - if (timer && grandprixinfo.gp == false && bossinfo.boss == false) + if (timer && grandprixinfo.gp == false && bossinfo.boss == false && !modeattacking) { - if (timer && grandprixinfo.gp == false) - { - char *string; - INT32 tickdown = (timer+1)/TICRATE; + char *string; + INT32 tickdown = (timer+1)/TICRATE; - if (multiplayer && demo.playback) - string = va("Replay ends in %d", tickdown); - else - string = va("%s starts in %d", cv_advancemap.string, tickdown); + if (multiplayer && demo.playback) + string = va("Replay ends in %d", tickdown); + else + string = va("%s starts in %d", cv_advancemap.string, tickdown); - V_DrawCenteredString(BASEVIDWIDTH/2, 188, hilicol, - string); - } + V_DrawCenteredString(BASEVIDWIDTH/2, 188, hilicol, string); if ((demo.recording || demo.savemode == DSM_SAVED) && !demo.playback) + { switch (demo.savemode) { - case DSM_NOTSAVING: - V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|hilicol, "Look Backward: Save replay"); - break; + case DSM_NOTSAVING: + V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|hilicol, "(B): Save replay"); + break; - case DSM_SAVED: - V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|hilicol, "Replay saved!"); - break; + case DSM_SAVED: + V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|hilicol, "Replay saved!"); + break; - case DSM_TITLEENTRY: - ST_DrawDemoTitleEntry(); - break; + case DSM_TITLEENTRY: + ST_DrawDemoTitleEntry(); + break; - default: // Don't render any text here - break; + default: // Don't render any text here + break; } + } //if ((intertic/TICRATE) & 1) // Make it obvious that scrambling is happening next round. (OR NOT, I GUESS) //{ - /*if (cv_scrambleonchange.value && cv_teamscramble.value) - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, hilicol, M_GetText("Teams will be scrambled next round!"));*/ - if (speedscramble != -1 && speedscramble != gamespeed) + { V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24, hilicol|V_ALLOWLOWERCASE|V_SNAPTOBOTTOM, va(M_GetText("Next race will be %s Speed!"), kartspeed_cons_t[1+speedscramble].strvalue)); + } //} } From 74449ba55e2c5906220a8c6986c417a009018341 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 28 Aug 2022 06:53:06 -0400 Subject: [PATCH 312/379] Spacing --- src/k_menufunc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 35bdce6e2..083180453 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -5697,10 +5697,13 @@ void M_EndModeAttackRun(void) Command_ExitGame_f(); M_StartControlPanel(); + currentMenu = &PLAY_TimeAttackDef; itemOn = currentMenu->lastOn; + G_SetGamestate(GS_MENU); S_ChangeMusicInternal("menu", true); + modeattacking = ATTACKING_NONE; } From 88fa175a492c2df9d53fd0396fea8e392fe3e7a1 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 28 Aug 2022 07:47:26 -0400 Subject: [PATCH 313/379] Only ask for profile on startup once per session Fixes Time Attack ending resetting you to the Guest profile, and makes going back to the title screen slightly less frustrating --- src/k_menufunc.c | 51 ++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 083180453..010e527cc 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -931,35 +931,44 @@ void M_StartControlPanel(void) menuactive = true; - if (demo.playback) + if (!Playing()) { - currentMenu = &PAUSE_PlaybackMenuDef; - } - else if (!Playing()) - { - // we need to do this before setting ApplyProfile otherwise funky things are going to happen. - currentMenu = &MAIN_ProfilesDef; - optionsmenu.profilen = cv_ttlprofilen.value; + if (cv_currprofile.value == -1) // Only ask once per session. + { + // we need to do this before setting ApplyProfile otherwise funky things are going to happen. + currentMenu = &MAIN_ProfilesDef; + optionsmenu.profilen = cv_ttlprofilen.value; - // options don't need initializing here. - PR_ApplyProfile(0, 0); // apply guest profile to player 0 by default. - // this is to garantee that the controls aren't fucked up. + // options don't need initializing here. + PR_ApplyProfile(0, 0); // apply guest profile to player 0 by default. + // this is to garantee that the controls aren't fucked up. - // make sure we don't overstep that. - if (optionsmenu.profilen > PR_GetNumProfiles()) - optionsmenu.profilen = PR_GetNumProfiles(); - else if (optionsmenu.profilen < 0) - optionsmenu.profilen = 0; + // make sure we don't overstep that. + if (optionsmenu.profilen > PR_GetNumProfiles()) + optionsmenu.profilen = PR_GetNumProfiles(); + else if (optionsmenu.profilen < 0) + optionsmenu.profilen = 0; - itemOn = 0; + itemOn = 0; - CV_StealthSetValue(&cv_currprofile, -1); // Make sure to reset that as it is set by PR_ApplyProfile which we kind of hack together to force it. - CV_StealthSetValue(&cv_splitdevice, 0); // Disable this option by default. + CV_StealthSetValue(&cv_currprofile, -1); // Make sure to reset that as it is set by PR_ApplyProfile which we kind of hack together to force it. + CV_StealthSetValue(&cv_splitdevice, 0); // Disable this option by default. + } + else + { + currentMenu = &MainDef; + } } else { - // For now let's just always open the same pause menu. - M_OpenPauseMenu(); + if (demo.playback) + { + currentMenu = &PAUSE_PlaybackMenuDef; + } + else + { + M_OpenPauseMenu(); + } } CON_ToggleOff(); // move away console From c292009ebd1fbb3e30e425193e69733654914662 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 28 Aug 2022 08:04:50 -0400 Subject: [PATCH 314/379] Fix human vs bot P_CheckRacers conditions --- src/d_clisrv.c | 1 + src/p_inter.c | 98 ++++++++++++++++++++++++++------------------------ 2 files changed, 52 insertions(+), 47 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 3defba64f..76e3baa4d 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3500,6 +3500,7 @@ static void Got_AddBot(UINT8 **p, INT32 playernum) players[newplayernum].splitscreenindex = 0; players[newplayernum].bot = true; players[newplayernum].botvars.difficulty = difficulty; + players[newplayernum].lives = 9; players[newplayernum].skincolor = skins[skinnum].prefcolor; sprintf(player_names[newplayernum], "%s", skins[skinnum].realname); diff --git a/src/p_inter.c b/src/p_inter.c index 0974e11d7..ccb4baca0 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -786,73 +786,76 @@ void P_CheckPointLimit(void) // Checks whether or not to end a race netgame. boolean P_CheckRacers(void) { - UINT8 i; - UINT8 numplayersingame = 0; - UINT8 numexiting = 0; - boolean eliminatelast = cv_karteliminatelast.value; - boolean everyonedone = true; - boolean eliminatebots = false; const boolean griefed = (spectateGriefed > 0); + boolean eliminateLast = cv_karteliminatelast.value; + boolean allHumansDone = true; + //boolean allBotsDone = true; + + UINT8 numPlaying = 0; + UINT8 numExiting = 0; + UINT8 numHumans = 0; + UINT8 numBots = 0; + + UINT8 i; + // Check if all the players in the race have finished. If so, end the level. for (i = 0; i < MAXPLAYERS; i++) { - if (!playeringame[i] || players[i].spectator || players[i].lives <= 0) // Not playing + if (!playeringame[i] || players[i].spectator || players[i].lives <= 0) { // Y'all aren't even playing continue; } - numplayersingame++; + numPlaying++; + + if (players[i].bot) + { + numBots++; + } + else + { + numHumans++; + } if (players[i].exiting || (players[i].pflags & PF_NOCONTEST)) { - numexiting++; + numExiting++; } else { if (players[i].bot) { - // Isn't a human, thus doesn't matter. (Sorry, robots.) - // Set this so that we can check for bots that need to get eliminated, though! - eliminatebots = true; - continue; + //allBotsDone = false; + } + else + { + allHumansDone = false; } - - everyonedone = false; } } - // If we returned here with bots left, then the last place bot may have a chance to finish the map and NOT get time over. - // Not that it affects anything, they didn't make the map take longer or even get any points from it. But... it's a bit inconsistent! - // So if there's any bots, we'll let the game skip this, continue onto calculating eliminatelast, THEN we return true anyway. - if (everyonedone && !eliminatebots) - { - // Everyone's finished, we're done here! - racecountdown = exitcountdown = 0; - return true; - } - - if (numplayersingame <= 1) + if (numPlaying <= 1) { // Never do this without enough players. - eliminatelast = false; + eliminateLast = false; } else { - if (grandprixinfo.gp == true) - { - // Always do this in GP - eliminatelast = true; - } - else if (griefed) + if (griefed == true) { // Don't do this if someone spectated - eliminatelast = false; + eliminateLast = false; + } + else if (grandprixinfo.gp == true) + { + // Always do this in GP + eliminateLast = true; } } - if (eliminatelast == true && (numexiting >= numplayersingame-1)) + if (eliminateLast == true && (numExiting >= numPlaying-1)) { // Everyone's done playing but one guy apparently. // Just kill everyone who is still playing. @@ -879,9 +882,10 @@ boolean P_CheckRacers(void) return true; } - if (everyonedone) + if (numHumans > 0 && allHumansDone == true) { - // See above: there might be bots that are still going, but all players are done, so we can exit now. + // There might be bots that are still going, + // but all of the humans are done, so we can exit now. racecountdown = exitcountdown = 0; return true; } @@ -889,22 +893,21 @@ boolean P_CheckRacers(void) // SO, we're not done playing. // Let's see if it's time to start the death counter! - if (!racecountdown) + if (racecountdown == 0) { // If the winners are all done, then start the death timer. - UINT8 winningpos = 1; + UINT8 winningPos = max(1, numPlaying / 2); - winningpos = max(1, numplayersingame/2); - if (numplayersingame % 2) // any remainder? + if (numPlaying % 2) // Any remainder? Then round up. { - winningpos++; + winningPos++; } - if (numexiting >= winningpos) + if (numExiting >= winningPos) { tic_t countdown = 30*TICRATE; // 30 seconds left to finish, get going! - if (netgame) + if (K_CanChangeRules() == true) { // Custom timer countdown = cv_countdowntime.value * TICRATE; @@ -914,13 +917,14 @@ boolean P_CheckRacers(void) } } - // We're still playing, but no one else is, so we need to reset spectator griefing. - if (numplayersingame <= 1) + // We're still playing, but no one else is, + // so we need to reset spectator griefing. + if (numPlaying <= 1) { spectateGriefed = 0; } - // Turns out we're still having a good time & playing the game, we didn't have to do anything :) + // We are still having fun and playing the game :) return false; } From c4ff86a7f3a131409240d93e571075f58c3534bc Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 29 Aug 2022 01:08:33 -0400 Subject: [PATCH 315/379] Scrolling text fixes - Interpolates properly - Uses additive and subtractive because it exists now --- src/k_menudraw.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index f83f5a455..a3940f0cb 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -193,29 +193,29 @@ void M_DrawMenuBackground(void) V_DrawFixedPatch(-bgImageScroll, 0, FRACUNIT, 0, W_CachePatchName("MENUBG1", PU_CACHE), NULL); V_DrawFixedPatch(-bgImageScroll, 0, FRACUNIT, 0, W_CachePatchName(bgImageName, PU_CACHE), NULL); - V_DrawFixedPatch(0, (BASEVIDHEIGHT + 16) * FRACUNIT, FRACUNIT, V_TRANSLUCENT, W_CachePatchName("MENUBG2", PU_CACHE), NULL); - - V_DrawFixedPatch(-bgText2Scroll, (BASEVIDHEIGHT-8) * FRACUNIT, - FRACUNIT, V_TRANSLUCENT, text2, NULL); - V_DrawFixedPatch(-bgText2Scroll + text2loop, (BASEVIDHEIGHT-8) * FRACUNIT, - FRACUNIT, V_TRANSLUCENT, text2, NULL); + V_DrawFixedPatch(0, (BASEVIDHEIGHT + 16) * FRACUNIT, FRACUNIT, V_SUBTRACT, W_CachePatchName("MENUBG2", PU_CACHE), NULL); V_DrawFixedPatch(8 * FRACUNIT, -bgText1Scroll, - FRACUNIT, V_TRANSLUCENT, text1, NULL); + FRACUNIT, V_SUBTRACT, text1, NULL); V_DrawFixedPatch(8 * FRACUNIT, -bgText1Scroll + text1loop, - FRACUNIT, V_TRANSLUCENT, text1, NULL); + FRACUNIT, V_SUBTRACT, text1, NULL); - bgText1Scroll += (MENUBG_TEXTSCROLL*rendertimefrac); + bgText1Scroll += (MENUBG_TEXTSCROLL*renderdeltatics); while (bgText1Scroll > text1loop) bgText1Scroll -= text1loop; - bgText2Scroll += (MENUBG_TEXTSCROLL*rendertimefrac); + V_DrawFixedPatch(-bgText2Scroll, (BASEVIDHEIGHT-8) * FRACUNIT, + FRACUNIT, V_ADD, text2, NULL); + V_DrawFixedPatch(-bgText2Scroll + text2loop, (BASEVIDHEIGHT-8) * FRACUNIT, + FRACUNIT, V_ADD, text2, NULL); + + bgText2Scroll += (MENUBG_TEXTSCROLL*renderdeltatics); while (bgText2Scroll > text2loop) bgText2Scroll -= text2loop; if (bgImageScroll > 0) { - bgImageScroll -= (MENUBG_IMAGESCROLL*rendertimefrac); + bgImageScroll -= (MENUBG_IMAGESCROLL*renderdeltatics); if (bgImageScroll < 0) { bgImageScroll = 0; From 4f9fc51b80fc114a500e3f714b69b14697731714 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 29 Aug 2022 02:01:37 -0400 Subject: [PATCH 316/379] Use circle code for followers Better than list with only 3 entries, will get further improved by categories later --- src/k_menudraw.c | 307 ++++++++++++++++++++++------------------------- 1 file changed, 142 insertions(+), 165 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index a3940f0cb..b93ff768e 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -841,15 +841,31 @@ void M_DrawImageDef(void) static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) { angle_t angamt = ANGLE_MAX; - UINT16 i, numoptions; + UINT16 i, numoptions = 0; UINT16 l = 0, r = 0; - if (p->mdepth == CSSTEP_ALTS) - numoptions = setup_chargrid[p->gridx][p->gridy].numskins; - else if (p->mdepth == CSSTEP_FOLLOWERCOLORS) - numoptions = nummenucolors+2; - else - numoptions = nummenucolors; + switch (p->mdepth) + { + case CSSTEP_ALTS: + numoptions = setup_chargrid[p->gridx][p->gridy].numskins; + break; + case CSSTEP_COLORS: + numoptions = nummenucolors; + break; + case CSSTEP_FOLLOWER: + numoptions = numfollowers+1; + break; + case CSSTEP_FOLLOWERCOLORS: + numoptions = nummenucolors+2; + break; + default: + return; + } + + if (numoptions == 0) + { + return; + } angamt /= numoptions; @@ -863,88 +879,131 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) fixed_t radius = 28<mdepth == CSSTEP_ALTS) + switch (p->mdepth) { - INT16 skin; - - n = (p->clonenum) + numoptions/2; - if (subtract) - n -= ((i+1)/2); - else - n += ((i+1)/2); - n %= numoptions; - - skin = setup_chargrid[p->gridx][p->gridy].skinlist[n]; - patch = faceprefix[skin][FACE_RANK]; - colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE); - radius = 24<width) << FRACBITS) >> 1; - cy -= (SHORT(patch->height) << FRACBITS) >> 1; - } - - else if (p->mdepth == CSSTEP_FOLLOWERCOLORS) - { - INT16 diff; - UINT16 col; - - if (i == 0) + case CSSTEP_ALTS: { - n = l = r = M_GetColorBefore(p->followercolor, numoptions/2, true); - } - else if (subtract) - { - n = l = M_GetColorBefore(l, 1, true); - } - else - { - n = r = M_GetColorAfter(r, 1, true); + INT16 skin; + + n = (p->clonenum) + numoptions/2; + if (subtract) + n -= ((i+1)/2); + else + n += ((i+1)/2); + n %= numoptions; + + skin = setup_chargrid[p->gridx][p->gridy].skinlist[n]; + patch = faceprefix[skin][FACE_RANK]; + colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE); + radius = 24<width) << FRACBITS) >> 1; + cy -= (SHORT(patch->height) << FRACBITS) >> 1; + break; } - col = K_GetEffectiveFollowerColor(n, p->color); - - colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); - - diff = (numoptions - i)/2; // only 0 when i == numoptions-1 - - if (diff == 0) - patch = W_CachePatchName("COLORSP2", PU_CACHE); - else if (abs(diff) < 25) - patch = W_CachePatchName("COLORSP1", PU_CACHE); - else - patch = W_CachePatchName("COLORSP0", PU_CACHE); - - cx -= (SHORT(patch->width) << FRACBITS) >> 1; - } - else - { - INT16 diff; - - if (i == 0) + case CSSTEP_COLORS: { - n = l = r = M_GetColorBefore(p->color, numoptions/2, false); - } - else if (subtract) - { - n = l = M_GetColorBefore(l, 1, false); - } - else - { - n = r = M_GetColorAfter(r, 1, false); + INT16 diff; + + if (i == 0) + { + n = l = r = M_GetColorBefore(p->color, numoptions/2, false); + } + else if (subtract) + { + n = l = M_GetColorBefore(l, 1, false); + } + else + { + n = r = M_GetColorAfter(r, 1, false); + } + + colormap = R_GetTranslationColormap(TC_DEFAULT, n, GTC_MENUCACHE); + + diff = (numoptions - i)/2; // only 0 when i == numoptions-1 + + if (diff == 0) + patch = W_CachePatchName("COLORSP2", PU_CACHE); + else if (abs(diff) < 25) + patch = W_CachePatchName("COLORSP1", PU_CACHE); + else + patch = W_CachePatchName("COLORSP0", PU_CACHE); + + cx -= (SHORT(patch->width) << FRACBITS) >> 1; + break; } - colormap = R_GetTranslationColormap(TC_DEFAULT, n, GTC_MENUCACHE); + case CSSTEP_FOLLOWER: + { + follower_t *fl = NULL; - diff = (numoptions - i)/2; // only 0 when i == numoptions-1 + n = (p->followern + 1) + numoptions/2; + if (subtract) + n -= ((i+1)/2); + else + n += ((i+1)/2); + n %= numoptions; - if (diff == 0) - patch = W_CachePatchName("COLORSP2", PU_CACHE); - else if (abs(diff) < 25) - patch = W_CachePatchName("COLORSP1", PU_CACHE); - else - patch = W_CachePatchName("COLORSP0", PU_CACHE); + if (n == 0) + { + patch = W_CachePatchName("K_NOBLNS", PU_CACHE); + } + else + { + fl = &followers[n - 1]; + patch = W_CachePatchName(fl->icon, PU_CACHE); - cx -= (SHORT(patch->width) << FRACBITS) >> 1; + colormap = R_GetTranslationColormap(TC_DEFAULT, + K_GetEffectiveFollowerColor(p->followercolor, p->color), + GTC_MENUCACHE + ); + } + + radius = 24<width) << FRACBITS) >> 1; + cy -= (SHORT(patch->height) << FRACBITS) >> 1; + break; + } + + case CSSTEP_FOLLOWERCOLORS: + { + INT16 diff; + UINT16 col; + + if (i == 0) + { + n = l = r = M_GetColorBefore(p->followercolor, numoptions/2, true); + } + else if (subtract) + { + n = l = M_GetColorBefore(l, 1, true); + } + else + { + n = r = M_GetColorAfter(r, 1, true); + } + + col = K_GetEffectiveFollowerColor(n, p->color); + + colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); + + diff = (numoptions - i)/2; // only 0 when i == numoptions-1 + + if (diff == 0) + patch = W_CachePatchName("COLORSP2", PU_CACHE); + else if (abs(diff) < 25) + patch = W_CachePatchName("COLORSP1", PU_CACHE); + else + patch = W_CachePatchName("COLORSP0", PU_CACHE); + + cx -= (SHORT(patch->width) << FRACBITS) >> 1; + break; + } + + default: + break; } if (subtract) @@ -1007,72 +1066,6 @@ static boolean M_DrawCharacterSprite(INT16 x, INT16 y, INT16 skin, INT32 addflag return true; } -// Draws the follower list. -static void M_DrawFollowerList(setup_player_t *p, UINT8 num) -{ - INT16 x = 82; - INT16 y = 3; - INT32 cf = p->followern; - UINT8 i; - UINT8 *colormap = NULL; - - if (num & 1) - x = 172; - - if (num >= 2) - y = 176; - - // this places it at the bottom of the card! - if (optionsmenu.profile) - { - x = 35; - y = 143; - } - - // Start 1 follower below. - cf--; - if (cf < -1) - cf = numfollowers-1; - - for (i = 0; i < 3; i++) - { - patch_t *pp = NULL; - follower_t fl = followers[cf]; - - if (W_LumpExists(fl.icon) && cf >= 0) - { - UINT16 col = K_GetEffectiveFollowerColor(p->followercolor, p->color); - pp = W_CachePatchName(fl.icon, PU_CACHE); - - colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); - V_DrawMappedPatch(x, y, 0, pp, colormap); - if (i == 1) - V_DrawMappedPatch(x, y, 0, W_CachePatchName(va("K_CHILI%d", p->follower_timer%16 /2 +1), PU_CACHE), NULL); - - } - // No patch.... - else - { - V_DrawFill(x, y, 16, 16, 0); - - if (i == 1) - V_DrawMappedPatch(x, y, 0, W_CachePatchName(va("K_CHILI%d", p->follower_timer%16 /2 +1), PU_CACHE), NULL); - - if (cf >= 0) - V_DrawString(x, y, 0, va("%d\n", cf)); - else - V_DrawMappedPatch(x-4, y-3, 0, W_CachePatchName("K_NOBLNS", PU_CACHE), NULL); - - } - - cf++; - if (cf >= numfollowers) - cf = -1; - - x += 23; - } -} - // Returns false is the follower shouldn't be rendered. // 'num' can be used to directly specify the follower number, but doing this will not animate it. // if a setup_player_t is specified instead, its data will be used to animate the follower sprite. @@ -1185,15 +1178,9 @@ static void M_DrawCharSelectPreview(UINT8 num) if (p->mdepth >= CSSTEP_FOLLOWER) { M_DrawFollowerSprite(x+16, y+75, -1, !(num & 1) ? V_FLIP : 0, 0, p); - - if (p->mdepth == CSSTEP_FOLLOWER) - M_DrawFollowerList(p, num); } - if (p->mdepth == CSSTEP_ALTS || p->mdepth == CSSTEP_COLORS || p->mdepth == CSSTEP_FOLLOWERCOLORS) - { - M_DrawCharSelectCircle(p, x+32, y+64); - } + M_DrawCharSelectCircle(p, x+32, y+64); } if ((setup_animcounter/10) & 1 && gamestate == GS_MENU) // Not drawn outside of GS_MENU. @@ -1432,14 +1419,10 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) } } - if (sp->mdepth == CSSTEP_ALTS || sp->mdepth == CSSTEP_COLORS || sp->mdepth == CSSTEP_FOLLOWERCOLORS) - { - M_DrawCharSelectCircle(sp, x-22, y+104); - } + M_DrawCharSelectCircle(sp, x-22, y+104); } else if (skinnum > -1) // otherwise, read from profile. { - UINT16 col = K_GetEffectiveFollowerColor(p->followercolor, p->color);; UINT8 fln = K_FollowerAvailable(p->follower); @@ -1476,11 +1459,6 @@ void M_DrawCharacterSelect(void) INT16 skin; INT32 basex = optionsmenu.profile != NULL ? 64 : 0; - // Draw page num. - // @TODO: make it fancier than the default string lol. - if (setup_numplayers < 2) - V_DrawCenteredString(160, 1, 0, va("%d/%d", setup_page+1, setup_maxpage+1)); - if (setup_numplayers > 0) { priority = setup_animcounter % setup_numplayers; @@ -1553,13 +1531,12 @@ void M_DrawCharacterSelect(void) { // Draw a preview for each player if (optionsmenu.profile == NULL) + { M_DrawCharSelectPreview(i); + } else if (i == 0) { M_DrawProfileCard(optionsmenu.optx, optionsmenu.opty, false, optionsmenu.profile); - - if (setup_player[0].mdepth == CSSTEP_FOLLOWER) - M_DrawFollowerList(&setup_player[0], 0); } if (i >= setup_numplayers) From 7924805d2b828d4fcabf43f5a6640b9522cdd92e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 29 Aug 2022 02:05:48 -0400 Subject: [PATCH 317/379] Use rotate sounds --- src/k_menufunc.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 010e527cc..c50645397 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2603,7 +2603,6 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num) return false; } - static void M_HandleCharRotate(setup_player_t *p, UINT8 num) { UINT8 numclones = setup_chargrid[p->gridx][p->gridy].numskins; @@ -2713,37 +2712,43 @@ static void M_AnimateFollower(setup_player_t *p) p->follower_timer++; } -static void M_HandleChooseFollower(setup_player_t *p, UINT8 num) +static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num) { + UINT8 numclones = setup_chargrid[p->gridx][p->gridy].numskins; + if (cv_splitdevice.value) num = 0; - M_AnimateFollower(p); - - if (menucmd[num].dpad_lr > 0 && numfollowers) + if (menucmd[num].dpad_lr > 0) { p->followern++; if (p->followern >= numfollowers) p->followern = -1; - M_SetMenuDelay(num); - S_StartSound(NULL, sfx_s3k5b); M_GetFollowerState(p); + + p->rotate = CSROTATETICS; + p->delay = CSROTATETICS; + S_StartSound(NULL, sfx_s3kc3s); } - else if (menucmd[num].dpad_lr < 0 && numfollowers) + else if (menucmd[num].dpad_lr < 0) { p->followern--; if (p->followern < -1) p->followern = numfollowers-1; - M_SetMenuDelay(num); - S_StartSound(NULL, sfx_s3k5b); - M_GetFollowerState(p); + p->rotate = -CSROTATETICS; + p->delay = CSROTATETICS; + S_StartSound(NULL, sfx_s3kc3s); } - else if (M_MenuConfirmPressed(num)) + + if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/) { if (p->followern > -1) + { p->mdepth = CSSTEP_FOLLOWERCOLORS; + S_StartSound(NULL, sfx_s3k63); + } else { p->mdepth = CSSTEP_READY; @@ -2752,7 +2757,6 @@ static void M_HandleChooseFollower(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_s3k4e); } - S_StartSound(NULL, sfx_s3k63); M_SetMenuDelay(num); } else if (M_MenuBackPressed(num)) @@ -2888,7 +2892,7 @@ boolean M_CharacterSelectHandler(INT32 choice) M_HandleColorRotate(p, i); break; case CSSTEP_FOLLOWER: - M_HandleChooseFollower(p, i); + M_HandleFollowerRotate(p, i); break; case CSSTEP_FOLLOWERCOLORS: M_HandleFollowerColorRotate(p, i); From 8ae4bba36d52010c845493da7bd25dac2b225791 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 29 Aug 2022 02:16:09 -0400 Subject: [PATCH 318/379] Fix ready explosion sound not playing --- src/k_menufunc.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index c50645397..78e8b69cb 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2466,9 +2466,9 @@ static void M_HandleCharAskChange(setup_player_t *p, UINT8 num) } else if (M_MenuConfirmPressed(num)) { - // no changes if (!p->changeselect) { + // no changes M_GetFollowerState(p); p->mdepth = CSSTEP_READY; p->delay = TICRATE; @@ -2476,13 +2476,14 @@ static void M_HandleCharAskChange(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_s3k4e); M_SetupReadyExplosions(p); } - - // changes else + { + // changes p->mdepth = CSSTEP_CHARS; + S_StartSound(NULL, sfx_s3k63); + } M_SetMenuDelay(num); - S_StartSound(NULL, sfx_s3k63); } } @@ -2667,18 +2668,19 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num) { p->mdepth = CSSTEP_FOLLOWER; M_GetFollowerState(p); - - //p->delay = TICRATE; - //M_SetupReadyExplosions(p); - //S_StartSound(NULL, sfx_s3k4e); + S_StartSound(NULL, sfx_s3k63); M_SetMenuDelay(num); } else if (M_MenuBackPressed(num)) { if (setup_chargrid[p->gridx][p->gridy].numskins == 1) + { p->mdepth = CSSTEP_CHARS; // Skip clones menu + } else + { p->mdepth = CSSTEP_ALTS; + } S_StartSound(NULL, sfx_s3k5b); M_SetMenuDelay(num); } @@ -2714,8 +2716,6 @@ static void M_AnimateFollower(setup_player_t *p) static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num) { - UINT8 numclones = setup_chargrid[p->gridx][p->gridy].numskins; - if (cv_splitdevice.value) num = 0; @@ -2789,7 +2789,7 @@ static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num) S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s } - if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/) + if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/) { p->mdepth = CSSTEP_READY; p->delay = TICRATE; From 2b3d8dccf94b34d261986cd91f517bcfe0c55be5 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 29 Aug 2022 02:35:05 -0400 Subject: [PATCH 319/379] Guest profile always does changes --- src/k_menufunc.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 78e8b69cb..01ebdb62c 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2433,7 +2433,17 @@ static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num) M_SetupProfileGridPos(p); p->changeselect = 0; - p->mdepth = CSSTEP_ASKCHANGES; + + if (p->profilen == 0) + { + // Guest profile, always ask for options. + p->mdepth = CSSTEP_CHARS; + } + else + { + p->mdepth = CSSTEP_ASKCHANGES; + } + S_StartSound(NULL, sfx_s3k63); } @@ -2874,7 +2884,6 @@ boolean M_CharacterSelectHandler(INT32 choice) case CSSTEP_NONE: // Enter Game if (gamestate == GS_MENU) // do NOT handle that outside of GS_MENU. playersChanged = M_HandlePressStart(p, i); - break; case CSSTEP_PROFILE: playersChanged = M_HandleCSelectProfile(p, i); From b388890647beb1b759f3f2bfd98f6e071f4794ea Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 29 Aug 2022 04:08:12 -0400 Subject: [PATCH 320/379] Profile fixes - Less M_StartMessage spam - Say "NEW" instead of "EMPTY" for new profile creation - Use Eggman instead of Sonic for the Guest profile - Instead of needing to hold X for 3 seconds to exit the test controls menu, you simply press nothing for 5 seconds. - Add separate back option to controls menu. - Started on the ability to use prefcolor as your profile color - Allow guest online because there is literally already mechanics for having a player with no power level :V --- src/k_menudef.c | 7 ++- src/k_menudraw.c | 9 ++-- src/k_menufunc.c | 116 ++++++++++++++++++----------------------------- src/k_profiles.c | 32 +++++++++++-- src/k_profiles.h | 8 +++- 5 files changed, 88 insertions(+), 84 deletions(-) diff --git a/src/k_menudef.c b/src/k_menudef.c index e62a8c0e0..39bde4022 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -653,7 +653,7 @@ menuitem_t OPTIONS_ProfileControls[] = { {IT_CONTROL, "OPEN TEAM CHAT", "Do we even have team gamemodes?", NULL, {.routine = M_ProfileSetControl}, gc_teamtalk, 0}, - {IT_CONTROL, "OPEN CONSOLE", "Also usable with ` on Keyboard.", + {IT_CONTROL, "OPEN CONSOLE", "Opens the developer options console.", NULL, {.routine = M_ProfileSetControl}, gc_console, 0}, {IT_CONTROL, "LUA/A", "May be used by add-ons.", @@ -674,8 +674,11 @@ menuitem_t OPTIONS_ProfileControls[] = { {IT_HEADER, "EXTRA", "", NULL, {NULL}, 0, 0}, - {IT_STRING | IT_CALL, "TRY MAPPINGS", "Only display the controller for testing.", + {IT_STRING | IT_CALL, "TRY MAPPINGS", "Test your controls.", NULL, {.routine = M_ProfileTryController}, 0, 0}, + + {IT_STRING | IT_CALL, "BACK...", "Go back to profile setup.", + NULL, {.routine = M_GoBack}, 0, 0}, }; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index b93ff768e..25677a87b 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1227,7 +1227,7 @@ static void M_DrawCharSelectPreview(UINT8 num) } else if (dist == 2) { - V_DrawCenteredFileString(px+26, py, 0, pr->version ? pr->profilename : "EMPTY"); + V_DrawCenteredFileString(px+26, py, 0, pr->version ? pr->profilename : "NEW"); V_DrawScaledPatch(px, py, V_TRANSLUCENT, W_CachePatchName("FILEBACK", PU_CACHE)); } else @@ -1235,7 +1235,7 @@ static void M_DrawCharSelectPreview(UINT8 num) V_DrawScaledPatch(px, py, 0, W_CachePatchName("FILEBACK", PU_CACHE)); if (i != p->profilen || ((setup_animcounter/10) & 1)) - V_DrawCenteredFileString(px+26, py, 0, pr->version ? pr->profilename : "EMPTY"); + V_DrawCenteredFileString(px+26, py, 0, pr->version ? pr->profilename : "NEW"); } py += 12; } @@ -1366,11 +1366,11 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) INT32 skinnum = -1; INT32 powerlevel = -1; - char pname[PROFILENAMELEN+1] = "empty"; + char pname[PROFILENAMELEN+1] = "NEW"; if (p != NULL && p->version) { - colormap = R_GetTranslationColormap(TC_DEFAULT, p->color, GTC_CACHE); + colormap = R_GetTranslationColormap(TC_DEFAULT, PR_GetProfileColor(p), GTC_CACHE); strcpy(pname, p->profilename); skinnum = R_SkinAvailable(p->skinname); powerlevel = p->powerlevels[0]; // Only display race power level. @@ -2904,6 +2904,7 @@ void M_DrawProfileControls(void) case IT_STRING: V_DrawString(x, y+1, (i == itemOn ? highlightflags : 0), currentMenu->menuitems[i].text); + y += spacing; break; case IT_STRING2: diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 01ebdb62c..edfd94767 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2057,7 +2057,7 @@ static void M_SetupProfileGridPos(setup_player_t *p) alt++; p->clonenum = alt; - p->color = pr->color; + p->color = PR_GetProfileColor(pr); return; // we're done here } } @@ -3759,19 +3759,9 @@ void M_MPOptSelectInit(INT32 choice) { INT16 arrcpy[3][3] = {{0,68,0}, {0,12,0}, {0,74,0}}; UINT8 i = 0, j = 0; // To copy the array into the struct - const UINT8 pid = 0; (void)choice; - // Don't allow guest profile online - if (cv_currprofile.value == 0) - { - M_StartMessage(M_GetText("Cannot play online with\nGuest Profile.\nMake a custom Profile and try again.\n\n(Press any key)"), NULL, MM_NOTHING); - S_StartSound(NULL, sfx_s3k7b); - M_SetMenuDelay(pid); - return; - } - mpmenu.modechoice = 0; mpmenu.ticker = 0; @@ -4778,40 +4768,41 @@ void M_HandleProfileSelect(INT32 ch) if (optionsmenu.profilen == 0) // Guest profile, you can't edit that one! { S_StartSound(NULL, sfx_s3k7b); - M_StartMessage(M_GetText("Guest Profile cannot be edited.\nTo change parameters,\ncreate a new Profile."), NULL, MM_NOTHING); + M_StartMessage(M_GetText("The Guest profile cannot be edited.\nCreate a new profile instead."), NULL, MM_NOTHING); M_SetMenuDelay(pid); return; } else if (optionsmenu.profilen == maxp && gamestate != GS_MENU) { S_StartSound(NULL, sfx_s3k7b); - M_StartMessage(M_GetText("Cannot create a New Profile\nmid-game. Return to the\nTitle Screen first."), NULL, MM_NOTHING); + M_StartMessage(M_GetText("Cannot create a new profile\nmid-game. Return to the\ntitle screen first."), NULL, MM_NOTHING); M_SetMenuDelay(pid); return; } S_StartSound(NULL, sfx_s3k5b); - M_StartEditProfile(MA_YES); } else { // We're on the profile selection screen. - if (optionsmenu.profilen == 0) + if (optionsmenu.profilen == maxp) { - M_StartMessage(M_GetText("Are you sure you wish\nto use the Guest Profile?\nThis profile cannot be customised.\nIt is recommended to create\na new Profile instead.\n\n(Press A to confirm)"), FUNCPTRCAST(M_FirstPickProfile), MM_YESNO); - M_SetMenuDelay(pid); - return; - } - else if (optionsmenu.profilen == maxp) - { - M_StartMessage(M_GetText("Create a new Profile?\n\n(Press A to confirm)"), FUNCPTRCAST(M_StartEditProfile), MM_YESNO); + M_StartEditProfile(MA_YES); M_SetMenuDelay(pid); return; } else { - M_StartMessage(M_GetText("Are you sure you wish to\nselect this profile?\n\n(Press A to confirm)"), FUNCPTRCAST(M_FirstPickProfile), MM_YESNO); +#if 0 + if (optionsmenu.profilen == 0) + { + M_StartMessage(M_GetText("Are you sure you wish\nto use the Guest Profile?\nThis profile cannot be customised.\nIt is recommended to create\na new Profile instead.\n\n(Press A to confirm)"), FUNCPTRCAST(M_FirstPickProfile), MM_YESNO); + return; + } +#endif + + M_FirstPickProfile(MA_YES); M_SetMenuDelay(pid); return; } @@ -4836,6 +4827,15 @@ static boolean M_ProfileEditEnd(const UINT8 pid) { UINT8 i; + // Guest profile, you can't edit that one! + if (optionsmenu.profilen == 0) + { + S_StartSound(NULL, sfx_s3k7b); + M_StartMessage(M_GetText("Guest profile cannot be edited.\nCreate a new profile instead."), NULL, MM_NOTHING); + M_SetMenuDelay(pid); + return false; + } + // check if some profiles have the same name for (i = 0; i < PR_GetNumProfiles(); i++) { @@ -4847,21 +4847,13 @@ static boolean M_ProfileEditEnd(const UINT8 pid) if (!(strcmp(optionsmenu.profile->profilename, check->profilename))) { S_StartSound(NULL, sfx_s3k7b); - M_StartMessage(M_GetText("Another Profile uses the same\nname identifier.\nPlease change the name\nof the Profile to save it."), NULL, MM_NOTHING); + M_StartMessage(M_GetText("Another profile uses the same name.\nThis must be changed to be able to save."), NULL, MM_NOTHING); M_SetMenuDelay(pid); return false; } } } - if (optionsmenu.profilen == 0) // Guest profile, you can't edit that one! - { - S_StartSound(NULL, sfx_s3k7b); - M_StartMessage(M_GetText("Guest Profile cannot be edited.\nTo change parameters,\ncreate a new Profile."), NULL, MM_NOTHING); - M_SetMenuDelay(pid); - return false; - } - return true; } @@ -5113,37 +5105,18 @@ void M_HandleProfileControls(void) } } -static void M_ProfileTryControllerResponse(INT32 choice) -{ - if (choice == MA_YES) - { - optionsmenu.trycontroller = TICRATE*3; - // Apply these controls right now on P1's end. - memcpy(&gamecontrol[0], optionsmenu.tempcontrols, sizeof(gamecontroldefault)); - } -} - void M_ProfileTryController(INT32 choice) { - (void) choice; + (void)choice; - // I managed to softlock myself during testing lol. - if (!optionsmenu.tempcontrols[gc_x][0]) - { - M_StartMessage(M_GetText("You need to bind a key to [X]\nto use this feature.\n"), NULL, MM_NOTHING); - return; - } - else - { - M_StartMessage(M_GetText("Your inputs will temporarily be\nremapped to match this Profile's settings.\nThe controller graphic will animate\nto show you what buttons are being pressed.\nIs this okay?\n\n(Press A to continue)"), - FUNCPTRCAST(M_ProfileTryControllerResponse), MM_YESNO); - return; - } + optionsmenu.trycontroller = TICRATE*5; + + // Apply these controls right now on P1's end. + memcpy(&gamecontrol[0], optionsmenu.tempcontrols, sizeof(gamecontroldefault)); } static void M_ProfileControlSaveResponse(INT32 choice) { - if (choice == MA_YES) { SINT8 belongsto = PR_ProfileUsedBy(optionsmenu.profile); @@ -5171,21 +5144,24 @@ boolean M_ProfileControlsInputs(INT32 ch) // By default, accept all inputs. if (optionsmenu.trycontroller) { - if (M_MenuButtonHeld(pid, MBT_X)) - optionsmenu.trycontroller--; + if (menucmd[pid].dpad_ud || menucmd[pid].dpad_lr || menucmd[pid].buttons) + { + optionsmenu.trycontroller = 5*TICRATE; + } else - optionsmenu.trycontroller = TICRATE*3; + { + optionsmenu.trycontroller--; + } - if (!optionsmenu.trycontroller) + if (optionsmenu.trycontroller == 0) { // Reset controls to that of the current profile. profile_t *cpr = PR_GetProfile(cv_currprofile.value); if (cpr == NULL) cpr = PR_GetProfile(0); // Creating a profile at boot, revert to guest profile memcpy(&gamecontrol[0], cpr->controls, sizeof(gamecontroldefault)); - - M_StartMessage(M_GetText("Your controls have been\nreverted to their previous state.\n\n(Press any key)"), NULL, MM_NOTHING); } + return true; } @@ -5212,13 +5188,9 @@ boolean M_ProfileControlsInputs(INT32 ch) } else if (M_MenuBackPressed(pid)) { - - SINT8 usedby = PR_ProfileUsedBy(optionsmenu.profile); - - if (usedby > -1 && cv_currprofile.value) - M_StartMessage(M_GetText(va("As this is Player %d's active Profile,\ncontrol changes will be applied \nimmediately upon exiting this menu.\nIs this okay?\n\n(Press A to confirm)", usedby+1)), FUNCPTRCAST(M_ProfileControlSaveResponse), MM_YESNO); - else - M_StartMessage(M_GetText("Exiting will save the control changes\nfor this Profile.\nIs this okay?\n\n(Press A to confirm)"), FUNCPTRCAST(M_ProfileControlSaveResponse), MM_YESNO); + //M_StartMessage(M_GetText("Exiting will save the control changes\nfor this Profile.\nIs this okay?\n\n(Press A to confirm)"), FUNCPTRCAST(M_ProfileControlSaveResponse), MM_YESNO); + // TODO: Add a graphic for controls saving, instead of obnoxious prompt. + M_ProfileControlSaveResponse(MA_YES); optionsmenu.profile->kickstartaccel = cv_dummyprofilekickstart.value; // Make sure to save kickstart accel. @@ -5535,7 +5507,7 @@ void M_CheckProfileData(INT32 choice) if (np < 2) { S_StartSound(NULL, sfx_s3k7b); - M_StartMessage("There are no custom Profiles.\n\n(Press any button)", NULL, MM_NOTHING); + M_StartMessage("There are no custom profiles.\n\n(Press any button)", NULL, MM_NOTHING); return; } @@ -5598,9 +5570,9 @@ void M_HandleProfileErase(INT32 choice) else if (M_MenuConfirmPressed(pid)) { if (optionsmenu.eraseprofilen == cv_currprofile.value) - M_StartMessage("This profile and all of\nits data will be erased.\nAre you sure you want to proceed?\nAs this is your currently loaded Profile,\ndeleting this Profile would also\nreturn you to the Title Screen\n\n(Press A to confirm)", FUNCPTRCAST(M_EraseProfileResponse), MM_YESNO); + M_StartMessage("This profile will be erased.\nAre you sure you want to proceed?\nDeleting this profile will also\nreturn you to the title screen.\n\n(Press A to confirm)", FUNCPTRCAST(M_EraseProfileResponse), MM_YESNO); else - M_StartMessage("This profile and all of\nits data will be erased.\nAre you sure you want to proceed?\n\n(Press A to confirm)", FUNCPTRCAST(M_EraseProfileResponse), MM_YESNO); + M_StartMessage("This profile will be erased.\nAre you sure you want to proceed?\n\n(Press A to confirm)", FUNCPTRCAST(M_EraseProfileResponse), MM_YESNO); M_SetMenuDelay(pid); } diff --git a/src/k_profiles.c b/src/k_profiles.c index 14fd53885..3f3cce4fe 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -13,6 +13,7 @@ #include "d_main.h" // pandf #include "k_profiles.h" #include "z_zone.h" +#include "r_skins.h" // List of all the profiles. static profile_t *profilesList[MAXPROFILES+1]; // +1 because we're gonna add a default "GUEST' profile. @@ -201,19 +202,23 @@ void PR_LoadProfiles(void) fread(profilesList[i], sizeof(profile_t), 1, f); // Attempt to correct numerical footguns - if (profilesList[i]->color >= numskincolors - || profilesList[i]->color == 0 + if (profilesList[i]->color == SKINCOLOR_NONE) + { + ; // Valid, even outside the bounds + } + else if (profilesList[i]->color >= numskincolors || skincolors[profilesList[i]->color].accessible == false) { profilesList[i]->color = PROFILEDEFAULTCOLOR; } + if (profilesList[i]->followercolor == FOLLOWERCOLOR_MATCH || profilesList[i]->followercolor == FOLLOWERCOLOR_OPPOSITE) { ; // Valid, even outside the bounds } else if (profilesList[i]->followercolor >= numskincolors - || profilesList[i]->followercolor == 0 + || profilesList[i]->followercolor == SKINCOLOR_NONE || skincolors[profilesList[i]->followercolor].accessible == false) { profilesList[i]->followercolor = PROFILEDEFAULTFOLLOWERCOLOR; @@ -232,6 +237,25 @@ void PR_LoadProfiles(void) } } +skincolornum_t PR_GetProfileColor(profile_t *p) +{ + if (p->color == SKINCOLOR_NONE) + { + // Get skin's prefcolor. + INT32 foundskin = R_SkinAvailable(p->skinname); + if (foundskin == -1) + { + // Return random default value + return SKINCOLOR_RED; + } + + return skins[foundskin].prefcolor; + } + + // Get exact color. + return p->color; +} + void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum) { profile_t *p = PR_GetProfile(profilenum); @@ -245,7 +269,7 @@ void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum) } CV_StealthSet(&cv_skin[playernum], p->skinname); - CV_StealthSetValue(&cv_playercolor[playernum], p->color); + CV_StealthSetValue(&cv_playercolor[playernum], PR_GetProfileColor(p)); CV_StealthSet(&cv_playername[playernum], p->playername); // Followers diff --git a/src/k_profiles.h b/src/k_profiles.h index 53d74470e..8525a1b54 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -33,8 +33,8 @@ #define PROFILEDEFAULTNAME "guest" #define PROFILEDEFAULTPNAME "Player" -#define PROFILEDEFAULTSKIN "sonic" -#define PROFILEDEFAULTCOLOR SKINCOLOR_SAPPHIRE +#define PROFILEDEFAULTSKIN "eggman" +#define PROFILEDEFAULTCOLOR SKINCOLOR_NONE #define PROFILEDEFAULTFOLLOWER "none" #define PROFILEDEFAULTFOLLOWERCOLOR FOLLOWERCOLOR_MATCH @@ -112,6 +112,10 @@ void PR_SaveProfiles(void); // This also loads void PR_LoadProfiles(void); +// PR_GetProfileColor(profile_t *p) +// Returns the profile's color, or the skin's prefcolor if set to none. +skincolornum_t PR_GetProfileColor(profile_t *p); + // PR_ApplyProfile(UINT8 profilenum, UINT8 playernum) // Applies the given profile's settings to the given player. void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum); From e8b179f7a1a6e887588203ede8d341fe7daf3f11 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 29 Aug 2022 04:21:14 -0400 Subject: [PATCH 321/379] Switch around maxplayers and ingamecap - ingamecap is now the new maxplayers - The old maxplayers is now called maxconnections, but can mostly be left alone - Insert into match race menu --- src/d_clisrv.c | 12 ++++++------ src/d_clisrv.h | 2 +- src/d_netcmd.c | 8 ++++---- src/d_netcmd.h | 2 +- src/k_bot.c | 6 +++--- src/k_kart.c | 6 +++--- src/k_menudef.c | 25 ++++++++++++++----------- src/k_menudraw.c | 2 +- src/k_menufunc.c | 20 +++++++++++--------- src/st_stuff.c | 4 ++-- 10 files changed, 46 insertions(+), 41 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 76e3baa4d..7f9c2ef21 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -887,13 +887,13 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) netbuffer->u.serverinfo.leveltime = (tic_t)LONG(leveltime); netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers(); - netbuffer->u.serverinfo.maxplayer = (UINT8)(min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value)); + netbuffer->u.serverinfo.maxplayer = (UINT8)(min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxconnections.value)); if (!node) netbuffer->u.serverinfo.refusereason = 0; else if (!cv_allownewplayer.value) netbuffer->u.serverinfo.refusereason = 1; - else if (D_NumPlayers() >= cv_maxplayers.value) + else if (D_NumPlayers() >= cv_maxconnections.value) netbuffer->u.serverinfo.refusereason = 2; else netbuffer->u.serverinfo.refusereason = 0; @@ -1057,7 +1057,7 @@ static boolean SV_SendServerConfig(INT32 node) netbuffer->u.servercfg.gametype = (UINT8)gametype; netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame; - netbuffer->u.servercfg.maxplayer = (UINT8)(min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value)); + netbuffer->u.servercfg.maxplayer = (UINT8)(min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxconnections.value)); netbuffer->u.servercfg.allownewplayer = cv_allownewplayer.value; netbuffer->u.servercfg.discordinvites = (boolean)cv_discordinvites.value; @@ -3072,7 +3072,7 @@ consvar_t cv_joinnextround = CVAR_INIT ("joinnextround", "Off", CV_NETVAR, CV_On #endif static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {MAXPLAYERS, "MAX"}, {0, NULL}}; -consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_SAVE|CV_CALL, maxplayers_cons_t, Joinable_OnChange); +consvar_t cv_maxconnections = CVAR_INIT ("maxconnections", "16", CV_SAVE|CV_CALL, maxplayers_cons_t, Joinable_OnChange); static CV_PossibleValue_t joindelay_cons_t[] = {{1, "MIN"}, {3600, "MAX"}, {0, "Off"}, {0, NULL}}; consvar_t cv_joindelay = CVAR_INIT ("joindelay", "10", CV_SAVE|CV_NETVAR, joindelay_cons_t, NULL); @@ -3108,7 +3108,7 @@ static void Joinable_OnChange(void) if (!server) return; - maxplayer = (UINT8)(min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value)); + maxplayer = (UINT8)(min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxconnections.value)); WRITEUINT8(p, maxplayer); WRITEUINT8(p, cv_allownewplayer.value); @@ -3760,7 +3760,7 @@ static void HandleConnect(SINT8 node) // Sal: Dedicated mode is INCREDIBLY hacked together. // If a server filled out, then it'd overwrite the host and turn everyone into weird husks..... // It's too much effort to legimately fix right now. Just prevent it from reaching that state. - UINT8 maxplayers = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value); + UINT8 maxplayers = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxconnections.value); if (bannednode && bannednode[node]) SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server.")); diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 9fa7ac37d..e10e51626 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -445,7 +445,7 @@ extern tic_t servermaxping; extern boolean server_lagless; -extern consvar_t cv_netticbuffer, cv_allownewplayer, cv_maxplayers, cv_joindelay; +extern consvar_t cv_netticbuffer, cv_allownewplayer, cv_maxconnections, cv_joindelay; extern consvar_t cv_resynchattempts, cv_blamecfail; extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index cd2ef2da7..48d82e41d 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -251,8 +251,8 @@ static consvar_t cv_dummyconsvar = CVAR_INIT ("dummyconsvar", "Off", CV_CALL|CV_ consvar_t cv_restrictskinchange = CVAR_INIT ("restrictskinchange", "Yes", CV_NETVAR|CV_CHEAT, CV_YesNo, NULL); consvar_t cv_allowteamchange = CVAR_INIT ("allowteamchange", "Yes", CV_NETVAR, CV_YesNo, NULL); -static CV_PossibleValue_t ingamecap_cons_t[] = {{0, "MIN"}, {MAXPLAYERS-1, "MAX"}, {0, NULL}}; -consvar_t cv_ingamecap = CVAR_INIT ("ingamecap", "0", CV_NETVAR, ingamecap_cons_t, NULL); +static CV_PossibleValue_t maxplayers_cons_t[] = {{1, "MIN"}, {MAXPLAYERS, "MAX"}, {0, NULL}}; +consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_NETVAR, maxplayers_cons_t, NULL); consvar_t cv_startinglives = CVAR_INIT ("startinglives", "3", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, startingliveslimit_cons_t, NULL); @@ -730,11 +730,11 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_allowexitlevel); CV_RegisterVar(&cv_restrictskinchange); CV_RegisterVar(&cv_allowteamchange); - CV_RegisterVar(&cv_ingamecap); + CV_RegisterVar(&cv_maxplayers); CV_RegisterVar(&cv_respawntime); // d_clisrv - CV_RegisterVar(&cv_maxplayers); + CV_RegisterVar(&cv_maxconnections); CV_RegisterVar(&cv_joindelay); CV_RegisterVar(&cv_resynchattempts); CV_RegisterVar(&cv_maxsend); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 302b2e145..aa4f1a80e 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -69,7 +69,7 @@ extern consvar_t cv_runscripts; extern consvar_t cv_mute; extern consvar_t cv_pause; -extern consvar_t cv_restrictskinchange, cv_allowteamchange, cv_ingamecap, cv_respawntime; +extern consvar_t cv_restrictskinchange, cv_allowteamchange, cv_maxplayers, cv_respawntime; // SRB2kart items extern consvar_t cv_superring, cv_sneaker, cv_rocketsneaker, cv_invincibility, cv_banana; diff --git a/src/k_bot.c b/src/k_bot.c index 0ae14c50e..d772ef14e 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -109,7 +109,7 @@ boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *p) void K_UpdateMatchRaceBots(void) { const UINT8 difficulty = cv_kartbot.value; - UINT8 pmax = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value); + UINT8 pmax = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxconnections.value); UINT8 numplayers = 0; UINT8 numbots = 0; UINT8 numwaiting = 0; @@ -135,9 +135,9 @@ void K_UpdateMatchRaceBots(void) } } - if (cv_ingamecap.value > 0) + if (cv_maxplayers.value > 0) { - pmax = min(pmax, cv_ingamecap.value); + pmax = min(pmax, cv_maxplayers.value); } for (i = 0; i < MAXPLAYERS; i++) diff --git a/src/k_kart.c b/src/k_kart.c index f47bf3b17..9ded09ea3 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -10515,7 +10515,7 @@ void K_CheckSpectateStatus(void) if (!players[i].spectator) { numingame++; - if (cv_ingamecap.value && numingame >= cv_ingamecap.value) // DON'T allow if you've hit the in-game player cap + if (cv_maxplayers.value && numingame >= cv_maxplayers.value) // DON'T allow if you've hit the in-game player cap return; if (gamestate != GS_LEVEL) // Allow if you're not in a level continue; @@ -10540,7 +10540,7 @@ void K_CheckSpectateStatus(void) return; // Organize by spectate wait timer - if (cv_ingamecap.value) + if (cv_maxplayers.value) { UINT8 oldrespawnlist[MAXPLAYERS]; memcpy(oldrespawnlist, respawnlist, numjoiners); @@ -10567,7 +10567,7 @@ void K_CheckSpectateStatus(void) // Finally, we can de-spectate everyone! for (i = 0; i < numjoiners; i++) { - if (cv_ingamecap.value && numingame+i >= cv_ingamecap.value) // Hit the in-game player cap while adding people? + if (cv_maxplayers.value && numingame+i >= cv_maxplayers.value) // Hit the in-game player cap while adding people? break; //CONS_Printf("player %s is joining on tic %d\n", player_names[respawnlist[i]], leveltime); P_SpectatorJoinGame(&players[respawnlist[i]]); diff --git a/src/k_menudef.c b/src/k_menudef.c index 39bde4022..45d4b37c2 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -128,23 +128,26 @@ menuitem_t PLAY_RaceDifficulty[] = // netgames {IT_STRING | IT_CVAR, "Difficulty", "Select the game speed", - NULL, {.cvar = &cv_dummykartspeed}, 0, 0}, + NULL, {.cvar = &cv_dummykartspeed}, 0, 0}, // DISABLE THAT OPTION OUTSIDE OF MATCH RACE - {IT_STRING2 | IT_CVAR, "CPU Players", "Enable or disable CPU players.", // 2 whitestring is used by the drawer to know to draw shitstring + {IT_STRING2 | IT_CVAR, "CPU", "Set the difficulty of CPU players.", NULL, {.cvar = &cv_dummymatchbots}, 0, 0}, - {IT_STRING2 | IT_CVAR, "Encore", "Enable or disable Encore mode", // 3 + {IT_STRING2 | IT_CVAR, "Racers", "Sets the number of racers, including players and CPU.", + NULL, {.cvar = &cv_maxplayers}, 0, 0}, + + {IT_STRING2 | IT_CVAR, "Encore", "Enable or disable Encore mode", NULL, {.cvar = &cv_dummygpencore}, 0, 0}, // For GP: - {IT_STRING | IT_CALL, "Cup Select", "Go on and select a cup!", NULL, {.routine = M_LevelSelectInit}, 2, GT_RACE}, // 4 + {IT_STRING | IT_CALL, "Cup Select", "Go on and select a cup!", NULL, {.routine = M_LevelSelectInit}, 2, GT_RACE}, // For Match Race: - {IT_STRING | IT_CALL, "Map Select", "Go on and select a race track!", NULL, {.routine = M_LevelSelectInit}, 0, GT_RACE}, // 5 + {IT_STRING | IT_CALL, "Map Select", "Go on and select a race track!", NULL, {.routine = M_LevelSelectInit}, 0, GT_RACE}, // For Match Race in NETGAMES: - {IT_STRING | IT_CALL, "Map Select", "Go on and select a race track!", NULL, {.routine =M_MPSetupNetgameMapSelect}, 0, GT_RACE}, // 6 + {IT_STRING | IT_CALL, "Map Select", "Go on and select a race track!", NULL, {.routine = M_MPSetupNetgameMapSelect}, 0, GT_RACE}, {IT_STRING | IT_CALL, "Back", NULL, NULL, {.routine = M_GoBack}, 0, 0}, }; @@ -357,7 +360,7 @@ menuitem_t PLAY_MP_Host[] = NULL, {.cvar = &cv_advertise}, 0, 0}, {IT_STRING | IT_CVAR, "Max. Players", "Set how many players can play at once. Others will spectate.", - NULL, {.cvar = &cv_ingamecap}, 0, 0}, + NULL, {.cvar = &cv_maxplayers}, 0, 0}, {IT_STRING | IT_CVAR, "Gamemode", "Are we racing? Or perhaps battling?", NULL, {.cvar = &cv_dummygametype}, 0, 0}, @@ -1144,12 +1147,12 @@ menuitem_t OPTIONS_Server[] = {IT_SPACE | IT_NOTHING, NULL, NULL, NULL, {NULL}, 0, 0}, - {IT_STRING | IT_CVAR, "Ingame Max. Players", "How many players can play at once. 0 Allows everyone who joins.", - NULL, {.cvar = &cv_ingamecap}, 0, 0}, - - {IT_STRING | IT_CVAR, "Server Max. Players", "How many players can connect to the server.", + {IT_STRING | IT_CVAR, "Maximum Players", "How many players can play at once.", NULL, {.cvar = &cv_maxplayers}, 0, 0}, + {IT_STRING | IT_CVAR, "Maximum Connections", "How many players & spectators can connect to the server.", + NULL, {.cvar = &cv_maxconnections}, 0, 0}, + {IT_STRING | IT_CVAR, "Allow Joining", "Sets whether players can connect to your server.", NULL, {.cvar = &cv_allownewplayer}, 0, 0}, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 25677a87b..73e4d0d92 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1627,7 +1627,7 @@ void M_DrawRaceDifficulty(void) } } - y += 12; + y += 10; break; } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index edfd94767..27af857e9 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3135,21 +3135,23 @@ void M_SetupDifficultySelect(INT32 choice) PLAY_RaceDifficulty[4].status = IT_DISABLED; PLAY_RaceDifficulty[5].status = IT_DISABLED; PLAY_RaceDifficulty[6].status = IT_DISABLED; + PLAY_RaceDifficulty[7].status = IT_DISABLED; if (choice) // Match Race { PLAY_RaceDifficulty[1].status = IT_STRING|IT_CVAR; // Kart Speed - PLAY_RaceDifficulty[2].status = IT_STRING2|IT_CVAR; // CPUs on/off use string2 to signify not to use the normal gm font drawer - PLAY_RaceDifficulty[3].status = IT_STRING2|IT_CVAR; // Encore on/off use string2 to signify not to use the normal gm font drawer - PLAY_RaceDifficulty[5].status = IT_STRING|IT_CALL; // Level Select (Match Race) + PLAY_RaceDifficulty[2].status = IT_STRING2|IT_CVAR; // CPUs on/off + PLAY_RaceDifficulty[3].status = IT_STRING2|IT_CVAR; // CPU amount + PLAY_RaceDifficulty[4].status = IT_STRING2|IT_CVAR; // Encore on/off + PLAY_RaceDifficulty[6].status = IT_STRING|IT_CALL; // Level Select (Match Race) PLAY_RaceDifficultyDef.lastOn = 5; // Select cup select by default. } else // GP { PLAY_RaceDifficulty[0].status = IT_STRING|IT_CVAR; // Difficulty - PLAY_RaceDifficulty[3].status = IT_STRING2|IT_CVAR; // Encore on/off use string2 to signify not to use the normal gm font drawer - PLAY_RaceDifficulty[4].status = IT_STRING|IT_CALL; // Level Select (GP) + PLAY_RaceDifficulty[4].status = IT_STRING2|IT_CVAR; // Encore on/off + PLAY_RaceDifficulty[5].status = IT_STRING|IT_CALL; // Level Select (GP) PLAY_RaceDifficultyDef.lastOn = 4; // Select cup select by default. } @@ -3437,8 +3439,8 @@ void M_CupSelectHandler(INT32 choice) memset(&grandprixinfo, 0, sizeof(struct grandprixinfo)); - if (cv_maxplayers.value < ssplayers+1) - CV_SetValue(&cv_maxplayers, ssplayers+1); + if (cv_maxconnections.value < ssplayers+1) + CV_SetValue(&cv_maxconnections, ssplayers+1); if (splitscreen != ssplayers) { @@ -3599,8 +3601,8 @@ void M_LevelSelectHandler(INT32 choice) /*if (levellist.choosemap == 0) levellist.choosemap = G_RandMap(G_TOLFlag(levellist.newgametype), -1, 0, 0, false, NULL);*/ - if (cv_maxplayers.value < ssplayers+1) - CV_SetValue(&cv_maxplayers, ssplayers+1); + if (cv_maxconnections.value < ssplayers+1) + CV_SetValue(&cv_maxconnections, ssplayers+1); if (splitscreen != ssplayers) { diff --git a/src/st_stuff.c b/src/st_stuff.c index 2dff31539..97678b244 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1054,7 +1054,7 @@ static void ST_overlayDrawer(void) else if (G_GametypeHasTeams()) itemtxt = M_GetText("Item - Join Team"); - if (cv_ingamecap.value) + if (cv_maxplayers.value) { UINT8 numingame = 0; UINT8 i; @@ -1063,7 +1063,7 @@ static void ST_overlayDrawer(void) if (playeringame[i] && !players[i].spectator) numingame++; - itemtxt = va("%s (%s: %d)", itemtxt, M_GetText("Slots left"), max(0, cv_ingamecap.value - numingame)); + itemtxt = va("%s (%s: %d)", itemtxt, M_GetText("Slots left"), max(0, cv_maxplayers.value - numingame)); } // SRB2kart: changed positions & text From 02a4b1851205d717eef6a57b76ede8adc243e597 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 29 Aug 2022 04:43:54 -0400 Subject: [PATCH 322/379] Make splitdevice console cvar This shit has only gotten in my way, and it will absolutely destroy someone just trying to set up the game casually --- src/d_netcmd.c | 2 +- src/k_menufunc.c | 45 +-------------------------------------------- 2 files changed, 2 insertions(+), 45 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 48d82e41d..3b328152a 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -320,7 +320,7 @@ consvar_t cv_currprofile = CVAR_INIT ("currprofile", "-1", CV_HIDDEN, lastprofil consvar_t cv_ttlprofilen = CVAR_INIT ("ttlprofilen", "0", CV_SAVE, lastprofile_cons_t, NULL); // Cvar for using splitscreen with 1 device. -consvar_t cv_splitdevice = CVAR_INIT ("splitdevice", "Off", CV_HIDDEN, CV_OnOff, NULL); +consvar_t cv_splitdevice = CVAR_INIT ("splitdevice", "Off", CV_SAVE, CV_OnOff, NULL); consvar_t cv_skipmapcheck = CVAR_INIT ("skipmapcheck", "Off", CV_SAVE, CV_OnOff, NULL); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 27af857e9..8a8b96237 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -951,8 +951,7 @@ void M_StartControlPanel(void) itemOn = 0; - CV_StealthSetValue(&cv_currprofile, -1); // Make sure to reset that as it is set by PR_ApplyProfile which we kind of hack together to force it. - CV_StealthSetValue(&cv_splitdevice, 0); // Disable this option by default. + CV_StealthSetValue(&cv_currprofile, -1); // Make sure to reset that as it is set by PR_ApplyProfile which we kind of hack together to force it. } else { @@ -2108,9 +2107,6 @@ void M_CharacterSelectInit(void) //CONS_Printf("Device for %d set to %d\n", i, -1); } - - // On main menu, reset that! - CV_StealthSetValue(&cv_splitdevice, 0); } //CONS_Printf("========\n"); @@ -2815,42 +2811,6 @@ static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num) } } -// -static void M_HandleSplitDevice(void) -{ - const UINT8 pid = 0; - setup_player_t *p = &setup_player[setup_numplayers]; - - if ((menucmd[pid].buttons & (MBT_L|MBT_R)) != (MBT_L|MBT_R)) - return; - - if (!(menucmd[pid].buttonsHeld & MBT_L) == !(menucmd[pid].buttonsHeld & MBT_R)) - return; - - if (setup_numplayers > 1 && !cv_splitdevice.value) - return; - - if (setup_numplayers == 4) - { - S_StartSound(NULL, sfx_s3k85); - while (setup_numplayers > 1) - { - setup_numplayers--; - setup_player[setup_numplayers].mdepth = CSSTEP_NONE; - CV_StealthSetValue(&cv_usejoystick[setup_numplayers], -1); - } - CV_StealthSetValue(&cv_splitdevice, 0); - return; - } - - if (!cv_splitdevice.value) - M_StartMessage(M_GetText("Split device enabled.\nP1 can add extra players with [L]+[R].\nP1 must set all Players' parameters.\n\nIntended for use for multiplayer games\non the same device (Keyboard...)\nand testing purposes.\n\nPress any key"), NULL, MM_NOTHING); - - CV_StealthSetValue(&cv_splitdevice, 1); - S_StartSound(NULL, sfx_s3k65); - p->mdepth = CSSTEP_PROFILE; // Ready the player setup. -} - boolean M_CharacterSelectHandler(INT32 choice) { INT32 i; @@ -2866,9 +2826,6 @@ boolean M_CharacterSelectHandler(INT32 choice) { if (!optionsmenu.profile) { - if (p->mdepth > CSSTEP_NONE && i == 0) - M_HandleSplitDevice(); - // If splitdevice is true, only do the last non-ready setups. if (cv_splitdevice.value) { From d22c7edd3072d27837bfaa47c9f3f7fdb81532eb Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 29 Aug 2022 06:27:45 -0400 Subject: [PATCH 323/379] Show current party on the menus --- src/k_menudraw.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 73e4d0d92..e4d21f105 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -223,8 +223,133 @@ void M_DrawMenuBackground(void) } } +static void M_DrawMenuParty(void) +{ + const INT32 PLATTER_WIDTH = 19; + const INT32 PLATTER_STAGGER = 6; + const INT32 PLATTER_OFFSET = (PLATTER_WIDTH - PLATTER_STAGGER); + + patch_t *small = W_CachePatchName("MENUPLRA", PU_CACHE); + patch_t *large = W_CachePatchName("MENUPLRB", PU_CACHE); + + INT32 x, y; + INT32 skin; + UINT16 color; + UINT8 *colormap; + + if (setup_numplayers == 0 || currentMenu == &PLAY_CharSelectDef) + { + return; + } + + x = 2; + y = BASEVIDHEIGHT - small->height - 2; + + switch (setup_numplayers) + { + case 1: + { + x -= 8; + V_DrawScaledPatch(x, y, 0, small); + + skin = R_SkinAvailable(cv_skin[0].string); + color = cv_playercolor[0].value; + colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); + + V_DrawMappedPatch(x + 22, y + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap); + break; + } + case 2: + { + x -= 8; + V_DrawScaledPatch(x, y, 0, small); + V_DrawScaledPatch(x + PLATTER_OFFSET, y - PLATTER_STAGGER, 0, small); + + skin = R_SkinAvailable(cv_skin[1].string); + color = cv_playercolor[1].value; + colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); + + V_DrawMappedPatch(x + PLATTER_OFFSET + 22, y - PLATTER_STAGGER + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap); + + skin = R_SkinAvailable(cv_skin[0].string); + color = cv_playercolor[0].value; + colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); + + V_DrawMappedPatch(x + 22, y + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap); + break; + } + case 3: + { + V_DrawScaledPatch(x, y, 0, large); + V_DrawScaledPatch(x + PLATTER_OFFSET, y - PLATTER_STAGGER, 0, small); + + skin = R_SkinAvailable(cv_skin[1].string); + color = cv_playercolor[1].value; + colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); + + V_DrawMappedPatch(x + PLATTER_OFFSET + 22, y - PLATTER_STAGGER + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap); + + skin = R_SkinAvailable(cv_skin[0].string); + color = cv_playercolor[0].value; + colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); + + V_DrawMappedPatch(x + 12, y - 2, 0, faceprefix[skin][FACE_MINIMAP], colormap); + + skin = R_SkinAvailable(cv_skin[2].string); + color = cv_playercolor[2].value; + colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); + + V_DrawMappedPatch(x + 22, y + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap); + break; + } + case 4: + { + V_DrawScaledPatch(x, y, 0, large); + V_DrawScaledPatch(x + PLATTER_OFFSET, y - PLATTER_STAGGER, 0, large); + + skin = R_SkinAvailable(cv_skin[1].string); + color = cv_playercolor[1].value; + colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); + + V_DrawMappedPatch(x + PLATTER_OFFSET + 12, y - PLATTER_STAGGER - 2, 0, faceprefix[skin][FACE_MINIMAP], colormap); + + skin = R_SkinAvailable(cv_skin[0].string); + color = cv_playercolor[0].value; + colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); + + V_DrawMappedPatch(x + 12, y - 2, 0, faceprefix[skin][FACE_MINIMAP], colormap); + + skin = R_SkinAvailable(cv_skin[3].string); + color = cv_playercolor[3].value; + colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); + + V_DrawMappedPatch(x + PLATTER_OFFSET + 22, y - PLATTER_STAGGER + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap); + + skin = R_SkinAvailable(cv_skin[2].string); + color = cv_playercolor[2].value; + colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); + + V_DrawMappedPatch(x + 22, y + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap); + break; + } + default: + { + return; + } + } + + x += PLATTER_WIDTH; + y += small->height; + V_DrawScaledPatch(x + 16, y - 12, 0, W_CachePatchName(va("OPPRNK0%d", setup_numplayers % 10), PU_CACHE)); +} + void M_DrawMenuForeground(void) { + if (gamestate == GS_MENU) + { + M_DrawMenuParty(); + } + // draw non-green resolution border if ((vid.width % BASEVIDWIDTH != 0) || (vid.height % BASEVIDHEIGHT != 0)) { From d5a4954dd309694117e3c2875ee99c1dcfabf860 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 30 Aug 2022 03:51:35 -0400 Subject: [PATCH 324/379] Profiles handle power level properly now Overwriting the global power level all the time and not updating any code to use the profile was extremely yikes. This also allows for splitscreen players with power levels. --- src/d_main.c | 1 - src/d_netcmd.c | 10 ++++--- src/g_game.c | 13 --------- src/k_menudraw.c | 2 +- src/k_menufunc.c | 4 --- src/k_profiles.c | 74 ++++++++++++++++++++++++++++++++++-------------- src/k_profiles.h | 5 +++- src/k_pwrlv.c | 31 ++++++++++++-------- src/k_pwrlv.h | 1 - src/m_cond.c | 17 ++++++++++- src/p_user.c | 2 +- 11 files changed, 99 insertions(+), 61 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index adf9af079..863153060 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1538,7 +1538,6 @@ void D_SRB2Main(void) // Load Profiles now that default controls have been defined PR_LoadProfiles(); // load control profiles - PR_SaveProfiles(); // Test @TODO: remove this lol M_Init(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 3b328152a..cd78e3d19 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1886,21 +1886,23 @@ static void Got_LeaveParty(UINT8 **cp,INT32 playernum) void D_SendPlayerConfig(UINT8 n) { + const profile_t *pr = PR_GetProfile(cv_lastprofile[n].value); + UINT8 buf[4]; UINT8 *p = buf; SendNameAndColor(n); SendWeaponPref(n); - if (n == 0) + if (pr != NULL) { // Send it over - WRITEUINT16(p, vspowerlevel[PWRLV_RACE]); - WRITEUINT16(p, vspowerlevel[PWRLV_BATTLE]); + WRITEUINT16(p, pr->powerlevels[PWRLV_RACE]); + WRITEUINT16(p, pr->powerlevels[PWRLV_BATTLE]); } else { - // Splitscreen players have invalid powerlevel + // Guest players have no power level WRITEUINT16(p, 0); WRITEUINT16(p, 0); } diff --git a/src/g_game.c b/src/g_game.c index c4a010cf3..7315ad2fd 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3984,9 +3984,6 @@ void G_LoadGameData(void) totalplaytime = 0; // total play time (separate from all) matchesplayed = 0; // SRB2Kart: matches played & finished - for (i = 0; i < PWRLV_NUMTYPES; i++) // SRB2Kart: online rank system - vspowerlevel[i] = PWRLVRECORD_START; - if (M_CheckParm("-nodata")) return; // Don't load. @@ -4022,13 +4019,6 @@ void G_LoadGameData(void) totalplaytime = READUINT32(save_p); matchesplayed = READUINT32(save_p); - for (i = 0; i < PWRLV_NUMTYPES; i++) - { - vspowerlevel[i] = READUINT16(save_p); - if (vspowerlevel[i] < PWRLVRECORD_MIN || vspowerlevel[i] > PWRLVRECORD_MAX) - goto datacorrupt; - } - modded = READUINT8(save_p); // Aha! Someone's been screwing with the save file! @@ -4145,9 +4135,6 @@ void G_SaveGameData(void) WRITEUINT32(save_p, totalplaytime); WRITEUINT32(save_p, matchesplayed); - for (i = 0; i < PWRLV_NUMTYPES; i++) - WRITEUINT16(save_p, vspowerlevel[i]); - WRITEUINT8(save_p, (UINT8)savemoddata); // TODO put another cipher on these things? meh, I don't care... diff --git a/src/k_menudraw.c b/src/k_menudraw.c index e4d21f105..5d49dfca9 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1340,7 +1340,7 @@ static void M_DrawCharSelectPreview(UINT8 num) INT16 py = y+48 - p->profilen*12; UINT8 maxp = PR_GetNumProfiles(); - for (i=0; i < maxp; i++) + for (i = 0; i < maxp; i++) { profile_t *pr = PR_GetProfile(i); INT16 dist = abs(p->profilen - i); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 8a8b96237..07106d111 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -330,8 +330,6 @@ void Addons_option_Onchange(void) static void M_EraseDataResponse(INT32 ch) { - UINT8 i; - if (ch == MA_NO) return; @@ -343,8 +341,6 @@ static void M_EraseDataResponse(INT32 ch) // SRB2Kart: This actually needs to be done FIRST, so that you don't immediately regain playtime/matches secrets totalplaytime = 0; matchesplayed = 0; - for (i = 0; i < PWRLV_NUMTYPES; i++) - vspowerlevel[i] = PWRLVRECORD_START; } if (optionsmenu.erasecontext != 1) G_ClearRecords(); diff --git a/src/k_profiles.c b/src/k_profiles.c index 3f3cce4fe..4bddd04ad 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -16,8 +16,8 @@ #include "r_skins.h" // List of all the profiles. -static profile_t *profilesList[MAXPROFILES+1]; // +1 because we're gonna add a default "GUEST' profile. -static UINT8 numprofiles = 0; // # of loaded profiles +static profile_t *profilesList[MAXPROFILES+1]; // +1 because we're gonna add a default "GUEST' profile. +static UINT8 numprofiles = 0; // # of loaded profiles INT32 PR_GetNumProfiles(void) { @@ -47,7 +47,9 @@ profile_t* PR_MakeProfile(const char *prname, const char *pname, const char *sna // Init both power levels for (i = 0; i < PWRLV_NUMTYPES; i++) + { new->powerlevels[i] = PWRLVRECORD_START; + } return new; } @@ -89,8 +91,11 @@ profile_t* PR_GetProfile(INT32 num) boolean PR_DeleteProfile(INT32 num) { UINT8 i, j; + if (num <= 0 || num > numprofiles) + { return false; + } // If we're deleting inbetween profiles, move everything. if (num < numprofiles) @@ -103,10 +108,15 @@ boolean PR_DeleteProfile(INT32 num) for (j = 0; j < MAXSPLITSCREENPLAYERS; j++) { if (cv_lastprofile[j].value == num) - CV_StealthSetValue(&cv_lastprofile[j], 0); // If we were on the deleted profile, default back to guest. - - else if (cv_lastprofile[j].value == i+1) // Otherwise, shift our lastprofile number down to match the new order. + { + // If we were on the deleted profile, default back to guest. + CV_StealthSetValue(&cv_lastprofile[j], PROFILE_GUEST); + } + else if (cv_lastprofile[j].value == i+1) + { + // Otherwise, shift our lastprofile number down to match the new order. CV_StealthSetValue(&cv_lastprofile[j], cv_lastprofile[j].value-1); + } } } } @@ -158,14 +168,6 @@ void PR_SaveProfiles(void) { FILE *f = NULL; - // save powerlevel in the current profile. - // granted we're using a profile that isn't guest, that is. - if (cv_currprofile.value > 0) - { - profile_t *pr = PR_GetProfile(cv_currprofile.value); - memcpy(&pr->powerlevels, vspowerlevel, sizeof(vspowerlevel)); - } - f = fopen(va(pandf, srb2home, PROFILESFILE), "w"); if (f != NULL) { @@ -192,11 +194,11 @@ void PR_LoadProfiles(void) if (f != NULL) { - INT32 i; + INT32 i, j; fread(&numprofiles, sizeof numprofiles, 1, f); - for (i = 1; i < numprofiles; ++i) + for (i = PROFILE_GUEST+1; i < numprofiles; ++i) { profilesList[i] = Z_Malloc(sizeof(profile_t), PU_STATIC, NULL); fread(profilesList[i], sizeof(profile_t), 1, f); @@ -223,12 +225,22 @@ void PR_LoadProfiles(void) { profilesList[i]->followercolor = PROFILEDEFAULTFOLLOWERCOLOR; } + + for (j = 0; j < PWRLV_NUMTYPES; j++) + { + if (profilesList[i]->powerlevels[j] < PWRLVRECORD_MIN + || profilesList[i]->powerlevels[j] > PWRLVRECORD_MAX) + { + // invalid, reset + profilesList[i]->powerlevels[j] = PWRLVRECORD_START; + } + } } fclose(f); // Overwrite the first profile for the default profile to avoid letting anyone tamper with it. - profilesList[0] = dprofile; + profilesList[PROFILE_GUEST] = dprofile; } else { @@ -286,12 +298,9 @@ void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum) CV_StealthSetValue(&cv_lastprofile[playernum], profilenum); // If we're doing this on P1, also change current profile. - // and update the powerlevel local array. - - if (!playernum) + if (playernum == 0) { CV_StealthSetValue(&cv_currprofile, profilenum); - memcpy(&vspowerlevel, p->powerlevels, sizeof(p->powerlevels)); } } @@ -325,11 +334,32 @@ SINT8 PR_ProfileUsedBy(profile_t *p) UINT8 i; UINT8 prn = PR_GetProfileNum(p); - for (i=0; i < MAXSPLITSCREENPLAYERS; i++) + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { if (prn == cv_lastprofile[i].value) return i; } return -1; -} \ No newline at end of file +} + +profile_t *PR_GetPlayerProfile(player_t *player) +{ + const UINT8 playerNum = (player - players); + UINT8 i; + + if (demo.playback) + { + return NULL; + } + + for (i = 0; i <= splitscreen; i++) + { + if (playerNum == g_localplayers[i]) + { + return PR_GetProfile(cv_lastprofile[i].value); + } + } + + return NULL; +} diff --git a/src/k_profiles.h b/src/k_profiles.h index 8525a1b54..0e6bf4ab2 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -30,6 +30,7 @@ #define PROFILEVER 1 #define MAXPROFILES 16 #define PROFILESFILE "ringprofiles.cfg" +#define PROFILE_GUEST 0 #define PROFILEDEFAULTNAME "guest" #define PROFILEDEFAULTPNAME "Player" @@ -58,7 +59,7 @@ typedef struct profile_s char follower[SKINNAMESIZE+1]; // Follower UINT16 followercolor; // Follower color - UINT16 powerlevels[PWRLV_NUMTYPES]; // PWRLV for race & battle. + UINT16 powerlevels[PWRLV_NUMTYPES]; // PWRLV for each gametype. // Player-specific consvars. // @TODO: List all of those @@ -134,4 +135,6 @@ UINT8 PR_GetProfileNum(profile_t *p); // If the profile belongs to no player, then this returns -1 SINT8 PR_ProfileUsedBy(profile_t *p); +profile_t *PR_GetPlayerProfile(player_t *player); + #endif diff --git a/src/k_pwrlv.c b/src/k_pwrlv.c index cd3ee1827..d5cc7e2ab 100644 --- a/src/k_pwrlv.c +++ b/src/k_pwrlv.c @@ -17,10 +17,7 @@ #include "p_tick.h" // leveltime #include "k_grandprix.h" #include "k_boss.h" - -// Online rankings for the main gametypes. -// This array is saved to the gamedata. -UINT16 vspowerlevel[PWRLV_NUMTYPES]; +#include "k_profiles.h" // Client-sided calculations done for Power Levels. // This is done so that clients will never be able to hack someone else's score over the server. @@ -370,6 +367,7 @@ INT16 K_FinalPowerIncrement(player_t *player, INT16 yourPower, INT16 baseInc) // Get at least one point. inc = 1; } +#if 0 else { // You trade points in 1v1s, @@ -388,6 +386,7 @@ INT16 K_FinalPowerIncrement(player_t *player, INT16 yourPower, INT16 baseInc) } } } +#endif } if (yourPower + inc > PWRLVRECORD_MAX) @@ -416,14 +415,16 @@ void K_CashInPowerLevels(void) { if (playeringame[i] == true && powerType != PWRLV_DISABLED) { + profile_t *pr = PR_GetPlayerProfile(&players[i]); INT16 inc = K_FinalPowerIncrement(&players[i], clientpowerlevels[i][powerType], clientPowerAdd[i]); + clientpowerlevels[i][powerType] += inc; //CONS_Printf("%s: %d -> %d (%d)\n", player_names[i], clientpowerlevels[i][powerType] - inc, clientpowerlevels[i][powerType], inc); - if (!demo.playback && i == consoleplayer && inc != 0) + if (pr != NULL && inc != 0) { - vspowerlevel[powerType] = clientpowerlevels[i][powerType]; + pr->powerlevels[powerType] = clientpowerlevels[i][powerType]; if (M_UpdateUnlockablesAndExtraEmblems()) { @@ -567,6 +568,7 @@ void K_SetPowerLevelScrambles(SINT8 powertype) void K_PlayerForfeit(UINT8 playerNum, boolean pointLoss) { + profile_t *pr; UINT8 p = 0; SINT8 powerType = PWRLV_DISABLED; @@ -631,17 +633,22 @@ void K_PlayerForfeit(UINT8 playerNum, boolean pointLoss) K_UpdatePowerLevelsOnFailure(&players[playerNum]); inc = K_FinalPowerIncrement(&players[playerNum], yourPower, clientPowerAdd[playerNum]); - if (inc >= 0) + if (inc == 0) { - // Don't record no change or increases. + // No change return; } - // pointLoss isn't set for stuff like sync-outs, - // which shouldn't be so harsh on the victim! - if (!demo.playback && pointLoss == true && playerNum == consoleplayer) + if (inc < 0 && pointLoss == false) { - vspowerlevel[powerType] = yourPower + inc; + // Don't record point losses for sync-out / crashes. + return; + } + + pr = PR_GetPlayerProfile(&players[playerNum]); + if (pr != NULL) + { + pr->powerlevels[powerType] = yourPower + inc; if (M_UpdateUnlockablesAndExtraEmblems()) { diff --git a/src/k_pwrlv.h b/src/k_pwrlv.h index 9e5645c2f..0dff84774 100644 --- a/src/k_pwrlv.h +++ b/src/k_pwrlv.h @@ -31,7 +31,6 @@ typedef enum extern SINT8 speedscramble; extern SINT8 encorescramble; -extern UINT16 vspowerlevel[PWRLV_NUMTYPES]; extern UINT16 clientpowerlevels[MAXPLAYERS][PWRLV_NUMTYPES]; extern INT16 clientPowerAdd[MAXPLAYERS]; extern UINT8 spectateGriefed; diff --git a/src/m_cond.c b/src/m_cond.c index ceff3c167..1b99010b9 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -20,7 +20,9 @@ #include "g_game.h" // record info #include "r_skins.h" // numskins #include "r_draw.h" // R_GetColorByName + #include "k_pwrlv.h" +#include "k_profiles.h" // Map triggers for linedef executors // 32 triggers, one bit each @@ -108,7 +110,20 @@ UINT8 M_CheckCondition(condition_t *cn) case UC_MATCHESPLAYED: // Requires any level completed >= x times return (matchesplayed >= (unsigned)cn->requirement); case UC_POWERLEVEL: // Requires power level >= x on a certain gametype - return (vspowerlevel[cn->extrainfo1] >= (unsigned)cn->requirement); + { + UINT8 i; + for (i = PROFILE_GUEST; i < PR_GetNumProfiles(); i++) + { + profile_t *p = PR_GetProfile(i); + + if (p->powerlevels[cn->extrainfo1] >= (unsigned)cn->requirement) + { + return true; + } + } + + return false; + } case UC_GAMECLEAR: // Requires game beaten >= x times return (timesBeaten >= (unsigned)cn->requirement); case UC_OVERALLTIME: // Requires overall time <= x diff --git a/src/p_user.c b/src/p_user.c index 748d4d525..77ee3624a 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1099,7 +1099,7 @@ boolean P_IsMachineLocalPlayer(player_t *player) return false; } - for (i = 0; i <= r_splitscreen; i++) + for (i = 0; i <= splitscreen; i++) { if (player == &players[g_localplayers[i]]) return true; From 8228377a0eaf8c82c571f08cdea2bd0cc1088b1c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 30 Aug 2022 03:52:41 -0400 Subject: [PATCH 325/379] Save ringprofiles as a "prf" file It shouldn't be cfg since it's raw data, not a text format. --- src/k_profiles.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_profiles.h b/src/k_profiles.h index 0e6bf4ab2..775e12c91 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -29,7 +29,7 @@ #define PROFILENAMELEN 6 #define PROFILEVER 1 #define MAXPROFILES 16 -#define PROFILESFILE "ringprofiles.cfg" +#define PROFILESFILE "ringprofiles.prf" #define PROFILE_GUEST 0 #define PROFILEDEFAULTNAME "guest" From abac095d06dbcc156b7607f3a5253f0426dce84d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 30 Aug 2022 04:07:42 -0400 Subject: [PATCH 326/379] Properly use 0 power for the guest player --- src/k_profiles.c | 31 ++++++++++++++++++++++++++----- src/k_profiles.h | 13 ++++++++++--- src/y_inter.c | 2 +- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/k_profiles.c b/src/k_profiles.c index 4bddd04ad..482f748f3 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -24,7 +24,13 @@ INT32 PR_GetNumProfiles(void) return numprofiles; } -profile_t* PR_MakeProfile(const char *prname, const char *pname, const char *sname, const UINT16 col, const char *fname, UINT16 fcol, INT32 controlarray[num_gamecontrols][MAXINPUTMAPPING]) +profile_t* PR_MakeProfile( + const char *prname, + const char *pname, + const char *sname, const UINT16 col, + const char *fname, const UINT16 fcol, + INT32 controlarray[num_gamecontrols][MAXINPUTMAPPING], + boolean guest) { profile_t *new = Z_Malloc(sizeof(profile_t), PU_STATIC, NULL); UINT8 i; @@ -48,7 +54,7 @@ profile_t* PR_MakeProfile(const char *prname, const char *pname, const char *sna // Init both power levels for (i = 0; i < PWRLV_NUMTYPES; i++) { - new->powerlevels[i] = PWRLVRECORD_START; + new->powerlevels[i] = (guest ? 0 : PWRLVRECORD_START); } return new; @@ -57,7 +63,7 @@ profile_t* PR_MakeProfile(const char *prname, const char *pname, const char *sna profile_t* PR_MakeProfileFromPlayer(const char *prname, const char *pname, const char *sname, const UINT16 col, const char *fname, UINT16 fcol, UINT8 pnum) { // Generate profile using the player's gamecontrol, as we set them directly when making profiles from menus. - profile_t *new = PR_MakeProfile(prname, pname, sname, col, fname, fcol, gamecontrol[pnum]); + profile_t *new = PR_MakeProfile(prname, pname, sname, col, fname, fcol, gamecontrol[pnum], false); // Player bound cvars: new->kickstartaccel = cv_kickstartaccel[pnum].value; @@ -160,7 +166,14 @@ void PR_InitNewProfile(void) } } - dprofile = PR_MakeProfile(pname, PROFILEDEFAULTPNAME, PROFILEDEFAULTSKIN, PROFILEDEFAULTCOLOR, PROFILEDEFAULTFOLLOWER, PROFILEDEFAULTFOLLOWERCOLOR, gamecontroldefault); + dprofile = PR_MakeProfile( + pname, + PROFILEDEFAULTPNAME, + PROFILEDEFAULTSKIN, PROFILEDEFAULTCOLOR, + PROFILEDEFAULTFOLLOWER, PROFILEDEFAULTFOLLOWERCOLOR, + gamecontroldefault, + false + ); PR_AddProfile(dprofile); } @@ -189,7 +202,15 @@ void PR_SaveProfiles(void) void PR_LoadProfiles(void) { FILE *f = NULL; - profile_t *dprofile = PR_MakeProfile(PROFILEDEFAULTNAME, PROFILEDEFAULTPNAME, PROFILEDEFAULTSKIN, PROFILEDEFAULTCOLOR, PROFILEDEFAULTFOLLOWER, PROFILEDEFAULTFOLLOWERCOLOR, gamecontroldefault); + profile_t *dprofile = PR_MakeProfile( + PROFILEDEFAULTNAME, + PROFILEDEFAULTPNAME, + PROFILEDEFAULTSKIN, PROFILEDEFAULTCOLOR, + PROFILEDEFAULTFOLLOWER, PROFILEDEFAULTFOLLOWERCOLOR, + gamecontroldefault, + true + ); + f = fopen(va(pandf, srb2home, PROFILESFILE), "r"); if (f != NULL) diff --git a/src/k_profiles.h b/src/k_profiles.h index 775e12c91..b86ef6d50 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -32,8 +32,8 @@ #define PROFILESFILE "ringprofiles.prf" #define PROFILE_GUEST 0 -#define PROFILEDEFAULTNAME "guest" -#define PROFILEDEFAULTPNAME "Player" +#define PROFILEDEFAULTNAME "GUEST" +#define PROFILEDEFAULTPNAME "Guest" #define PROFILEDEFAULTSKIN "eggman" #define PROFILEDEFAULTCOLOR SKINCOLOR_NONE #define PROFILEDEFAULTFOLLOWER "none" @@ -78,7 +78,14 @@ INT32 PR_GetNumProfiles(void); // PR_MakeProfile // Makes a profile from the supplied profile name, player name, colour, follower, followercolour and controls. // The consvar values are left untouched. -profile_t* PR_MakeProfile(const char *prname, const char *pname, const char *sname, const UINT16 col, const char *fname, UINT16 fcol, INT32 controlarray[num_gamecontrols][MAXINPUTMAPPING]); +profile_t* PR_MakeProfile( + const char *prname, + const char *pname, + const char *sname, const UINT16 col, + const char *fname, const UINT16 fcol, + INT32 controlarray[num_gamecontrols][MAXINPUTMAPPING], + boolean guest +); // PR_MakeProfileFromPlayer // Makes a profile_t from the supplied profile name, player name, colour, follower and followercolour. diff --git a/src/y_inter.c b/src/y_inter.c index b005f1f01..be38fc510 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -504,7 +504,7 @@ void Y_IntermissionDrawer(void) { if (powertype != PWRLV_DISABLED && !clientpowerlevels[data.num[i]][powertype]) { - // No power level (splitscreen guests) + // No power level (guests) STRBUFCPY(strtime, "----"); } else From 0fe3e6ceeb87e39ab0d95d8e1d9c29c71b5172ec Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 30 Aug 2022 04:42:01 -0400 Subject: [PATCH 327/379] Don't allow 2 players to select the same profile (Excluding the Guest profile, of course.) --- src/k_menudraw.c | 33 +++++++++++++++++++++++++++++---- src/k_menufunc.c | 24 +++++++++++++++++++++++- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 5d49dfca9..09feba4b8 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1335,24 +1335,47 @@ static void M_DrawCharSelectPreview(UINT8 num) // Profile selection if (p->mdepth == CSSTEP_PROFILE) { - UINT8 i = 0; INT16 px = x+12; INT16 py = y+48 - p->profilen*12; UINT8 maxp = PR_GetNumProfiles(); + UINT8 i = 0; + UINT8 j; + for (i = 0; i < maxp; i++) { profile_t *pr = PR_GetProfile(i); INT16 dist = abs(p->profilen - i); + INT32 notSelectable = 0; + SINT8 belongsTo = -1; + + if (i != PROFILE_GUEST) + { + for (j = 0; j < setup_numplayers; j++) + { + if (setup_player[j].mdepth > CSSTEP_PROFILE + && setup_player[j].profilen == i) + { + belongsTo = j; + break; + } + } + } + + if (belongsTo != -1 && belongsTo != num) + { + notSelectable |= V_TRANSLUCENT; + } if (dist > 2) { py += 12; continue; } - else if (dist == 2) + + if (dist == 2) { - V_DrawCenteredFileString(px+26, py, 0, pr->version ? pr->profilename : "NEW"); + V_DrawCenteredFileString(px+26, py, notSelectable, pr->version ? pr->profilename : "NEW"); V_DrawScaledPatch(px, py, V_TRANSLUCENT, W_CachePatchName("FILEBACK", PU_CACHE)); } else @@ -1360,7 +1383,9 @@ static void M_DrawCharSelectPreview(UINT8 num) V_DrawScaledPatch(px, py, 0, W_CachePatchName("FILEBACK", PU_CACHE)); if (i != p->profilen || ((setup_animcounter/10) & 1)) - V_DrawCenteredFileString(px+26, py, 0, pr->version ? pr->profilename : "NEW"); + { + V_DrawCenteredFileString(px+26, py, notSelectable, pr->version ? pr->profilename : "NEW"); + } } py += 12; } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 07106d111..234837032 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2420,13 +2420,35 @@ static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num) } else if (M_MenuConfirmPressed(num)) { + SINT8 belongsTo = -1; + + if (p->profilen != PROFILE_GUEST) + { + for (i = 0; i < setup_numplayers; i++) + { + if (setup_player[i].mdepth > CSSTEP_PROFILE + && setup_player[i].profilen == p->profilen) + { + belongsTo = i; + break; + } + } + } + + if (belongsTo != -1 && belongsTo != num) + { + S_StartSound(NULL, sfx_s3k7b); + M_SetMenuDelay(num); + return false; + } + // Apply the profile. PR_ApplyProfile(p->profilen, realnum); // Otherwise P1 would inherit the last player's profile in splitdevice and that's not what we want... M_SetupProfileGridPos(p); p->changeselect = 0; - if (p->profilen == 0) + if (p->profilen == PROFILE_GUEST) { // Guest profile, always ask for options. p->mdepth = CSSTEP_CHARS; From 6eb5b2dee99ab04f25be79578cc8ad97dbb1f85d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 30 Aug 2022 05:23:54 -0400 Subject: [PATCH 328/379] Make back button properly confirm settings --- src/k_menu.h | 1 + src/k_menudef.c | 4 ++-- src/k_menufunc.c | 29 +++++++++++++++++++---------- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 86d0d2bfe..bf06addd4 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -856,6 +856,7 @@ void M_ProfileSetControl(INT32 ch); void M_MapProfileControl(event_t *ev); void M_ProfileTryController(INT32 choice); +void M_ProfileConfirm(INT32 choice); // video modes menu (resolution) void M_VideoModeMenu(INT32 choice); diff --git a/src/k_menudef.c b/src/k_menudef.c index 45d4b37c2..80af93181 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -680,8 +680,8 @@ menuitem_t OPTIONS_ProfileControls[] = { {IT_STRING | IT_CALL, "TRY MAPPINGS", "Test your controls.", NULL, {.routine = M_ProfileTryController}, 0, 0}, - {IT_STRING | IT_CALL, "BACK...", "Go back to profile setup.", - NULL, {.routine = M_GoBack}, 0, 0}, + {IT_STRING | IT_CALL, "CONFIRM", "Go back to profile setup.", + NULL, {.routine = M_ProfileConfirm}, 0, 0}, }; diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 234837032..00e46c956 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -5113,6 +5113,24 @@ static void M_ProfileControlSaveResponse(INT32 choice) } } +void M_ProfileConfirm(INT32 choice) +{ + (void)choice; + + //M_StartMessage(M_GetText("Exiting will save the control changes\nfor this Profile.\nIs this okay?\n\n(Press A to confirm)"), FUNCPTRCAST(M_ProfileControlSaveResponse), MM_YESNO); + // TODO: Add a graphic for controls saving, instead of obnoxious prompt. + + M_ProfileControlSaveResponse(MA_YES); + + optionsmenu.profile->kickstartaccel = cv_dummyprofilekickstart.value; // Make sure to save kickstart accel. + + // Reapply player 1's real profile. + if (cv_currprofile.value > -1) + { + PR_ApplyProfile(cv_lastprofile[0].value, 0); + } +} + boolean M_ProfileControlsInputs(INT32 ch) { const UINT8 pid = 0; @@ -5165,16 +5183,7 @@ boolean M_ProfileControlsInputs(INT32 ch) } else if (M_MenuBackPressed(pid)) { - //M_StartMessage(M_GetText("Exiting will save the control changes\nfor this Profile.\nIs this okay?\n\n(Press A to confirm)"), FUNCPTRCAST(M_ProfileControlSaveResponse), MM_YESNO); - // TODO: Add a graphic for controls saving, instead of obnoxious prompt. - M_ProfileControlSaveResponse(MA_YES); - - optionsmenu.profile->kickstartaccel = cv_dummyprofilekickstart.value; // Make sure to save kickstart accel. - - // Reapply player 1's real profile. - if (cv_currprofile.value > -1) - PR_ApplyProfile(cv_lastprofile[0].value, 0); - + M_ProfileControlsConfirm(); return true; } From 7c20b667826ac1bf1ab8a0d8094f57eb8feb5592 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 30 Aug 2022 05:25:59 -0400 Subject: [PATCH 329/379] Fix hold controls prompt --- src/k_menudraw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 09feba4b8..393b883db 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -3017,7 +3017,7 @@ void M_DrawProfileControls(void) optionsmenu.tcontx = BASEVIDWIDTH*2/3 - 10; optionsmenu.tconty = BASEVIDHEIGHT/2 +70; - V_DrawCenteredString(160, 180, highlightflags, va("HOLD [X] FOR %d SECONDS TO BACK OUT", optionsmenu.trycontroller/TICRATE)); + V_DrawCenteredString(160, 180, highlightflags, va("PRESS NOTHING FOR %d SEC TO GO BACK", optionsmenu.trycontroller/TICRATE)); return; // Don't draw the rest if we're trying the controller. } From 8b93c051752a73458cfd09e2b3b1fd12a109836b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 30 Aug 2022 05:30:13 -0400 Subject: [PATCH 330/379] Fix party HUD after selecting profile char --- src/k_menu.h | 2 +- src/k_menufunc.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index bf06addd4..8f741e4e8 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -856,7 +856,7 @@ void M_ProfileSetControl(INT32 ch); void M_MapProfileControl(event_t *ev); void M_ProfileTryController(INT32 choice); -void M_ProfileConfirm(INT32 choice); +void M_ProfileControlsConfirm(INT32 choice); // video modes menu (resolution) void M_VideoModeMenu(INT32 choice); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 00e46c956..167bac111 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3013,6 +3013,7 @@ void M_CharacterSelectTick(void) // reset setup_player memset(setup_player, 0, sizeof(setup_player)); + setup_numplayers = 0; M_GoBack(0); return; @@ -5113,7 +5114,7 @@ static void M_ProfileControlSaveResponse(INT32 choice) } } -void M_ProfileConfirm(INT32 choice) +void M_ProfileControlsConfirm(INT32 choice) { (void)choice; From f1717a37ef85047ec1cda928d473a64fb3d60654 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 30 Aug 2022 05:30:50 -0400 Subject: [PATCH 331/379] Fix missing zero --- src/k_menufunc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 167bac111..22c2f3319 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -5184,7 +5184,7 @@ boolean M_ProfileControlsInputs(INT32 ch) } else if (M_MenuBackPressed(pid)) { - M_ProfileControlsConfirm(); + M_ProfileControlsConfirm(0); return true; } From 1af1b9bd1c21abda56368ecc507661c4f0a37abe Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 30 Aug 2022 05:33:04 -0400 Subject: [PATCH 332/379] Fix wrong function name --- src/k_menudef.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_menudef.c b/src/k_menudef.c index 80af93181..d2e76cc9c 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -681,7 +681,7 @@ menuitem_t OPTIONS_ProfileControls[] = { NULL, {.routine = M_ProfileTryController}, 0, 0}, {IT_STRING | IT_CALL, "CONFIRM", "Go back to profile setup.", - NULL, {.routine = M_ProfileConfirm}, 0, 0}, + NULL, {.routine = M_ProfileControlsConfirm}, 0, 0}, }; From c77f9fa558e81a38550c7dd12486d8fd5c943220 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 30 Aug 2022 06:26:35 -0400 Subject: [PATCH 333/379] Add a billion debug prints --- src/k_menufunc.c | 21 +++++++++++---------- src/sdl/i_system.c | 5 +++++ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 22c2f3319..7bd0cee22 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -2099,12 +2099,12 @@ void M_CharacterSelectInit(void) { // Un-set devices for other players. if (i != 0 || optionsmenu.profile) + { CV_SetValue(&cv_usejoystick[i], -1); - - //CONS_Printf("Device for %d set to %d\n", i, -1); + CONS_Printf("M_CharacterSelectInit: Device for %d set to %d\n", i, -1); + } } } - //CONS_Printf("========\n"); memset(setup_chargrid, -1, sizeof(setup_chargrid)); for (i = 0; i < 9; i++) @@ -2329,16 +2329,14 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num) CV_SetValue(&cv_usejoystick[num], i); - //CONS_Printf("Device for %d set to %d\n", num, i); - //CONS_Printf("========\n"); + CONS_Printf("M_HandlePressStart: Device for %d set to %d\n", num, i); for (j = num+1; j < MAXSPLITSCREENPLAYERS; j++) { // Un-set devices for other players. CV_SetValue(&cv_usejoystick[j], -1); - //CONS_Printf("Device for %d set to %d\n", j, -1); + CONS_Printf("M_HandlePressStart: Device for %d set to %d\n", j, -1); } - //CONS_Printf("========\n"); //setup_numplayers++; p->mdepth = CSSTEP_PROFILE; @@ -2407,6 +2405,7 @@ static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num) if (num > 0) { CV_StealthSetValue(&cv_usejoystick[num], -1); + CONS_Printf("M_HandleCSelectProfile: Device for %d set to %d\n", num, -1); } return true; @@ -5015,7 +5014,8 @@ void M_HandleVideoModes(INT32 ch) // sets whatever device has had its key pressed to the active device. // 20/05/22: Commented out for now but not deleted as it might still find some use in the future? -/*static void SetDeviceOnPress(void) +/* +static void SetDeviceOnPress(void) { UINT8 i; @@ -5024,11 +5024,12 @@ void M_HandleVideoModes(INT32 ch) if (deviceResponding[i]) { CV_SetValue(&cv_usejoystick[0], i); // Force-set this joystick as the current joystick we're using for P1 (which is the only one controlling menus) - //CONS_Printf("Using device %d for mappings\n", i); + CONS_Printf("SetDeviceOnPress: Device for %d set to %d\n", 0, i); return; } } -}*/ +} +*/ // Prompt a device selection window (just tap any button on the device you want) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index a89dd649c..da63294d9 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1013,6 +1013,7 @@ void I_UpdateJoystickDeviceIndex(UINT8 player) if (JoyInfo[player].dev) { cv_usejoystick[player].value = I_GetJoystickDeviceIndex(JoyInfo[player].dev) + 1; + CONS_Printf("I_UpdateJoystickDeviceIndex: Device for %d set to %d\n", player, cv_usejoystick[player].value); } else { @@ -1036,6 +1037,7 @@ void I_UpdateJoystickDeviceIndex(UINT8 player) { // We DID make it through the whole loop, so we can use this one! cv_usejoystick[player].value = value; + CONS_Printf("I_UpdateJoystickDeviceIndex: Device for %d set to %d\n", player, cv_usejoystick[player].value); break; } } @@ -1045,6 +1047,7 @@ void I_UpdateJoystickDeviceIndex(UINT8 player) // We DID NOT make it through the whole loop, so we can't assign this joystick to anything. // When you try your best, but you don't succeed... cv_usejoystick[player].value = 0; + CONS_Printf("I_UpdateJoystickDeviceIndex: Device for %d set to %d\n", player, 0); } } } @@ -1235,6 +1238,7 @@ void I_InitJoystick(UINT8 index) if (newcontroller && i < MAXSPLITSCREENPLAYERS) // don't override an active device { cv_usejoystick[index].value = I_GetJoystickDeviceIndex(JoyInfo[index].dev) + 1; + CONS_Printf("I_InitJoystick: Device for %d set to %d\n", index, cv_usejoystick[index].value); } else if (newcontroller && joy_open(index, cv_usejoystick[index].value) != -1) { @@ -1248,6 +1252,7 @@ void I_InitJoystick(UINT8 index) if (JoyInfo[index].oldjoy) I_ShutdownJoystick(index); cv_usejoystick[index].value = 0; + CONS_Printf("I_InitJoystick: Device for %d set to %d\n", index, cv_usejoystick[index].value); joystick_started[index] = 0; } From b0c9fccd2d6c5b05abeb773a0dd4c80f42661a25 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 31 Aug 2022 22:52:06 +0100 Subject: [PATCH 334/379] Rework profile deletion - O(n) instead of O(4n) - Actually free the sacrificed profile's memory - Don't hop to profile 1 after deleting a later profile, to reduce the % of footguns/losing your place in the list. --- src/k_menufunc.c | 4 ++-- src/k_profiles.c | 43 ++++++++++++++++++++++++++++++------------- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 7bd0cee22..b37825586 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -5518,8 +5518,8 @@ static void M_EraseProfileResponse(INT32 choice) F_StartIntro(); M_ClearMenus(true); } - else - optionsmenu.eraseprofilen = 1; + else if (optionsmenu.eraseprofilen > PR_GetNumProfiles()-1) + optionsmenu.eraseprofilen--; } } diff --git a/src/k_profiles.c b/src/k_profiles.c index 482f748f3..19c39d502 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -96,34 +96,47 @@ profile_t* PR_GetProfile(INT32 num) boolean PR_DeleteProfile(INT32 num) { - UINT8 i, j; + UINT8 i; + profile_t* sacrifice; if (num <= 0 || num > numprofiles) { return false; } + sacrifice = profilesList[num]; + // If we're deleting inbetween profiles, move everything. if (num < numprofiles) { for (i = num; i < numprofiles-1; i++) { profilesList[i] = profilesList[i+1]; + } - // Make sure to move cv_lastprofile values as well - for (j = 0; j < MAXSPLITSCREENPLAYERS; j++) + // Make sure to move cv_lastprofile values as well! + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + INT32 profileset = cv_lastprofile[i].value; + + if (profileset < num) { - if (cv_lastprofile[j].value == num) - { - // If we were on the deleted profile, default back to guest. - CV_StealthSetValue(&cv_lastprofile[j], PROFILE_GUEST); - } - else if (cv_lastprofile[j].value == i+1) - { - // Otherwise, shift our lastprofile number down to match the new order. - CV_StealthSetValue(&cv_lastprofile[j], cv_lastprofile[j].value-1); - } + // Not affected. + continue; } + + if (profileset > num) + { + // Shift our lastprofile number down to match the new order. + profileset--; + } + else + { + // There's no hope for it. If we were on the deleted profile, default back to guest. + profileset = PROFILE_GUEST; + } + + CV_StealthSetValue(&cv_lastprofile[i], profileset); } } @@ -132,6 +145,10 @@ boolean PR_DeleteProfile(INT32 num) numprofiles--; PR_SaveProfiles(); + + // Finally, clear up our memory! + Z_Free(sacrifice); + return true; } From 0424f0f07cfd455e40fa5d0c22b161158673ed66 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 31 Aug 2022 23:06:14 +0100 Subject: [PATCH 335/379] Apply last-used title profile selection when game skips over menus (-warp, -server) --- src/d_main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/d_main.c b/src/d_main.c index 863153060..da324bfa0 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1759,6 +1759,10 @@ void D_SRB2Main(void) CV_ClearChangedFlags(); + // Has to be done before anything else so skin, color, etc in command buffer has an affect. + // ttlprofilen used because it's roughly equivalent in functionality - a QoL aid for quickly getting from startup to action + PR_ApplyProfile(cv_ttlprofilen.value, 0); + // Do this here so if you run SRB2 with eg +timelimit 5, the time limit counts // as having been modified for the first game. M_PushSpecialParameters(); // push all "+" parameter at the command buffer From a6843bae78679f079b273d8c587c728e3042e2a8 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 1 Sep 2022 17:40:26 +0100 Subject: [PATCH 336/379] Improve profile deletion further - Fix the check for deleting the current profile - Handle cv_ttlprofilen like cv_lastprofile[] --- src/k_menufunc.c | 6 ++++-- src/k_profiles.c | 9 +++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index b37825586..a09ddadda 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -5507,19 +5507,21 @@ static void M_EraseProfileResponse(INT32 choice) { if (choice == MA_YES) { + const boolean current = (optionsmenu.eraseprofilen == cv_currprofile.value); // has to be grabbed before deletion S_StartSound(NULL, sfx_itrole); // bweh heh heh PR_DeleteProfile(optionsmenu.eraseprofilen); - if (optionsmenu.eraseprofilen == cv_currprofile.value) + if (current) { CV_StealthSetValue(&cv_currprofile, -1); - CV_StealthSetValue(&cv_ttlprofilen, 0); F_StartIntro(); M_ClearMenus(true); } else if (optionsmenu.eraseprofilen > PR_GetNumProfiles()-1) + { optionsmenu.eraseprofilen--; + } } } diff --git a/src/k_profiles.c b/src/k_profiles.c index 19c39d502..34648268a 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -114,10 +114,11 @@ boolean PR_DeleteProfile(INT32 num) profilesList[i] = profilesList[i+1]; } - // Make sure to move cv_lastprofile values as well! - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + // Make sure to move cv_lastprofile (and title profile) values as well! + for (i = 0; i < MAXSPLITSCREENPLAYERS+1; i++) { - INT32 profileset = cv_lastprofile[i].value; + consvar_t *cv = (i == MAXSPLITSCREENPLAYERS) ? &cv_ttlprofilen : &cv_lastprofile[i]; + INT32 profileset = cv->value; if (profileset < num) { @@ -136,7 +137,7 @@ boolean PR_DeleteProfile(INT32 num) profileset = PROFILE_GUEST; } - CV_StealthSetValue(&cv_lastprofile[i], profileset); + CV_StealthSetValue(cv, profileset); } } From cd2bb2b30032d302a5718eadd14aa1b05270767d Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 1 Sep 2022 18:44:29 +0100 Subject: [PATCH 337/379] Extra profile menu improvements * Don't allow creation/loading of more profiles than the game supports. * Add a few missing M_SetMenuDelay()'s * Fix PRF%c default name generation issues * There could still be an infinite loop if MAXPROFILES is ever increased >= 26... but we can handle that one later. --- src/k_menufunc.c | 16 +++++++++++++--- src/k_profiles.c | 12 +++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index a09ddadda..99a1d0d4e 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -4702,9 +4702,15 @@ static void M_StartEditProfile(INT32 c) void M_HandleProfileSelect(INT32 ch) { const UINT8 pid = 0; - const INT32 maxp = PR_GetNumProfiles(); + INT32 maxp = PR_GetNumProfiles(); + boolean creatable = (maxp < MAXPROFILES); (void) ch; + if (!creatable) + { + maxp = MAXPROFILES; + } + if (menucmd[pid].dpad_lr > 0) { optionsmenu.profilen++; @@ -4749,7 +4755,7 @@ void M_HandleProfileSelect(INT32 ch) M_SetMenuDelay(pid); return; } - else if (optionsmenu.profilen == maxp && gamestate != GS_MENU) + else if (creatable && optionsmenu.profilen == maxp && gamestate != GS_MENU) { S_StartSound(NULL, sfx_s3k7b); M_StartMessage(M_GetText("Cannot create a new profile\nmid-game. Return to the\ntitle screen first."), NULL, MM_NOTHING); @@ -4763,7 +4769,7 @@ void M_HandleProfileSelect(INT32 ch) else { // We're on the profile selection screen. - if (optionsmenu.profilen == maxp) + if (creatable && optionsmenu.profilen == maxp) { M_StartEditProfile(MA_YES); M_SetMenuDelay(pid); @@ -4790,6 +4796,7 @@ void M_HandleProfileSelect(INT32 ch) { optionsmenu.resetprofilemenu = true; M_GoBack(0); + M_SetMenuDelay(pid); } if (menutransition.tics == 0 && optionsmenu.resetprofile) @@ -4859,6 +4866,7 @@ boolean M_ProfileEditInputs(INT32 ch) M_SetupNextMenu(&MAIN_ProfilesDef, false); else M_GoBack(0); + M_SetMenuDelay(pid); } return true; } @@ -4910,6 +4918,7 @@ void M_ConfirmProfile(INT32 choice) { M_ProfileEditExit(); M_GoBack(0); + M_SetMenuDelay(pid); } else { @@ -5186,6 +5195,7 @@ boolean M_ProfileControlsInputs(INT32 ch) else if (M_MenuBackPressed(pid)) { M_ProfileControlsConfirm(0); + M_SetMenuDelay(pid); return true; } diff --git a/src/k_profiles.c b/src/k_profiles.c index 34648268a..e88ce6b9c 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -161,10 +161,13 @@ void PR_InitNewProfile(void) UINT8 i; boolean nameok = false; + pname[4] = '\0'; + // When deleting profile, it's possible to do some pretty wacko stuff that would lead a new fresh profile to share the same name as another profile we have never changed the name of. + // This could become an infinite loop if MAXPROFILES >= 26. while (!nameok) { - strcpy(pname, va("PRF%c", 'A'+usenum-1)); + pname[3] = 'A'+usenum; for (i = 0; i < numprofiles; i++) { @@ -172,8 +175,8 @@ void PR_InitNewProfile(void) if (!strcmp(pr->profilename, pname)) { usenum++; - if (usenum > 'Z' -1) - usenum = 'A'; + if (pname[3] == 'Z') + usenum = 0; break; } @@ -237,6 +240,9 @@ void PR_LoadProfiles(void) fread(&numprofiles, sizeof numprofiles, 1, f); + if (numprofiles > MAXPROFILES) + numprofiles = MAXPROFILES; + for (i = PROFILE_GUEST+1; i < numprofiles; ++i) { profilesList[i] = Z_Malloc(sizeof(profile_t), PU_STATIC, NULL); From 36797e6bdeacb22272970c02f328d794d01a9cd2 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 1 Sep 2022 21:32:18 +0100 Subject: [PATCH 338/379] Change the text for skipping charsel with profile info Used to be "CHANGES? No/Yes", but that was confusing. Now "READY? All good/Change" (had to be adjusted from discussed text to fit in the space, even with thinstring) --- src/k_menudraw.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 393b883db..db5854e31 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1395,20 +1395,20 @@ static void M_DrawCharSelectPreview(UINT8 num) else if (p->mdepth == CSSTEP_ASKCHANGES) { UINT8 i; - char choices[][4] = {"NO", "YES"}; + char choices[2][9] = {"All good", "Change"}; INT32 xpos = x+8; INT32 ypos = y+38; - V_DrawFileString(xpos, ypos, 0, "CHANGES?"); + V_DrawFileString(xpos, ypos, 0, "READY?"); for (i = 0; i < 2; i++) { UINT8 cy = ypos+16 + (i*10); if (p->changeselect == i) - V_DrawScaledPatch(xpos+4, cy, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); + V_DrawScaledPatch(xpos, cy, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); - V_DrawString(xpos+20, cy, p->changeselect == i ? highlightflags : 0, choices[i]); + V_DrawThinString(xpos+16, cy, (p->changeselect == i ? highlightflags : 0)|V_6WIDTHSPACE, choices[i]); } } } From 86b1e57f3277d984e7f9c9d39655986d0ce3d3d5 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 1 Sep 2022 21:58:39 +0100 Subject: [PATCH 339/379] Permit KEY_ESCAPE to close the typing view as well. Preparation for reworking Addons menu -- I have been caught in these virtual keyboard popups without remembering how to escape a little too often for comfort. --- src/k_menufunc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 99a1d0d4e..8ad6c3086 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1213,7 +1213,7 @@ static void M_UpdateKeyboardX(void) static boolean M_IsTypingKey(INT32 key) { return key == KEY_BACKSPACE || key == KEY_ENTER || - key == KEY_DEL || isprint(key); + key == KEY_ESCAPE || key == KEY_DEL || isprint(key); } static void M_MenuTypingInput(INT32 key) @@ -1274,7 +1274,7 @@ static void M_MenuTypingInput(INT32 key) } // OTHERWISE, process keyboard inputs for typing! - if (key == KEY_ENTER) + if (key == KEY_ENTER || key == KEY_ESCAPE) { menutyping.menutypingclose = true; // close menu. return; From 5c4ff2ead08960c96d9927e5d08c1c9e304266a2 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 2 Sep 2022 19:38:54 +0100 Subject: [PATCH 340/379] Rewrite Profile saving and loading - Previous implementation used fopen, fwrite, fread, etc. - Instead, use the byteptr.h macros to/from a buffer, performing IO all at once before/after. - This way, if we do something unrecoverable mid-write, we won't corrupt the user's profile. - Also cross-endian compatible AND now capable of supporting changes in the struct. - Sadly not back-compatible. This should be the last time we destroy the team's existing profiles... Also, modify a typo in a gamedata error this system used as reference. --- src/g_game.c | 2 +- src/k_profiles.c | 221 +++++++++++++++++++++++++++++++++-------------- src/k_profiles.h | 3 + 3 files changed, 159 insertions(+), 67 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 7315ad2fd..31b5b2d15 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4013,7 +4013,7 @@ void G_LoadGameData(void) Z_Free(savebuffer); save_p = NULL; - I_Error("Game data is from another version of SRB2.\nDelete %s(maybe in %s) and try again.", gamedatafilename, gdfolder); + I_Error("Game data is from another version of SRB2.\nDelete %s (maybe in %s) and try again.", gamedatafilename, gdfolder); } totalplaytime = READUINT32(save_p); diff --git a/src/k_profiles.c b/src/k_profiles.c index e88ce6b9c..32cba641a 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -11,6 +11,9 @@ /// \brief implements methods for profiles etc. #include "d_main.h" // pandf +#include "byteptr.h" // READ/WRITE macros +#include "p_saveg.h" // save_p +#include "m_misc.h" //FIL_WriteFile() #include "k_profiles.h" #include "z_zone.h" #include "r_skins.h" @@ -198,31 +201,75 @@ void PR_InitNewProfile(void) PR_AddProfile(dprofile); } +static UINT8 *savebuffer; + void PR_SaveProfiles(void) { - FILE *f = NULL; + size_t length = 0; + const size_t headerlen = strlen(PROFILEHEADER); + UINT8 i, j, k; - f = fopen(va(pandf, srb2home, PROFILESFILE), "w"); - if (f != NULL) + save_p = savebuffer = (UINT8 *)malloc(sizeof(UINT32) + (numprofiles * sizeof(profile_t))); + if (!save_p) { - UINT8 i; + I_Error("No more free memory for saving profiles\n"); + return; + } - fwrite(&numprofiles, sizeof numprofiles, 1, f); + // Add header. + WRITESTRINGN(save_p, PROFILEHEADER, headerlen); + WRITEUINT8(save_p, PROFILEVER); + WRITEUINT8(save_p, numprofiles); - for (i = 1; i < numprofiles; ++i) + for (i = 1; i < numprofiles; i++) + { + // Names. + WRITESTRINGN(save_p, profilesList[i]->profilename, PROFILENAMELEN); + WRITESTRINGN(save_p, profilesList[i]->playername, MAXPLAYERNAME); + + // Character and colour. + WRITESTRINGN(save_p, profilesList[i]->skinname, SKINNAMESIZE); + WRITEUINT16(save_p, profilesList[i]->color); + + // Follower and colour. + WRITESTRINGN(save_p, profilesList[i]->follower, SKINNAMESIZE); + WRITEUINT16(save_p, profilesList[i]->followercolor); + + // PWR. + for (j = 0; j < PWRLV_NUMTYPES; j++) { - fwrite(profilesList[i], sizeof(profile_t), 1, f); + WRITEUINT16(save_p, profilesList[i]->powerlevels[j]); } - fclose(f); + // Consvars. + WRITEUINT8(save_p, profilesList[i]->kickstartaccel); + + // Controls. + for (j = 0; j < num_gamecontrols; j++) + { + for (k = 0; k < MAXINPUTMAPPING; k++) + { + WRITEINT32(save_p, profilesList[i]->controls[j][k]); + } + } } - else + + length = save_p - savebuffer; + + if (!FIL_WriteFile(va(pandf, srb2home, PROFILESFILE), savebuffer, length)) + { + free(savebuffer); I_Error("Couldn't save profiles. Are you out of Disk space / playing in a protected folder?"); + } + free(savebuffer); + save_p = savebuffer = NULL; } void PR_LoadProfiles(void) { - FILE *f = NULL; + size_t length = 0; + const size_t headerlen = strlen(PROFILEHEADER); + UINT8 i, j, k, version; profile_t *dprofile = PR_MakeProfile( PROFILEDEFAULTNAME, PROFILEDEFAULTPNAME, @@ -232,66 +279,108 @@ void PR_LoadProfiles(void) true ); - f = fopen(va(pandf, srb2home, PROFILESFILE), "r"); - - if (f != NULL) - { - INT32 i, j; - - fread(&numprofiles, sizeof numprofiles, 1, f); - - if (numprofiles > MAXPROFILES) - numprofiles = MAXPROFILES; - - for (i = PROFILE_GUEST+1; i < numprofiles; ++i) - { - profilesList[i] = Z_Malloc(sizeof(profile_t), PU_STATIC, NULL); - fread(profilesList[i], sizeof(profile_t), 1, f); - - // Attempt to correct numerical footguns - if (profilesList[i]->color == SKINCOLOR_NONE) - { - ; // Valid, even outside the bounds - } - else if (profilesList[i]->color >= numskincolors - || skincolors[profilesList[i]->color].accessible == false) - { - profilesList[i]->color = PROFILEDEFAULTCOLOR; - } - - if (profilesList[i]->followercolor == FOLLOWERCOLOR_MATCH - || profilesList[i]->followercolor == FOLLOWERCOLOR_OPPOSITE) - { - ; // Valid, even outside the bounds - } - else if (profilesList[i]->followercolor >= numskincolors - || profilesList[i]->followercolor == SKINCOLOR_NONE - || skincolors[profilesList[i]->followercolor].accessible == false) - { - profilesList[i]->followercolor = PROFILEDEFAULTFOLLOWERCOLOR; - } - - for (j = 0; j < PWRLV_NUMTYPES; j++) - { - if (profilesList[i]->powerlevels[j] < PWRLVRECORD_MIN - || profilesList[i]->powerlevels[j] > PWRLVRECORD_MAX) - { - // invalid, reset - profilesList[i]->powerlevels[j] = PWRLVRECORD_START; - } - } - } - - fclose(f); - - // Overwrite the first profile for the default profile to avoid letting anyone tamper with it. - profilesList[PROFILE_GUEST] = dprofile; - } - else + length = FIL_ReadFile(va(pandf, srb2home, PROFILESFILE), &savebuffer); + if (!length) { // No profiles. Add the default one. PR_AddProfile(dprofile); + return; } + + save_p = savebuffer; + + if (strncmp(PROFILEHEADER, (const char *)savebuffer, headerlen)) + { + const char *gdfolder = "the Ring Racers folder"; + if (strcmp(srb2home,".")) + gdfolder = srb2home; + + Z_Free(savebuffer); + save_p = NULL; + I_Error("Not a valid Profile file.\nDelete %s (maybe in %s) and try again.", PROFILESFILE, gdfolder); + } + save_p += headerlen; + + version = READUINT8(save_p); + if (version > PROFILEVER) + { + Z_Free(savebuffer); + save_p = NULL; + I_Error("Existing %s is from the future! (expected %d, got %d)", PROFILESFILE, PROFILEVER, version); + } + + numprofiles = READUINT8(save_p); + if (numprofiles > MAXPROFILES) + numprofiles = MAXPROFILES; + + for (i = 1; i < numprofiles; i++) + { + profilesList[i] = Z_Malloc(sizeof(profile_t), PU_STATIC, NULL); + + // Version. + profilesList[i]->version = version; + + // Names. + READSTRINGN(save_p, profilesList[i]->profilename, PROFILENAMELEN); + READSTRINGN(save_p, profilesList[i]->playername, MAXPLAYERNAME); + + // Character and colour. + READSTRINGN(save_p, profilesList[i]->skinname, SKINNAMESIZE); + profilesList[i]->color = READUINT16(save_p); + + if (profilesList[i]->color == SKINCOLOR_NONE) + { + ; // Valid, even outside the bounds + } + else if (profilesList[i]->color >= numskincolors + || skincolors[profilesList[i]->color].accessible == false) + { + profilesList[i]->color = PROFILEDEFAULTCOLOR; + } + + // Follower and colour. + READSTRINGN(save_p, profilesList[i]->follower, SKINNAMESIZE); + profilesList[i]->followercolor = READUINT16(save_p); + + if (profilesList[i]->followercolor == FOLLOWERCOLOR_MATCH + || profilesList[i]->followercolor == FOLLOWERCOLOR_OPPOSITE) + { + ; // Valid, even outside the bounds + } + else if (profilesList[i]->followercolor >= numskincolors + || profilesList[i]->followercolor == SKINCOLOR_NONE + || skincolors[profilesList[i]->followercolor].accessible == false) + { + profilesList[i]->followercolor = PROFILEDEFAULTFOLLOWERCOLOR; + } + + // PWR. + for (j = 0; j < PWRLV_NUMTYPES; j++) + { + profilesList[i]->powerlevels[j] = READUINT16(save_p); + if (profilesList[i]->powerlevels[j] < PWRLVRECORD_MIN + || profilesList[i]->powerlevels[j] > PWRLVRECORD_MAX) + { + // invalid, reset + profilesList[i]->powerlevels[j] = PWRLVRECORD_START; + } + } + + // Consvars. + profilesList[i]->kickstartaccel = (boolean)READUINT8(save_p); + + // Controls. + for (j = 0; j < num_gamecontrols; j++) + { + for (k = 0; k < MAXINPUTMAPPING; k++) + { + profilesList[i]->controls[j][k] = READINT32(save_p); + } + } + } + + // Add the the default profile directly to avoid letting anyone tamper with it. + profilesList[PROFILE_GUEST] = dprofile; } skincolornum_t PR_GetProfileColor(profile_t *p) diff --git a/src/k_profiles.h b/src/k_profiles.h index b86ef6d50..a099e1ca2 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -39,9 +39,12 @@ #define PROFILEDEFAULTFOLLOWER "none" #define PROFILEDEFAULTFOLLOWERCOLOR FOLLOWERCOLOR_MATCH +#define PROFILEHEADER "Doctor Robotnik's Ring Racers Profiles" + // Man I wish I had more than 16 friends!! // profile_t definition (WIP) +// If you edit, see PR_SaveProfiles and PR_LoadProfiles typedef struct profile_s { From c95fed770e134d9bf9d3eebca4fd2564e36baf4a Mon Sep 17 00:00:00 2001 From: SteelT Date: Fri, 2 Sep 2022 15:25:57 -0400 Subject: [PATCH 341/379] Fix the path of srb2home not being taken into account when loading the last IPs joined log file --- src/m_misc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/m_misc.c b/src/m_misc.c index 30fe5b525..0628ef3b6 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -542,11 +542,11 @@ void M_LoadJoinedIPs(void) { FILE *f = NULL; UINT8 i = 0; - char filepath[255]; + char *filepath; char *s; char content[255]; // 255 is more than long enough! - strcpy(filepath, IPLOGFILE); + filepath = va("%s"PATHSEP"%s", srb2home, IPLOGFILE); f = fopen(filepath, "r"); if (f == NULL) From bcd9c7efba808aa61365f6f0e926122613ab72cb Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 2 Sep 2022 21:55:29 +0100 Subject: [PATCH 342/379] Fix circumstances the game restricts access to certain pause menu options. Changed conditions - - Switch Map and Addons on in-game pause menu - now restricted by !K_CanChangeRules(), which covers all singleplayer conditions - Gameplay and Server Options - now restricted if in-game, and either singleplayer conditions or not admin - Erase data - now restricted if in-game at all (fixes new-menus regression) --- src/k_menu.h | 12 ++++++++++++ src/k_menudef.c | 6 +++--- src/k_menufunc.c | 34 +++++++++++++++++++--------------- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 8f741e4e8..baa9eb356 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -258,6 +258,18 @@ typedef enum mopt_manual, } mopt_e; +typedef enum +{ + dopt_screenshot = 0, + dopt_addon, + dopt_replay, +#ifdef HAVE_DISCORDRPC + dopt_discord, +#endif + dopt_spacer, + dopt_erase, +} dopt_e; + extern menuitem_t OPTIONS_Profiles[]; extern menu_t OPTIONS_ProfilesDef; diff --git a/src/k_menudef.c b/src/k_menudef.c index d2e76cc9c..0da8f04be 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -474,7 +474,7 @@ menu_t PLAY_MP_ServerBrowserDef = { M_ServerBrowserInputs }; -// options menu +// options menu -- see mopt_e menuitem_t OPTIONS_Main[] = { @@ -1249,6 +1249,7 @@ menu_t OPTIONS_ServerAdvancedDef = { }; #endif +// data options menu -- see dopt_e menuitem_t OPTIONS_Data[] = { @@ -1269,8 +1270,7 @@ menuitem_t OPTIONS_Data[] = {IT_SPACE | IT_NOTHING, NULL, NULL, NULL, {NULL}, 0, 0}, - // escape sequences don't like any letter from A to E following them... So let's also put E as an escape sequence lol. E is 69 (nice) which is 45 in hex. - {IT_STRING | IT_SUBMENU, "\x85\x45rase Data...", "Erase specific data. Be careful, what's deleted is gone forever!", + {IT_STRING | IT_SUBMENU, "\x85""Erase Data...", "Erase specific data. Be careful, what's deleted is gone forever!", NULL, {.submenu = &OPTIONS_DataEraseDef}, 0, 0}, }; diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 8ad6c3086..077829c26 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -4391,17 +4391,21 @@ void M_InitOptions(INT32 choice) { (void)choice; - OPTIONS_MainDef.menuitems[mopt_profiles].status = IT_STRING | IT_CALL; - OPTIONS_MainDef.menuitems[mopt_gameplay].status = IT_STRING | IT_SUBMENU; - OPTIONS_MainDef.menuitems[mopt_server].status = IT_STRING | IT_SUBMENU; + OPTIONS_MainDef.menuitems[mopt_gameplay].status = IT_STRING | IT_TRANSTEXT; + OPTIONS_MainDef.menuitems[mopt_server].status = IT_STRING | IT_TRANSTEXT; - // disable gameplay & server options if you aren't an admin in netgames. (GS_MENU check maybe unecessary but let's not take any chances) - if (netgame && gamestate != GS_MENU && !IsPlayerAdmin(consoleplayer)) + // enable gameplay & server options under the right circumstances. + if (gamestate == GS_MENU + || ((server || IsPlayerAdmin(consoleplayer)) && K_CanChangeRules())) { - OPTIONS_MainDef.menuitems[mopt_gameplay].status = IT_STRING | IT_TRANSTEXT; - OPTIONS_MainDef.menuitems[mopt_server].status = IT_STRING | IT_TRANSTEXT; + OPTIONS_MainDef.menuitems[mopt_gameplay].status = IT_STRING | IT_SUBMENU; + OPTIONS_MainDef.menuitems[mopt_server].status = IT_STRING | IT_SUBMENU; } + OPTIONS_DataDef.menuitems[dopt_erase].status = (gamestate == GS_MENU + ? (IT_STRING | IT_SUBMENU) + : (IT_TRANSTEXT2 | IT_SPACE)); + M_ResetOptions(); // So that pause doesn't go to the main menu... @@ -5706,7 +5710,6 @@ struct pausemenu_s pausemenu; // Pause menu! void M_OpenPauseMenu(void) { - boolean singleplayermode = (modeattacking || grandprixinfo.gp); currentMenu = &PAUSE_MainDef; // Ready the variables @@ -5736,18 +5739,19 @@ void M_OpenPauseMenu(void) Dummymenuplayer_OnChange(); // Make sure the consvar is within bounds of the amount of splitscreen players we have. - if (!singleplayermode && (server || IsPlayerAdmin(consoleplayer))) + if (K_CanChangeRules()) { - PAUSE_Main[mpause_switchmap].status = IT_STRING | IT_SUBMENU; - PAUSE_Main[mpause_addons].status = IT_STRING | IT_CALL; - } - - if (!singleplayermode) PAUSE_Main[mpause_psetup].status = IT_STRING | IT_CALL; + if (server || IsPlayerAdmin(consoleplayer)) + { + PAUSE_Main[mpause_switchmap].status = IT_STRING | IT_SUBMENU; + PAUSE_Main[mpause_addons].status = IT_STRING | IT_CALL; + } + } + if (G_GametypeHasSpectators()) { - if (splitscreen) PAUSE_Main[mpause_spectatemenu].status = IT_STRING|IT_SUBMENU; else From 061eca773bd45f789f732b8e2fc427e5c055de1d Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 2 Sep 2022 22:05:11 +0100 Subject: [PATCH 343/379] Fix itemOn out-of-bounds event for Main Menu and its ilk. Was caused by the following actions: - Starting a game - Going to the Options menu - scrolling beyond the 4th entry - Exiting to title screen - Opening up main menu again --- src/k_menufunc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 077829c26..297b5ca8a 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -945,7 +945,7 @@ void M_StartControlPanel(void) else if (optionsmenu.profilen < 0) optionsmenu.profilen = 0; - itemOn = 0; + currentMenu->lastOn = 0; CV_StealthSetValue(&cv_currprofile, -1); // Make sure to reset that as it is set by PR_ApplyProfile which we kind of hack together to force it. } @@ -966,6 +966,8 @@ void M_StartControlPanel(void) } } + itemOn = currentMenu->lastOn; + CON_ToggleOff(); // move away console } @@ -5719,8 +5721,7 @@ void M_OpenPauseMenu(void) pausemenu.openoffset = 256; pausemenu.closing = false; - itemOn = mpause_continue; // Make sure we select "RESUME GAME" by default - + currentMenu->lastOn = mpause_continue; // Make sure we select "RESUME GAME" by default // Now the hilarious balancing act of deciding what options should be enabled and which ones shouldn't be! // By default, disable anything sensitive: From 1aabde7d4f48be6d53359f192d8c7c53e22038e8 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 2 Sep 2022 22:18:33 +0100 Subject: [PATCH 344/379] Let's not tip our hand too early. (Oversight when minimising boss material) --- src/d_netcmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index cd78e3d19..c1fe80aff 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5051,7 +5051,7 @@ void Command_Retry_f(void) } else if (grandprixinfo.gp == false && bossinfo.boss == false) { - CONS_Printf(M_GetText("This only works in Grand Prix or Mission Mode.\n")); + CONS_Printf(M_GetText("This only works in singleplayer games.\n")); } else { From cf25cf0da15975f1f849fa3ec2623b999ffbe241 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 2 Sep 2022 22:37:40 +0100 Subject: [PATCH 345/379] Clear menus if wiping from GS_MENU to GS_LEVEL/GSWAITINGPLAYERS Catches a case that's never been relevant before -- now that you can open the console on the menu, map commands have newfound dangers to be worked around. --- src/d_clisrv.c | 2 ++ src/g_game.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 7f9c2ef21..13d0a67d6 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1999,6 +1999,8 @@ static void CL_ConnectToServer(void) DEBFILE(va("waiting %d nodes\n", doomcom->numnodes)); G_SetGamestate(GS_WAITINGPLAYERS); + if (wipegamestate == GS_MENU) + M_ClearMenus(true); wipegamestate = GS_WAITINGPLAYERS; ClearAdminPlayers(); diff --git a/src/g_game.c b/src/g_game.c index 31b5b2d15..9760197b9 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1286,6 +1286,8 @@ void G_DoLoadLevel(boolean resetplayer) titlemapinaction = TITLEMAP_OFF; G_SetGamestate(GS_LEVEL); + if (wipegamestate == GS_MENU) + M_ClearMenus(true); I_UpdateMouseGrab(); for (i = 0; i < MAXPLAYERS; i++) From 52c1cfd1cf57e1e5ff50a7e6ca93f38c87d6271c Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 2 Sep 2022 22:56:55 +0100 Subject: [PATCH 346/379] Remove redundant settings -- PR_ApplyProfile does these for us! --- src/k_menufunc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 297b5ca8a..dcc4c7082 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -4638,12 +4638,10 @@ static void M_FirstPickProfile(INT32 c) M_ResetOptions(); // Reset all options variables otherwise things are gonna go reaaal bad lol. optionsmenu.profile = NULL; // Make sure to get rid of that, too. - CV_StealthSetValue(&cv_currprofile, optionsmenu.profilen); PR_ApplyProfile(optionsmenu.profilen, 0); M_SetupNextMenu(&MainDef, false); // Tell the game this is the last profile we picked. - CV_StealthSetValue(&cv_lastprofile[0], optionsmenu.profilen); CV_StealthSetValue(&cv_ttlprofilen, optionsmenu.profilen); // Save em! From 8946bf9e01d88a3fccd2c563bf6f99b4e7057684 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 3 Sep 2022 00:16:41 +0100 Subject: [PATCH 347/379] Refactor PR_ApplyProfile and its ilk - Reduces copypasted code. - Preperation for the next commit. --- src/k_profiles.c | 64 +++++++++++++++++++++++++++++++++++------------- src/k_profiles.h | 5 ++++ 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/k_profiles.c b/src/k_profiles.c index 32cba641a..5067b98ae 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -402,18 +402,8 @@ skincolornum_t PR_GetProfileColor(profile_t *p) return p->color; } -void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum) +static void PR_ApplyProfile_Appearance(profile_t *p, UINT8 playernum) { - profile_t *p = PR_GetProfile(profilenum); - - // this CAN happen!! - if (p == NULL) - { - CONS_Printf("Profile '%d' could not be loaded as it does not exist. Guest Profile will be loaded instead.\n", profilenum); - profilenum = 0; // make sure to set this so that the cvar is set properly. - p = PR_GetProfile(0); // Use guest profile instead if things went south somehow. - } - CV_StealthSet(&cv_skin[playernum], p->skinname); CV_StealthSetValue(&cv_playercolor[playernum], PR_GetProfileColor(p)); CV_StealthSet(&cv_playername[playernum], p->playername); @@ -421,13 +411,19 @@ void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum) // Followers CV_StealthSet(&cv_follower[playernum], p->follower); CV_StealthSetValue(&cv_followercolor[playernum], p->followercolor); +} +static void PR_ApplyProfile_Settings(profile_t *p, UINT8 playernum) +{ // toggles CV_StealthSetValue(&cv_kickstartaccel[playernum], p->kickstartaccel); // set controls... memcpy(&gamecontrol[playernum], p->controls, sizeof(gamecontroldefault)); +} +static void PR_ApplyProfile_Memory(UINT8 profilenum, UINT8 playernum) +{ // set memory cvar CV_StealthSetValue(&cv_lastprofile[playernum], profilenum); @@ -438,17 +434,51 @@ void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum) } } +void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum) +{ + profile_t *p = PR_GetProfile(profilenum); + + // this CAN happen!! + if (p == NULL) + { + CONS_Printf("Profile '%d' could not be loaded as it does not exist. Guest Profile will be loaded instead.\n", profilenum); + profilenum = 0; // make sure to set this so that the cvar is set properly. + p = PR_GetProfile(profilenum); + } + + PR_ApplyProfile_Appearance(p, playernum); + PR_ApplyProfile_Settings(p, playernum); + PR_ApplyProfile_Memory(profilenum, playernum); +} + void PR_ApplyProfileLight(UINT8 profilenum, UINT8 playernum) { profile_t *p = PR_GetProfile(profilenum); - CV_StealthSet(&cv_skin[playernum], p->skinname); - CV_StealthSetValue(&cv_playercolor[playernum], p->color); - CV_StealthSet(&cv_playername[playernum], p->playername); + // this CAN happen!! + if (p == NULL) + { + // no need to be as loud... + profilenum = 0; // make sure to set this so that the cvar is set properly. + p = PR_GetProfile(profilenum); + } - // Followers - CV_StealthSet(&cv_follower[playernum], p->follower); - CV_StealthSetValue(&cv_followercolor[playernum], p->followercolor); + PR_ApplyProfile_Appearance(p, playernum); +} + +void PR_ApplyProfilePretend(UINT8 profilenum, UINT8 playernum) +{ + profile_t *p = PR_GetProfile(profilenum); + + // this CAN happen!! + if (p == NULL) + { + CONS_Printf("Profile '%d' could not be loaded as it does not exist. Guest Profile will be loaded instead.\n", profilenum); + profilenum = 0; // make sure to set this so that the cvar is set properly. + p = PR_GetProfile(profilenum); + } + + PR_ApplyProfile_Memory(profilenum, playernum); } UINT8 PR_GetProfileNum(profile_t *p) diff --git a/src/k_profiles.h b/src/k_profiles.h index a099e1ca2..0a254bef5 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -136,6 +136,11 @@ void PR_ApplyProfile(UINT8 profilenum, UINT8 playernum); // Controls, kickstartaccel and "current profile" data is *not* modified. void PR_ApplyProfileLight(UINT8 profilenum, UINT8 playernum); +// PR_ApplyProfilePretend(UINT8 profilenum, UINT8 playernum) +// ONLY modifies "current profile" data. +// Exists because any other option inteferes with rapid testing. +void PR_ApplyProfilePretend(UINT8 profilenum, UINT8 playernum); + // PR_GetProfileNum(profile_t *p) // Gets the profile's index # in profilesList UINT8 PR_GetProfileNum(profile_t *p); From 139def0d3d2a311143751ddbd4f13443f051a5b5 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 3 Sep 2022 00:19:03 +0100 Subject: [PATCH 348/379] Plug the last gap that allowed you to avoid selecting a profile. There's no actual good way to handle the case of changing your skin, etc, maybe even various controls for a momentary test AND supporting profiles, so don't even try. Instead, let the game "pretend" you selected ttlprofilen to let it just about work. This works because all the relevant cvars and controls still get saved to the config when changed... they just get written over when you select a profile. So if you haven't changed anything since your last successful launch, it is now functionally indistinguishable. --- src/d_clisrv.c | 2 ++ src/g_game.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 13d0a67d6..64aef5840 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1992,6 +1992,8 @@ static void CL_ConnectToServer(void) CONS_Printf(M_GetText("Contacting the server...\n")); } + if (cv_currprofile.value == -1) + PR_ApplyProfilePretend(cv_ttlprofilen.value, 0); if (gamestate == GS_INTERMISSION) Y_EndIntermission(); // clean up intermission graphics etc if (gamestate == GS_VOTING) diff --git a/src/g_game.c b/src/g_game.c index 9760197b9..8ff414b84 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1265,6 +1265,8 @@ void G_DoLoadLevel(boolean resetplayer) if (wipegamestate == GS_LEVEL) wipegamestate = -1; // force a wipe + if (cv_currprofile.value == -1) + PR_ApplyProfilePretend(cv_ttlprofilen.value, 0); if (gamestate == GS_INTERMISSION) Y_EndIntermission(); if (gamestate == GS_VOTING) From 8312f12d6cae934ae8de7e46da231c311f4b4fde Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 3 Sep 2022 16:23:16 +0100 Subject: [PATCH 349/379] Addons menu refactor part 1 - Move M_AddonsRefresh out of M_DrawAddons - death to a HORRIBLE hack - Use it as the tickroutine for the addons menu instead - Behaves as before, possibly more consistent --- src/k_menu.h | 2 +- src/k_menudef.c | 2 +- src/k_menudraw.c | 7 ------- src/k_menufunc.c | 20 ++++++++++---------- 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index baa9eb356..356a0a4ee 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -963,7 +963,7 @@ void M_ReplayHut(INT32 choice); // Misc menus: #define numaddonsshown 4 void M_Addons(INT32 choice); -boolean M_AddonsRefresh(void); +void M_AddonsRefresh(void); void M_HandleAddons(INT32 choice); char *M_AddonsHeaderPath(void); diff --git a/src/k_menudef.c b/src/k_menudef.c index 0da8f04be..6d32a4646 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -1742,7 +1742,7 @@ menu_t MISC_AddonsDef = { 0, 0, 0, 0, M_DrawAddons, - NULL, + M_AddonsRefresh, NULL, NULL, NULL diff --git a/src/k_menudraw.c b/src/k_menudraw.c index db5854e31..ff7c1550f 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -4284,13 +4284,6 @@ void M_DrawAddons(void) V_DrawFixedPatch(0, 0, FRACUNIT, 0, bg, NULL); } - // 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 diff --git a/src/k_menufunc.c b/src/k_menufunc.c index dcc4c7082..0212f6ac6 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -6321,8 +6321,8 @@ static void M_AddonsClearName(INT32 choice) M_StopMessage(choice); } -// returns whether to do message draw -boolean M_AddonsRefresh(void) +// Handles messages for addon errors. +void M_AddonsRefresh(void) { if ((refreshdirmenu & REFRESHDIR_NORMAL) && !preparefilemenu(true, false)) { @@ -6331,7 +6331,7 @@ boolean M_AddonsRefresh(void) { CLEARNAME; } - return true; + return;// true; } #ifdef DEVELOP @@ -6368,23 +6368,23 @@ boolean M_AddonsRefresh(void) if (message) { M_StartMessage(message,FUNCPTRCAST(M_AddonsClearName),MM_YESNO); - return true; + return;// true; } S_StartSound(NULL, sfx_s221); CLEARNAME; } - return false; + return;// false; } static void M_AddonExec(INT32 ch) { - if (ch == MA_NO) - return; - - S_StartSound(NULL, sfx_zoom); - COM_BufAddText(va("exec \"%s%s\"", menupath, dirmenu[dir_on[menudepthleft]]+DIR_STRING)); + if (ch == MA_YES) + { + S_StartSound(NULL, sfx_zoom); + COM_BufAddText(va("exec \"%s%s\"", menupath, dirmenu[dir_on[menudepthleft]]+DIR_STRING)); + } } #define len menusearch[0] From 88f04da1805842e352086a431c97b96988c98424 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 3 Sep 2022 19:43:13 +0100 Subject: [PATCH 350/379] Addons menu refactor part 2 - Change M_DrawAddons to draw relatively, so that the height can be changed without consequence. - Add an option to adjust the spacing of Addons Menu entries (currently unused, but could be explored later). - Moved Search (still not yet functional) further up the menu. --- src/k_menudraw.c | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index ff7c1550f..7d3f186fb 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -4266,6 +4266,7 @@ static void M_CacheAddonPatches(void) addonsp[NUM_EXT+4] = W_CachePatchName("M_FSAVE", PU_STATIC); } +#define addonsseperation 16 void M_DrawAddons(void) { @@ -4308,11 +4309,25 @@ void M_DrawAddons(void) hilicol = V_GetStringColormap(highlightflags)[0]; - 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); + y -= 16; - m = (BASEVIDHEIGHT - currentMenu->y + 2) - (y - 1); + V_DrawString(x-21, y + (lsheadingheight - 12), highlightflags|V_ALLOWLOWERCASE, M_AddonsHeaderPath()); + V_DrawFill(x-21, y + (lsheadingheight - 3), MAXSTRINGLENGTH*8+6, 1, hilicol); + //V_DrawFill(x-21, y + (lsheadingheight - 2), MAXSTRINGLENGTH*8+6, 1, 30); + + y += 10; + + 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..."); + + V_DrawSmallScaledPatch(x - (21 + 5 + 16), y+4, (menusearch[0] ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+3]); + + y += 21; + + m = (addonsseperation*(2*numaddonsshown + 1)) + 2*(16-addonsseperation); V_DrawFill(x - 21, y - 1, MAXSTRINGLENGTH*8+6, m, 159); // scrollbar! @@ -4352,6 +4367,8 @@ void M_DrawAddons(void) if (skullAnimCounter < 4) flashcol = V_GetStringColormap(highlightflags); + y -= (16-addonsseperation); + for (; i < m; i++) { UINT32 flags = V_ALLOWLOWERCASE; @@ -4382,7 +4399,7 @@ void M_DrawAddons(void) V_DrawString(x, y+4, flags, dirmenu[i]+DIR_STRING); } #undef type - y += 16; + y += addonsseperation; } if (m != (ssize_t)sizedirmenu) @@ -4390,21 +4407,11 @@ void M_DrawAddons(void) 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; + x = BASEVIDWIDTH - (x - (21 + 5 + 16)) - 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]); } + +#undef addonsseperation From 26461d568cba8650bd162378250e104669b09a92 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 3 Sep 2022 21:15:23 +0100 Subject: [PATCH 351/379] Addons menu refactor part 3 Search finally works again! - Now uses a cvar which is copied into static memory to uppercase it, instead of a weird static string uppercased into zone memory. - You have to scroll to the top of the menu to use it, one entry above the previous first file/folder/"UP...". - Don't play menu sound if you've reached the end of an un-looping menu. --- src/filesrch.c | 18 ++++---- src/k_menu.h | 1 + src/k_menudef.c | 4 +- src/k_menudraw.c | 21 ++++++--- src/k_menufunc.c | 113 ++++++++++++++++++++++++----------------------- 5 files changed, 83 insertions(+), 74 deletions(-) diff --git a/src/filesrch.c b/src/filesrch.c index 17f0db7e8..9db81ad37 100644 --- a/src/filesrch.c +++ b/src/filesrch.c @@ -561,15 +561,15 @@ char exttable[NUM_EXT_TABLE][7] = { // maximum extension length (currently 4) pl char filenamebuf[MAX_WADFILES][MAX_WADPATH]; -static boolean filemenucmp(char *haystack, char *needle) +static boolean filemenucmp(char *haystack) { static char localhaystack[128]; strlcpy(localhaystack, haystack, 128); if (!cv_addons_search_case.value) strupr(localhaystack); if (cv_addons_search_type.value) - return (strstr(localhaystack, needle) != 0); - return (!strncmp(localhaystack, needle, menusearch[0])); + return (strstr(localhaystack, menusearch+1) != 0); + return (!strncmp(localhaystack, menusearch+1, menusearch[0])); } void closefilemenu(boolean validsize) @@ -616,7 +616,6 @@ void closefilemenu(boolean validsize) void searchfilemenu(char *tempname) { size_t i, first; - char localmenusearch[MAXSTRINGLENGTH] = ""; if (dirmenu) { @@ -662,14 +661,10 @@ void searchfilemenu(char *tempname) return; } - strcpy(localmenusearch, menusearch+1); - if (!cv_addons_search_case.value) - strupr(localmenusearch); - sizedirmenu = 0; for (i = first; i < sizecoredirmenu; i++) { - if (filemenucmp(coredirmenu[i]+DIR_STRING, localmenusearch)) + if (filemenucmp(coredirmenu[i]+DIR_STRING)) sizedirmenu++; } @@ -691,7 +686,7 @@ void searchfilemenu(char *tempname) sizedirmenu = 0; for (i = first; i < sizecoredirmenu; i++) { - if (filemenucmp(coredirmenu[i]+DIR_STRING, localmenusearch)) + if (filemenucmp(coredirmenu[i]+DIR_STRING)) { if (tempname && !strcmp(coredirmenu[i]+DIR_STRING, tempname)) { @@ -724,7 +719,10 @@ boolean preparefilemenu(boolean samedepth, boolean replayhut) tempname = Z_StrDup(dirmenu[dir_on[menudepthleft]]+DIR_STRING); // don't need to I_Error if can't make - not important, just QoL } else + { menusearch[0] = menusearch[1] = 0; // clear search + CV_StealthSet(&cv_dummyaddonsearch, ""); + } if (!(dirhandle = opendir(menupath))) // get directory { diff --git a/src/k_menu.h b/src/k_menu.h index 356a0a4ee..e85d2425e 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -966,6 +966,7 @@ void M_Addons(INT32 choice); void M_AddonsRefresh(void); void M_HandleAddons(INT32 choice); char *M_AddonsHeaderPath(void); +extern consvar_t cv_dummyaddonsearch; void M_Manual(INT32 choice); void M_HandleImageDef(INT32 choice); diff --git a/src/k_menudef.c b/src/k_menudef.c index 6d32a4646..b83c27273 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -1726,9 +1726,11 @@ menuitem_t MISC_Manual[] = { menu_t MISC_ManualDef = IMAGEDEF(MISC_Manual); -// Addons menu! (Just a straight port for now) +// Addons menu! menuitem_t MISC_AddonsMenu[] = { + {IT_STRING | IT_CVAR | IT_CV_STRING, NULL, NULL, + NULL, {.cvar = &cv_dummyaddonsearch}, 0, 0}, {IT_KEYHANDLER | IT_NOTHING, NULL, NULL, NULL, {.routine = M_HandleAddons}, 0, 0}, // dummy menuitem for the control func }; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 7d3f186fb..5fc87bbb4 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -4191,7 +4191,7 @@ void M_DrawReplayStartMenu(void) // Draw misc menus: -// Addons (this is merely copypasted, original code by toaster) +// Addons #define lsheadingheight 16 @@ -4318,16 +4318,23 @@ void M_DrawAddons(void) y += 10; 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..."); + { + const char *str = (menusearch[0] ? cv_dummyaddonsearch.string : "Search..."); + INT32 tflag = (menusearch[0] ? 0 : V_TRANSLUCENT); + INT32 xoffs = 0; + if (itemOn == 0) + { + xoffs += 8; + V_DrawString(x + (skullAnimCounter/5) - 20, y+8, highlightflags, "\x1D"); + } + V_DrawString(x + xoffs - 18, y+8, V_ALLOWLOWERCASE|tflag, str); + } V_DrawSmallScaledPatch(x - (21 + 5 + 16), y+4, (menusearch[0] ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+3]); y += 21; - m = (addonsseperation*(2*numaddonsshown + 1)) + 2*(16-addonsseperation); + m = (addonsseperation*(2*numaddonsshown + 1)) + 1 + 2*(16-addonsseperation); V_DrawFill(x - 21, y - 1, MAXSTRINGLENGTH*8+6, m, 159); // scrollbar! @@ -4385,7 +4392,7 @@ void M_DrawAddons(void) else V_DrawSmallScaledPatch(x-(16+4), y, 0, addonsp[(type & ~EXT_LOADED)]); - if ((size_t)i == dir_on[menudepthleft]) + if (itemOn == 1 && (size_t)i == dir_on[menudepthleft]) { V_DrawFixedPatch((x-(16+4))< currentMenu->numitems - 1) + { + // Prevent looparound here + // If you're going to add any extra exceptions, DON'T. + // Add a "don't loop" flag to the menu_t struct instead. + if (currentMenu == &MISC_AddonsDef) + return false; itemOn = 0; + } else itemOn++; } while (oldItemOn != itemOn && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_SPACE); M_UpdateMenuBGImage(false); + + return true; } -static void M_PrevOpt(void) +static boolean M_PrevOpt(void) { INT16 oldItemOn = itemOn; // prevent infinite loop @@ -747,12 +759,21 @@ static void M_PrevOpt(void) do { if (!itemOn) + { + // Prevent looparound here + // If you're going to add any extra exceptions, DON'T. + // Add a "don't loop" flag to the menu_t struct instead. + if (currentMenu == &MISC_AddonsDef) + return false; itemOn = currentMenu->numitems - 1; + } else itemOn--; } while (oldItemOn != itemOn && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_SPACE); M_UpdateMenuBGImage(false); + + return true; } // @@ -1436,15 +1457,15 @@ static void M_HandleMenuInput(void) // Keys usable within menu if (ud > 0) { - M_NextOpt(); - S_StartSound(NULL, sfx_s3k5b); + if (M_NextOpt()) + S_StartSound(NULL, sfx_s3k5b); M_SetMenuDelay(pid); return; } else if (ud < 0) { - M_PrevOpt(); - S_StartSound(NULL, sfx_s3k5b); + if (M_PrevOpt()) + S_StartSound(NULL, sfx_s3k5b); M_SetMenuDelay(pid); return; } @@ -1683,6 +1704,8 @@ void M_Init(void) CV_RegisterVar(&cv_dummygpencore); CV_RegisterVar(&cv_dummymatchbots); + CV_RegisterVar(&cv_dummyaddonsearch); + M_UpdateMenuBGImage(true); #if 0 @@ -6280,6 +6303,8 @@ void M_Addons(INT32 choice) else dir_on[menudepthleft] = 0; + MISC_AddonsDef.lastOn = 0; // Always start on search + MISC_AddonsDef.prevMenu = currentMenu; M_SetupNextMenu(&MISC_AddonsDef, false); } @@ -6387,43 +6412,23 @@ static void M_AddonExec(INT32 ch) } } -#define len menusearch[0] -static boolean M_ChangeStringAddons(INT32 choice) +static void M_UpdateAddonsSearch(void) { - if (shiftdown && choice >= 32 && choice <= 127) - choice = shiftxform[choice]; + menusearch[0] = strlen(cv_dummyaddonsearch.string); + strlcpy(menusearch+1, cv_dummyaddonsearch.string, MAXSTRINGLENGTH); + if (!cv_addons_search_case.value) + strupr(menusearch+1); - switch (choice) +#if 0 // much slower + if (!preparefilemenu(true, false)) { - 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; + UNEXIST; + return; } - return false; +#else // streamlined + searchfilemenu(NULL); +#endif } -#undef len void M_HandleAddons(INT32 choice) { @@ -6432,34 +6437,30 @@ void M_HandleAddons(INT32 choice) (void) choice; - 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 - } - if (menucmd[pid].dpad_ud > 0) { if (dir_on[menudepthleft] < sizedirmenu-1) + { dir_on[menudepthleft]++; - S_StartSound(NULL, sfx_s3k5b); + S_StartSound(NULL, sfx_s3k5b); + } + else if (M_NextOpt()) + { + S_StartSound(NULL, sfx_s3k5b); + } M_SetMenuDelay(pid); } else if (menucmd[pid].dpad_ud < 0) { if (dir_on[menudepthleft]) + { dir_on[menudepthleft]--; - S_StartSound(NULL, sfx_s3k5b); + S_StartSound(NULL, sfx_s3k5b); + } + else if (M_PrevOpt()) + { + S_StartSound(NULL, sfx_s3k5b); + } M_SetMenuDelay(pid); } From 594748ba136850b80ad82404d03922cf2fe4a3d2 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 3 Sep 2022 21:46:50 +0100 Subject: [PATCH 352/379] Addons menu refactor part 4 - Get rid of the temperature gauge, a relic from an age where the filenames you could fit in a packet controlled how many files you could load. - Instead, add a file count to the bottom of the screen. - Still a little ugly, but this menu is now SHIPPABLE. --- src/k_menudraw.c | 74 +++++------------------------------------------- 1 file changed, 7 insertions(+), 67 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 5fc87bbb4..5bfe908b8 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -4195,55 +4195,6 @@ void M_DrawReplayStartMenu(void) #define lsheadingheight 16 -#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, 0); - V_DrawFill(x + width, vpadding, 1, h, 0); - V_DrawFill(x - 1, vpadding-1, width+2, 1, 0); - V_DrawFill(x - 1, vpadding+h, width+2, 1, 0); - - // 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 - - // Just do this here instead. static void M_CacheAddonPatches(void) { @@ -4290,19 +4241,6 @@ void M_DrawAddons(void) 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 - { - y = FixedDiv(((ssize_t)(numwadfiles) - (ssize_t)(mainwads+1))< 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; @@ -4412,13 +4350,15 @@ void M_DrawAddons(void) if (m != (ssize_t)sizedirmenu) V_DrawString(19, y-12 + (skullAnimCounter/5), highlightflags, "\x1B"); - y = BASEVIDHEIGHT - currentMenu->y + 1; - - x = BASEVIDWIDTH - (x - (21 + 5 + 16)) - 16; - V_DrawSmallScaledPatch(x, y + 4, ((!majormods) ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+4]); + y -= 2; + V_DrawSmallScaledPatch(x, y, ((!majormods) ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+4]); if (modifiedgame) - V_DrawSmallScaledPatch(x, y + 4, 0, addonsp[NUM_EXT+2]); + V_DrawSmallScaledPatch(x, y, 0, addonsp[NUM_EXT+2]); + + m = numwadfiles-(mainwads+2+1); + + V_DrawCenteredString(BASEVIDWIDTH/2, y+4, (majormods ? highlightflags : V_TRANSLUCENT), va("%d ADD-ON%s LOADED", m, (m == 1) ? "" : "S")); //+2 for music, sounds, +1 for main.kart } #undef addonsseperation From 587f814ab35166145100b3aa0cb6af1e229bb8b5 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 3 Sep 2022 21:59:56 +0100 Subject: [PATCH 353/379] Adjust some messagebox-related material. - Fix an issue where if the last line of an M_StartMessage was the longest, the box width wouldn't account for it. (port from v1) - Reduce length of some common error messages the Addons menu may produce. - Add a warning for attempting to run .cfg files, since I absentmindedly overwrote ringconfig.cfg with kartconfig.cfg info while testing... --- src/k_menufunc.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 45776deff..1556bd60b 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1845,7 +1845,11 @@ void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtyp } if (i == strlen(message+start)) + { start += i; + if (i > max) + max = i; + } } menumessage.x = (INT16)((BASEVIDWIDTH - 8*max-16)/2); @@ -6376,12 +6380,12 @@ void M_AddonsRefresh(void) if (refreshdirmenu & REFRESHDIR_MAX) message = va("%c%s\x80\nMaximum number of addons reached.\nA file could not be loaded.\nIf you wish to play with this addon, 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); + message = va("%c%s\x80\nA file was not loaded.\nCheck the console log for more info.\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")); + message = va("%c%s\x80\nA file was loaded with %s.\nCheck the console log for more info.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname, ((refreshdirmenu & REFRESHDIR_ERROR) ? "errors" : "warnings")); } else if (majormods && !prevmajormods) { @@ -6544,7 +6548,7 @@ void M_HandleAddons(INT32 choice) break; case EXT_CFG: - M_AddonExec(MA_YES); + M_StartMessage(va("%c%s\x80\nThis file may modify your settings.\nAttempt to run anyways? \n\n(Press A to confirm)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), dirmenu[dir_on[menudepthleft]]+DIR_STRING),FUNCPTRCAST(M_AddonExec),MM_YESNO); break; case EXT_LUA: From 6544e95c01a56e8afcb06e9dfbfa09ab4f2b37c7 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 4 Sep 2022 14:56:07 +0100 Subject: [PATCH 354/379] Rework gamespeed cvar handling a little bit. - Make the menu-only dummykartspeed and dummygpdifficulty cvars also affected by the unlock system. - Master mode is currently behind SECRET_HARDSPEED, this can be changed later when we're seriously thinking about unlock progression. - Complete forwardport of changes to cv_kartspeed from 1.4+, since I missed a spot previously. --- src/command.c | 25 +++++++++++++++++++++++-- src/command.h | 2 +- src/d_netcmd.c | 7 ------- src/k_menufunc.c | 7 ++----- 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/command.c b/src/command.c index acdc93d1c..c6e048fdb 100644 --- a/src/command.c +++ b/src/command.c @@ -82,6 +82,13 @@ CV_PossibleValue_t kartspeed_cons_t[] = { {KARTSPEED_HARD, "Hard"}, {0, NULL} }; +CV_PossibleValue_t gpdifficulty_cons_t[] = { + {KARTSPEED_EASY, "Easy"}, + {KARTSPEED_NORMAL, "Normal"}, + {KARTSPEED_HARD, "Hard"}, + {KARTGP_MASTER, "Master"}, + {0, NULL} +}; // Filter consvars by EXECVERSION // First implementation is 2 (1.0.2), so earlier configs default at 1 (1.0.0) @@ -1804,6 +1811,15 @@ static void CV_SetCVar(consvar_t *var, const char *value, boolean stealth) return; } + if (var == &cv_kartspeed && !M_SecretUnlocked(SECRET_HARDSPEED)) + { + if (!stricmp(value, "Hard") || atoi(value) >= KARTSPEED_HARD) + { + CONS_Printf(M_GetText("You haven't unlocked this yet!\n")); + return; + } + } + if (var == &cv_forceskin) { INT32 skin = R_SkinAvailable(value); @@ -2082,9 +2098,14 @@ void CV_AddValue(consvar_t *var, INT32 increment) return; } } - else if (var == &cv_kartspeed) + else if (var->PossibleValue == kartspeed_cons_t || var->PossibleValue == gpdifficulty_cons_t) { - max = (M_SecretUnlocked(SECRET_HARDSPEED) ? 3 : 2); + if (!M_SecretUnlocked(SECRET_HARDSPEED)) + { + max = KARTSPEED_NORMAL+1; + if (var->PossibleValue == kartspeed_cons_t) + max++; // Accommodate KARTSPEED_AUTO + } } #ifdef PARANOIA if (currentindice == -1) diff --git a/src/command.h b/src/command.h index 322892b74..9e52df9e4 100644 --- a/src/command.h +++ b/src/command.h @@ -175,7 +175,7 @@ extern CV_PossibleValue_t CV_Natural[]; #define KARTSPEED_NORMAL 1 #define KARTSPEED_HARD 2 #define KARTGP_MASTER 3 // Not a speed setting, gives the hardest speed with maxed out bots -extern CV_PossibleValue_t kartspeed_cons_t[]; +extern CV_PossibleValue_t kartspeed_cons_t[], gpdifficulty_cons_t[]; extern consvar_t cv_execversion; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index c53235467..b7db898d3 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5812,13 +5812,6 @@ static void KartFrantic_OnChange(void) static void KartSpeed_OnChange(void) { - if (!M_SecretUnlocked(SECRET_HARDSPEED) && cv_kartspeed.value == KARTSPEED_HARD) - { - CONS_Printf(M_GetText("You haven't earned this yet.\n")); - CV_StealthSet(&cv_kartspeed, cv_kartspeed.defaultvalue); - return; - } - if (K_CanChangeRules() == false) { return; diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 1556bd60b..77a211953 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -187,9 +187,6 @@ static CV_PossibleValue_t dummyscramble_cons_t[] = {{0, "Random"}, {1, "Points"} static CV_PossibleValue_t dummystaff_cons_t[] = {{0, "MIN"}, {100, "MAX"}, {0, NULL}}; static CV_PossibleValue_t dummygametype_cons_t[] = {{0, "Race"}, {1, "Battle"}, {0, NULL}}; -static CV_PossibleValue_t dummygpdifficulty_cons_t[] = {{0, "Easy"}, {1, "Normal"}, {2, "Hard"}, {3, "Master"}, {0, NULL}}; -static CV_PossibleValue_t dummykartspeed_cons_t[] = {{-1, "Auto"}, {0, "Easy"}, {1, "Normal"}, {2, "Hard"}, {0, NULL}}; - //static consvar_t cv_dummymenuplayer = CVAR_INIT ("dummymenuplayer", "P1", CV_HIDDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange); static consvar_t cv_dummyteam = CVAR_INIT ("dummyteam", "Spectator", CV_HIDDEN, dummyteam_cons_t, NULL); //static cv_dummyspectate = CVAR_INITconsvar_t ("dummyspectate", "Spectator", CV_HIDDEN, dummyspectate_cons_t, NULL); @@ -204,8 +201,8 @@ consvar_t cv_dummyprofilename = CVAR_INIT ("dummyprofilename", "", CV_HIDDEN, NU consvar_t cv_dummyprofileplayername = CVAR_INIT ("dummyprofileplayername", "", CV_HIDDEN, NULL, NULL); consvar_t cv_dummyprofilekickstart = CVAR_INIT ("dummyprofilekickstart", "Off", CV_HIDDEN, CV_OnOff, NULL); -consvar_t cv_dummygpdifficulty = CVAR_INIT ("dummygpdifficulty", "Normal", CV_HIDDEN, dummygpdifficulty_cons_t, NULL); -consvar_t cv_dummykartspeed = CVAR_INIT ("dummykartspeed", "Auto", CV_HIDDEN, dummykartspeed_cons_t, NULL); +consvar_t cv_dummygpdifficulty = CVAR_INIT ("dummygpdifficulty", "Normal", CV_HIDDEN, gpdifficulty_cons_t, NULL); +consvar_t cv_dummykartspeed = CVAR_INIT ("dummykartspeed", "Auto", CV_HIDDEN, kartspeed_cons_t, NULL); consvar_t cv_dummygpencore = CVAR_INIT ("dummygpencore", "No", CV_HIDDEN, CV_YesNo, NULL); static void M_UpdateAddonsSearch(void); From 8d938bef9059840997243cabaa4e0329a0802628 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 4 Sep 2022 15:53:51 +0100 Subject: [PATCH 355/379] Add quick define-toggled testing apparatus to M_SecretUnlocked. --- src/m_cond.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/m_cond.c b/src/m_cond.c index 1b99010b9..0760275f2 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -374,10 +374,14 @@ UINT8 M_SecretUnlocked(INT32 type) { INT32 i; -#if 1 if (dedicated) return true; -#endif + +#if 0 + (void)type; + (void)i; + return false; // for quick testing +#else #ifdef DEVELOP #define CHADYES true @@ -393,6 +397,7 @@ UINT8 M_SecretUnlocked(INT32 type) return CHADYES; #undef CHADYES +#endif //if 0 } UINT8 M_MapLocked(INT32 mapnum) From 01af5127c827f71a1bfabdab92a35962aa620284 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 4 Sep 2022 14:23:51 -0400 Subject: [PATCH 356/379] Improved pain then hit confirm sound - The pain + hit confirm delay is done for all players, instead of only the damaged player. - The player who got the hit also gets to hear their pain sound at full volume. - Changed the code so that your hit confirm sound effect will no longer be interrupted if the player who got hit left the game. --- src/d_player.h | 4 +- src/k_kart.c | 94 +++++++++++++++++++++++++-------------------- src/k_kart.h | 5 ++- src/lua_baselib.c | 23 +++++++++-- src/lua_playerlib.c | 16 ++++---- src/p_inter.c | 6 +-- src/p_saveg.c | 8 ++-- 7 files changed, 91 insertions(+), 65 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 85eec2e3e..bf92758f8 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -504,8 +504,8 @@ typedef struct player_s SINT8 lastjawztarget; // (-1 to 15) - Last person you target with jawz, for playing the target switch sfx UINT8 jawztargetdelay; // (0 to 5) - Delay for Jawz target switching, to make it less twitchy - UINT8 confirmInflictor; // Player ID that dealt damage to you - UINT8 confirmInflictorDelay; // Delay before playing the sound + UINT8 confirmVictim; // Player ID that you dealt damage to + UINT8 confirmVictimDelay; // Delay before playing the sound UINT8 trickpanel; // Trick panel state UINT8 tricktime; // Increases while you're tricking. You can't input any trick until it's reached a certain threshold diff --git a/src/k_kart.c b/src/k_kart.c index 189b946cd..52ac8c12c 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2824,91 +2824,101 @@ void K_PlayOvertakeSound(mobj_t *source) K_RegularVoiceTimers(source->player); } -void K_PlayPainSound(mobj_t *source) +void K_PlayPainSound(mobj_t *source, mobj_t *other) { sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons + sfxenum_t sfx_id = ((skin_t *)source->skin)->soundsid[S_sfx[sfx_khurt1 + pick].skinsound]; + boolean alwaysHear = false; + + if (other != NULL && P_MobjWasRemoved(other) == false && other->player != NULL) + { + alwaysHear = P_IsDisplayPlayer(other->player); + } + if (cv_kartvoices.value) - S_StartSound(source, sfx_khurt1 + pick); + { + S_StartSound(alwaysHear ? NULL : source, sfx_id); + } K_RegularVoiceTimers(source->player); } -void K_PlayHitEmSound(mobj_t *source, mobj_t *victim) +void K_PlayHitEmSound(mobj_t *source, mobj_t *other) { - const boolean victimIsLocal = (victim != NULL && P_IsDisplayPlayer(victim->player) == true); + sfxenum_t sfx_id = ((skin_t *)source->skin)->soundsid[S_sfx[sfx_khitem].skinsound]; + boolean alwaysHear = false; - if (source->player->follower) + if (other != NULL && P_MobjWasRemoved(other) == false && other->player != NULL) { - follower_t fl = followers[source->player->followerskin]; - source->player->follower->movecount = fl.hitconfirmtime; // movecount is used to play the hitconfirm animation for followers. + alwaysHear = P_IsDisplayPlayer(other->player); } if (cv_kartvoices.value) { - if (victimIsLocal == false) - { - S_StartSound(source, sfx_khitem); - } - } - else - { - S_StartSound(source, sfx_s1c9); // The only lost gameplay functionality with voices disabled + S_StartSound(alwaysHear ? NULL : source, sfx_id); } K_RegularVoiceTimers(source->player); +} - if (victim != NULL && victim->player != NULL) +void K_TryHurtSoundExchange(mobj_t *victim, mobj_t *attacker) +{ + if (victim == NULL || P_MobjWasRemoved(victim) == true || victim->player == NULL) { - victim->player->confirmInflictor = source->player - players; - victim->player->confirmInflictorDelay = TICRATE/2; + return; } + + // In a perfect world we could move this here, but there's + // a few niche situations where we want a pain sound from + // the victim, but no confirm sound from the attacker. + // (ex: DMG_STING) + + //K_PlayPainSound(victim, attacker); + + if (attacker == NULL || P_MobjWasRemoved(attacker) == true || attacker->player == NULL) + { + return; + } + + attacker->player->confirmVictim = (victim->player - players); + attacker->player->confirmVictimDelay = TICRATE/2; } void K_PlayPowerGloatSound(mobj_t *source) { if (cv_kartvoices.value) + { S_StartSound(source, sfx_kgloat); + } K_RegularVoiceTimers(source->player); } static void K_HandleDelayedHitByEm(player_t *player) { - if (player->confirmInflictorDelay == 0) + if (player->confirmVictimDelay == 0) { return; } - player->confirmInflictorDelay--; + player->confirmVictimDelay--; - if (player->confirmInflictorDelay == 0 - && P_IsDisplayPlayer(player) == true - && cv_kartvoices.value) + if (player->confirmVictimDelay == 0) { - player_t *inflictor = NULL; + mobj_t *victim = NULL; - if (player->confirmInflictor >= MAXPLAYERS) + if (player->confirmVictim < MAXPLAYERS && playeringame[player->confirmVictim]) { - return; + player_t *victimPlayer = &players[player->confirmVictim]; + + if (victimPlayer != NULL && victimPlayer->spectator == false) + { + victim = victimPlayer->mo; + } } - if (!playeringame[player->confirmInflictor]) - { - return; - } - - inflictor = &players[player->confirmInflictor]; - if (inflictor == NULL || inflictor->spectator) - { - return; - } - - if (inflictor->mo != NULL && P_MobjWasRemoved(inflictor->mo) == false) - { - sfxenum_t sfx_id = ((skin_t *)inflictor->mo->skin)->soundsid[S_sfx[sfx_khitem].skinsound]; - S_StartSound(NULL, sfx_id); - } + K_PlayHitEmSound(player->mo, victim); } } diff --git a/src/k_kart.h b/src/k_kart.h index 00c36ea76..bab501200 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -154,8 +154,9 @@ void K_HandleDirectionalInfluence(player_t *player); void K_PlayAttackTaunt(mobj_t *source); void K_PlayBoostTaunt(mobj_t *source); void K_PlayOvertakeSound(mobj_t *source); -void K_PlayPainSound(mobj_t *source); -void K_PlayHitEmSound(mobj_t *source, mobj_t *victim); +void K_PlayPainSound(mobj_t *source, mobj_t *other); +void K_PlayHitEmSound(mobj_t *source, mobj_t *other); +void K_TryHurtSoundExchange(mobj_t *victim, mobj_t *attacker); void K_PlayPowerGloatSound(mobj_t *source); fixed_t K_ItemScaleForPlayer(player_t *player); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 301665e45..e172ea978 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -3339,23 +3339,37 @@ static int lib_kOvertakeSound(lua_State *L) static int lib_kPainSound(lua_State *L) { mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + mobj_t *other = NULL; NOHUD if (!mobj->player) return luaL_error(L, "K_PlayPainSound: mobj_t isn't a player object."); //Nothing bad would happen if we let it run the func, but telling why it ain't doing anything is helpful. - K_PlayPainSound(mobj); + if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) + other = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); + K_PlayPainSound(mobj, other); return 0; } static int lib_kHitEmSound(lua_State *L) { mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); - mobj_t *victim = NULL; + mobj_t *other = NULL; NOHUD if (!mobj->player) return luaL_error(L, "K_PlayHitEmSound: mobj_t isn't a player object."); //Nothing bad would happen if we let it run the func, but telling why it ain't doing anything is helpful. if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) - victim = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); - K_PlayHitEmSound(mobj, victim); + other = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); + K_PlayHitEmSound(mobj, other); + return 0; +} + +static int lib_kTryHurtSoundExchange(lua_State *L) +{ + mobj_t *victim = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + mobj_t *attacker = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); + NOHUD + if (!victim->player) + return luaL_error(L, "K_TryHurtSoundExchange: mobj_t isn't a player object."); //Nothing bad would happen if we let it run the func, but telling why it ain't doing anything is helpful. + K_TryHurtSoundExchange(victim, attacker); return 0; } @@ -4037,6 +4051,7 @@ static luaL_Reg lib[] = { {"K_PlayLossSound", lib_kLossSound}, {"K_PlayPainSound", lib_kPainSound}, {"K_PlayHitEmSound", lib_kHitEmSound}, + {"K_TryHurtSoundExchange", lib_kTryHurtSoundExchange}, {"K_IsPlayerLosing",lib_kIsPlayerLosing}, {"K_IsPlayerWanted",lib_kIsPlayerWanted}, {"K_KartBouncing",lib_kKartBouncing}, diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 1d5f6bee7..e166a9b07 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -360,10 +360,10 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->lastjawztarget); else if (fastcmp(field,"jawztargetdelay")) lua_pushinteger(L, plr->jawztargetdelay); - else if (fastcmp(field,"confirmInflictor")) - lua_pushinteger(L, plr->confirmInflictor); - else if (fastcmp(field,"confirmInflictorDelay")) - lua_pushinteger(L, plr->confirmInflictorDelay); + else if (fastcmp(field,"confirmVictim")) + lua_pushinteger(L, plr->confirmVictim); + else if (fastcmp(field,"confirmVictimDelay")) + lua_pushinteger(L, plr->confirmVictimDelay); else if (fastcmp(field,"glanceDir")) lua_pushinteger(L, plr->glanceDir); else if (fastcmp(field,"trickpanel")) @@ -718,10 +718,10 @@ static int player_set(lua_State *L) plr->lastjawztarget = luaL_checkinteger(L, 3); else if (fastcmp(field,"jawztargetdelay")) plr->jawztargetdelay = luaL_checkinteger(L, 3); - else if (fastcmp(field,"confirmInflictor")) - plr->confirmInflictor = luaL_checkinteger(L, 3); - else if (fastcmp(field,"confirmInflictorDelay")) - plr->confirmInflictorDelay = luaL_checkinteger(L, 3); + else if (fastcmp(field,"confirmVictim")) + plr->confirmVictim = luaL_checkinteger(L, 3); + else if (fastcmp(field,"confirmVictimDelay")) + plr->confirmVictimDelay = luaL_checkinteger(L, 3); else if (fastcmp(field,"glanceDir")) plr->glanceDir = luaL_checkinteger(L, 3); else if (fastcmp(field,"trickpanel")) diff --git a/src/p_inter.c b/src/p_inter.c index 0974e11d7..880e1ef7d 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2010,7 +2010,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da source->player->invincibilitytimer += kinvextend; } - K_PlayHitEmSound(source, target); + K_TryHurtSoundExchange(target, source); K_BattleAwardHit(source->player, player, inflictor, takeBumpers); K_TakeBumpersFromPlayer(source->player, player, takeBumpers); @@ -2083,14 +2083,14 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da player->flashing = K_GetKartFlashing(player); } - P_PlayRinglossSound(player->mo); + P_PlayRinglossSound(target); if (ringburst > 0) { P_PlayerRingBurst(player, ringburst); } - K_PlayPainSound(player->mo); + K_PlayPainSound(target, source); if ((hardhit == true) || (cv_kartdebughuddrop.value && !modeattacking)) { diff --git a/src/p_saveg.c b/src/p_saveg.c index 5569f24d5..689da4386 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -337,8 +337,8 @@ static void P_NetArchivePlayers(void) WRITESINT8(save_p, players[i].lastjawztarget); WRITEUINT8(save_p, players[i].jawztargetdelay); - WRITEUINT8(save_p, players[i].confirmInflictor); - WRITEUINT8(save_p, players[i].confirmInflictorDelay); + WRITEUINT8(save_p, players[i].confirmVictim); + WRITEUINT8(save_p, players[i].confirmVictimDelay); WRITEUINT8(save_p, players[i].trickpanel); WRITEUINT8(save_p, players[i].tricktime); @@ -622,8 +622,8 @@ static void P_NetUnArchivePlayers(void) players[i].lastjawztarget = READSINT8(save_p); players[i].jawztargetdelay = READUINT8(save_p); - players[i].confirmInflictor = READUINT8(save_p); - players[i].confirmInflictorDelay = READUINT8(save_p); + players[i].confirmVictim = READUINT8(save_p); + players[i].confirmVictimDelay = READUINT8(save_p); players[i].trickpanel = READUINT8(save_p); players[i].tricktime = READUINT8(save_p); From acd777a77a2cdb2a53b3eef7a2a9aec407405caf Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 4 Sep 2022 20:14:30 +0100 Subject: [PATCH 357/379] Significant work into the play-starting experience - Take the weird extra mini settings menu out of the Online Host Game sequence - Make sure to actively use the server variables in Match Race creation - Hides the Auto options for Encore and Kartspeed on the Match Race menu specifically... - BUT if you leave it at the default it'll secretly set them to Auto!!! --- src/command.c | 10 ++++++- src/command.h | 2 +- src/k_menu.h | 14 +++++++++- src/k_menudef.c | 18 +++++------- src/k_menudraw.c | 31 ++++++++++++-------- src/k_menufunc.c | 73 +++++++++++++++++++----------------------------- 6 files changed, 77 insertions(+), 71 deletions(-) diff --git a/src/command.c b/src/command.c index c6e048fdb..1439463c1 100644 --- a/src/command.c +++ b/src/command.c @@ -82,6 +82,12 @@ CV_PossibleValue_t kartspeed_cons_t[] = { {KARTSPEED_HARD, "Hard"}, {0, NULL} }; +CV_PossibleValue_t dummykartspeed_cons_t[] = { + {KARTSPEED_EASY, "Easy"}, + {KARTSPEED_NORMAL, "Normal"}, + {KARTSPEED_HARD, "Hard"}, + {0, NULL} +}; CV_PossibleValue_t gpdifficulty_cons_t[] = { {KARTSPEED_EASY, "Easy"}, {KARTSPEED_NORMAL, "Normal"}, @@ -2098,7 +2104,9 @@ void CV_AddValue(consvar_t *var, INT32 increment) return; } } - else if (var->PossibleValue == kartspeed_cons_t || var->PossibleValue == gpdifficulty_cons_t) + else if (var->PossibleValue == kartspeed_cons_t + || var->PossibleValue == dummykartspeed_cons_t + || var->PossibleValue == gpdifficulty_cons_t) { if (!M_SecretUnlocked(SECRET_HARDSPEED)) { diff --git a/src/command.h b/src/command.h index 9e52df9e4..876dce67f 100644 --- a/src/command.h +++ b/src/command.h @@ -175,7 +175,7 @@ extern CV_PossibleValue_t CV_Natural[]; #define KARTSPEED_NORMAL 1 #define KARTSPEED_HARD 2 #define KARTGP_MASTER 3 // Not a speed setting, gives the hardest speed with maxed out bots -extern CV_PossibleValue_t kartspeed_cons_t[], gpdifficulty_cons_t[]; +extern CV_PossibleValue_t kartspeed_cons_t[], dummykartspeed_cons_t[], gpdifficulty_cons_t[]; extern consvar_t cv_execversion; diff --git a/src/k_menu.h b/src/k_menu.h index e85d2425e..eafc8d2cf 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -193,6 +193,19 @@ extern menu_t PLAY_GamemodesDef; extern menuitem_t PLAY_RaceGamemodesMenu[]; extern menu_t PLAY_RaceGamemodesDef; +typedef enum +{ + drace_gpdifficulty = 0, + drace_mrkartspeed, + drace_mrcpu, + drace_mrracers, + drace_encore, + drace_boxend, + drace_cupselect = drace_boxend, + drace_mapselect, + drace_back +} drace_e; + extern menuitem_t PLAY_RaceDifficulty[]; extern menu_t PLAY_RaceDifficultyDef; @@ -673,7 +686,6 @@ extern consvar_t cv_dummygpencore; extern consvar_t cv_dummymatchbots; void M_SetupDifficultySelect(INT32 choice); -void M_SetupDifficultySelectMP(INT32 choice); void M_DifficultySelectInputs(INT32 choice); // Multiplayer menu stuff diff --git a/src/k_menudef.c b/src/k_menudef.c index b83c27273..3518d7551 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -119,36 +119,32 @@ menuitem_t PLAY_RaceGamemodesMenu[] = menu_t PLAY_RaceGamemodesDef = KARTGAMEMODEMENU(PLAY_RaceGamemodesMenu, &PLAY_GamemodesDef); -// difficulty selection: +// difficulty selection -- see drace_e menuitem_t PLAY_RaceDifficulty[] = { - // local play + // For GP {IT_STRING | IT_CVAR, "Difficulty", "Select the game difficulty", NULL, {.cvar = &cv_dummygpdifficulty}, 0, 0}, - // netgames + // Match Race {IT_STRING | IT_CVAR, "Difficulty", "Select the game speed", NULL, {.cvar = &cv_dummykartspeed}, 0, 0}, // DISABLE THAT OPTION OUTSIDE OF MATCH RACE {IT_STRING2 | IT_CVAR, "CPU", "Set the difficulty of CPU players.", NULL, {.cvar = &cv_dummymatchbots}, 0, 0}, - {IT_STRING2 | IT_CVAR, "Racers", "Sets the number of racers, including players and CPU.", NULL, {.cvar = &cv_maxplayers}, 0, 0}, {IT_STRING2 | IT_CVAR, "Encore", "Enable or disable Encore mode", NULL, {.cvar = &cv_dummygpencore}, 0, 0}, - // For GP: + // For GP {IT_STRING | IT_CALL, "Cup Select", "Go on and select a cup!", NULL, {.routine = M_LevelSelectInit}, 2, GT_RACE}, - // For Match Race: + // Match Race {IT_STRING | IT_CALL, "Map Select", "Go on and select a race track!", NULL, {.routine = M_LevelSelectInit}, 0, GT_RACE}, - // For Match Race in NETGAMES: - {IT_STRING | IT_CALL, "Map Select", "Go on and select a race track!", NULL, {.routine = M_MPSetupNetgameMapSelect}, 0, GT_RACE}, - {IT_STRING | IT_CALL, "Back", NULL, NULL, {.routine = M_GoBack}, 0, 0}, }; @@ -348,7 +344,7 @@ menu_t PLAY_MP_OptSelectDef = { NULL }; -// MULTIPLAYER HOST SCREEN +// MULTIPLAYER HOST SCREEN -- see mhost_e menuitem_t PLAY_MP_Host[] = { //{IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, M_MPOptSelect, 0, 0}, @@ -366,7 +362,7 @@ menuitem_t PLAY_MP_Host[] = NULL, {.cvar = &cv_dummygametype}, 0, 0}, {IT_STRING | IT_CALL, "GO", "Select a map with the currently selected gamemode", - NULL, {.routine = M_SetupDifficultySelectMP}, 0, 0}, + NULL, {.routine = M_MPSetupNetgameMapSelect}, 0, 0}, }; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 5bfe908b8..71beb1cc4 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1710,7 +1710,6 @@ void M_DrawCharacterSelect(void) void M_DrawRaceDifficulty(void) { - UINT8 n = currentMenu->numitems-4; patch_t *box = W_CachePatchName("M_DBOX", PU_CACHE); INT32 i; @@ -1729,9 +1728,8 @@ void M_DrawRaceDifficulty(void) for (i = 0; i < currentMenu->numitems; i++) { - if (i >= n) + if (i >= drace_boxend) { - x = GM_STARTX + (GM_XOFFSET * 5 / 2); y = GM_STARTY + (GM_YOFFSET * 5 / 2); @@ -2276,7 +2274,7 @@ static void M_DrawEggaChannel(void) { patch_t *background = W_CachePatchName("M_EGGACH", PU_CACHE); - V_DrawFill(0, 0, 999, 999, 25); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 25); V_DrawFixedPatch(160<numitems; i++) { - if (i == currentMenu->numitems-1) { + xp = 202; + yp = 100; UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_MOSS, GTC_CACHE); if (i == itemOn) colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE); // Ideally we'd calculate this but it's not worth it for a 1-off menu probably..... - V_DrawFixedPatch(202<width/2), 100 -3, V_ALLOWLOWERCASE, colormap, currentMenu->menuitems[i].text); + V_DrawFixedPatch(xp<width/2), yp -3, V_ALLOWLOWERCASE, colormap, currentMenu->menuitems[i].text); } else { switch (currentMenu->menuitems[i].status & IT_DISPLAY) { + case IT_TRANSTEXT2: + { + V_DrawThinString(xp, yp, V_ALLOWLOWERCASE|V_6WIDTHSPACE|V_TRANSLUCENT, currentMenu->menuitems[i].text); + xp += 5; + yp += 11; + break; + } case IT_STRING: { - V_DrawString(xp, yp, V_ALLOWLOWERCASE | (i == itemOn ? highlightflags : 0), currentMenu->menuitems[i].text); + V_DrawThinString(xp, yp, V_ALLOWLOWERCASE|V_6WIDTHSPACE | (i == itemOn ? highlightflags : 0), currentMenu->menuitems[i].text); // Cvar specific handling switch (currentMenu->menuitems[i].status & IT_TYPE) @@ -2330,15 +2337,15 @@ void M_DrawMPHost(void) switch (currentMenu->menuitems[i].status & IT_CVARTYPE) { case IT_CV_STRING: - V_DrawThinString(xp + 96, yp, V_ALLOWLOWERCASE, cv->string); + V_DrawThinString(xp + 96, yp, V_ALLOWLOWERCASE|V_6WIDTHSPACE, cv->string); if (skullAnimCounter < 4 && i == itemOn) - V_DrawCharacter(xp + 93 + V_ThinStringWidth(cv->string, 0), yp +1, '_' | 0x80, false); + V_DrawString(xp + 94 + V_ThinStringWidth(cv->string, V_6WIDTHSPACE), yp+1, 0, "_"); break; default: - w = V_StringWidth(cv->string, 0); - V_DrawString(xp + 138 - w, yp, ((cv->flags & CV_CHEAT) && !CV_IsSetToDefault(cv) ? warningflags : highlightflags), cv->string); + w = V_ThinStringWidth(cv->string, V_6WIDTHSPACE); + V_DrawThinString(xp + 138 - w, yp, ((cv->flags & CV_CHEAT) && !CV_IsSetToDefault(cv) ? warningflags : highlightflags)|V_6WIDTHSPACE, cv->string); if (i == itemOn) { V_DrawCharacter(xp + 138 - 10 - w - (skullAnimCounter/5), yp, '\x1C' | highlightflags, false); // left arrow diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 77a211953..cec36ce2d 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -202,8 +202,8 @@ consvar_t cv_dummyprofileplayername = CVAR_INIT ("dummyprofileplayername", "", C consvar_t cv_dummyprofilekickstart = CVAR_INIT ("dummyprofilekickstart", "Off", CV_HIDDEN, CV_OnOff, NULL); consvar_t cv_dummygpdifficulty = CVAR_INIT ("dummygpdifficulty", "Normal", CV_HIDDEN, gpdifficulty_cons_t, NULL); -consvar_t cv_dummykartspeed = CVAR_INIT ("dummykartspeed", "Auto", CV_HIDDEN, kartspeed_cons_t, NULL); -consvar_t cv_dummygpencore = CVAR_INIT ("dummygpencore", "No", CV_HIDDEN, CV_YesNo, NULL); +consvar_t cv_dummykartspeed = CVAR_INIT ("dummykartspeed", "Normal", CV_HIDDEN, dummykartspeed_cons_t, NULL); +consvar_t cv_dummygpencore = CVAR_INIT ("dummygpencore", "Off", CV_HIDDEN, CV_OnOff, NULL); static void M_UpdateAddonsSearch(void); consvar_t cv_dummyaddonsearch = CVAR_INIT ("dummyaddonsearch", "", CV_HIDDEN|CV_CALL|CV_NOINIT, NULL, M_UpdateAddonsSearch); @@ -3129,58 +3129,37 @@ void M_SetupDifficultySelect(INT32 choice) // setup the difficulty menu and then remove choices depending on choice PLAY_RaceDifficultyDef.prevMenu = currentMenu; - PLAY_RaceDifficulty[0].status = IT_DISABLED; - PLAY_RaceDifficulty[1].status = IT_DISABLED; - PLAY_RaceDifficulty[2].status = IT_DISABLED; - PLAY_RaceDifficulty[3].status = IT_DISABLED; - PLAY_RaceDifficulty[4].status = IT_DISABLED; - PLAY_RaceDifficulty[5].status = IT_DISABLED; - PLAY_RaceDifficulty[6].status = IT_DISABLED; - PLAY_RaceDifficulty[7].status = IT_DISABLED; + PLAY_RaceDifficulty[drace_gpdifficulty].status = IT_DISABLED; + PLAY_RaceDifficulty[drace_mrkartspeed].status = IT_DISABLED; + PLAY_RaceDifficulty[drace_mrcpu].status = IT_DISABLED; + PLAY_RaceDifficulty[drace_mrracers].status = IT_DISABLED; + PLAY_RaceDifficulty[drace_encore].status = IT_DISABLED; + PLAY_RaceDifficulty[drace_cupselect].status = IT_DISABLED; + PLAY_RaceDifficulty[drace_mapselect].status = IT_DISABLED; if (choice) // Match Race { - PLAY_RaceDifficulty[1].status = IT_STRING|IT_CVAR; // Kart Speed - PLAY_RaceDifficulty[2].status = IT_STRING2|IT_CVAR; // CPUs on/off - PLAY_RaceDifficulty[3].status = IT_STRING2|IT_CVAR; // CPU amount - PLAY_RaceDifficulty[4].status = IT_STRING2|IT_CVAR; // Encore on/off - PLAY_RaceDifficulty[6].status = IT_STRING|IT_CALL; // Level Select (Match Race) - PLAY_RaceDifficultyDef.lastOn = 5; // Select cup select by default. - + PLAY_RaceDifficulty[drace_mrkartspeed].status = IT_STRING|IT_CVAR; // Kart Speed + PLAY_RaceDifficulty[drace_mrcpu].status = IT_STRING2|IT_CVAR; // CPUs on/off + PLAY_RaceDifficulty[drace_mrracers].status = IT_STRING2|IT_CVAR; // CPU amount + PLAY_RaceDifficulty[drace_mapselect].status = IT_STRING|IT_CALL; // Level Select (Match Race) + PLAY_RaceDifficultyDef.lastOn = drace_mapselect; // Select map select by default. } else // GP { - PLAY_RaceDifficulty[0].status = IT_STRING|IT_CVAR; // Difficulty - PLAY_RaceDifficulty[4].status = IT_STRING2|IT_CVAR; // Encore on/off - PLAY_RaceDifficulty[5].status = IT_STRING|IT_CALL; // Level Select (GP) - PLAY_RaceDifficultyDef.lastOn = 4; // Select cup select by default. + PLAY_RaceDifficulty[drace_gpdifficulty].status = IT_STRING|IT_CVAR; // Difficulty + PLAY_RaceDifficulty[drace_cupselect].status = IT_STRING|IT_CALL; // Level Select (GP) + PLAY_RaceDifficultyDef.lastOn = drace_cupselect; // Select cup select by default. + } + + if (M_SecretUnlocked(SECRET_ENCORE)) + { + PLAY_RaceDifficulty[drace_encore].status = IT_STRING2|IT_CVAR; // Encore on/off } M_SetupNextMenu(&PLAY_RaceDifficultyDef, false); } -// calls the above but changes the cvar we set -void M_SetupDifficultySelectMP(INT32 choice) -{ - (void) choice; - - PLAY_RaceDifficultyDef.prevMenu = currentMenu; - M_SetupNextMenu(&PLAY_RaceDifficultyDef, false); - - PLAY_RaceDifficulty[0].status = IT_DISABLED; - PLAY_RaceDifficulty[1].status = IT_STRING|IT_CVAR; - PLAY_RaceDifficulty[2].status = IT_STRING2|IT_CVAR; // CPUs on/off use string2 to signify not to use the normal gm font drawer - PLAY_RaceDifficulty[3].status = IT_STRING2|IT_CVAR; // Encore on/off use string2 to signify not to use the normal gm font drawer - PLAY_RaceDifficulty[4].status = IT_DISABLED; - PLAY_RaceDifficulty[5].status = IT_DISABLED; - PLAY_RaceDifficulty[6].status = IT_STRING|IT_CALL; - - itemOn = 6; // Select cup select by default. - - // okay this is REALLY stupid but this fixes the host menu re-folding on itself when we go back. - mpmenu.modewinextend[0][0] = 1; -} - // LEVEL SELECT // @@ -3626,9 +3605,10 @@ void M_LevelSelectHandler(INT32 choice) netgame = levellist.netgame; // ^ ditto. CV_StealthSet(&cv_kartbot, cv_dummymatchbots.string); - CV_StealthSet(&cv_kartspeed, cv_dummykartspeed.string); + CV_StealthSet(&cv_kartencore, (cv_dummygpencore.value == 1) ? "On" : "Auto"); + CV_StealthSet(&cv_kartspeed, (cv_dummykartspeed.value == KARTSPEED_NORMAL) ? "Auto" : cv_dummykartspeed.string); - D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_dummygpencore.value == 1), 1, 1, false, false); + D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false); } else { @@ -3830,6 +3810,9 @@ void M_MPSetupNetgameMapSelect(INT32 choice) } } + // okay this is REALLY stupid but this fixes the host menu re-folding on itself when we go back. + mpmenu.modewinextend[0][0] = 1; + M_LevelListFromGametype(gt); // Setup the level select. // (This will also automatically send us to the apropriate menu) } From de5370b134c668727f3f05d0844f6d0c80745c10 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 4 Sep 2022 21:08:44 +0100 Subject: [PATCH 358/379] Fix the options menu on first boot bug. - Was reproducible by spamming back button events during game launch. - Caused by the way profile data was initialised - D_StartTitle was calling a function with too many side effects. - In addition, never instantly skip the title screen when the above occours. - This would be enough to patch over the mentioned bug, but I made sure to solve it properly so it won't break when we touch this again later. --- src/d_main.c | 3 --- src/k_menufunc.c | 10 +++++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index da324bfa0..8d6e7935e 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1003,9 +1003,6 @@ void D_StartTitle(void) advancedemo = false; F_StartTitleScreen(); - M_InitOptions(0); // Make sure the option menu is ready to go since we need to select a profile. - currentMenu = &MAIN_ProfilesDef; // reset the current menu ID - // Reset the palette if (rendermode != render_none) V_SetPaletteLump("PLAYPAL"); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index cec36ce2d..7608ccaab 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -87,7 +87,7 @@ boolean menuactive = false; boolean fromlevelselect = false; // current menudef -menu_t *currentMenu = &OPTIONS_ProfilesDef; +menu_t *currentMenu = &MAIN_ProfilesDef; char dummystaffname[22]; @@ -925,6 +925,11 @@ void M_StartControlPanel(void) menucmd[i].delay = MENUDELAYTIME; } + // No instantly skipping the titlescreen. + // (We can change this timer later when extra animation is added.) + if (gamestate == GS_TITLESCREEN && finalecount < 1) + return; + // intro might call this repeatedly if (menuactive) { @@ -949,6 +954,9 @@ void M_StartControlPanel(void) { if (cv_currprofile.value == -1) // Only ask once per session. { + // Make sure the profile data is ready now since we need to select a profile. + M_ResetOptions(); + // we need to do this before setting ApplyProfile otherwise funky things are going to happen. currentMenu = &MAIN_ProfilesDef; optionsmenu.profilen = cv_ttlprofilen.value; From 49f75520092a5abb39b80bde50705506c6eea022 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 4 Sep 2022 21:44:39 +0100 Subject: [PATCH 359/379] Improve profile deletion further - Also adjust cv_currprofile in PR_DeleteProfile, instead of half-heartedly outside - Make it clearer if you're going to destroy your current profile - Add an "[In use]" identifier to the menu's visuals --- src/k_menudraw.c | 6 +++++- src/k_menufunc.c | 7 +++---- src/k_profiles.c | 36 +++++++++++++++++++++++------------- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 71beb1cc4..062eec3b2 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2852,7 +2852,11 @@ void M_DrawProfileErase(void) V_DrawScaledPatch(x - 24, cursory, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); } - V_DrawString(x, y, i == optionsmenu.eraseprofilen ? highlightflags : 0, va("PRF%03d - %s (%s)", i, pr->profilename, pr->playername)); + V_DrawString(x, y, + (i == optionsmenu.eraseprofilen ? highlightflags : 0)|V_ALLOWLOWERCASE, + va("%sPRF%03d - %s (%s)", + (cv_currprofile.value == i) ? "[In use] " : "", + i, pr->profilename, pr->playername)); y += SMALLLINEHEIGHT; } } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 7608ccaab..e1e14c8ef 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -5536,14 +5536,13 @@ static void M_EraseProfileResponse(INT32 choice) { if (choice == MA_YES) { - const boolean current = (optionsmenu.eraseprofilen == cv_currprofile.value); // has to be grabbed before deletion S_StartSound(NULL, sfx_itrole); // bweh heh heh PR_DeleteProfile(optionsmenu.eraseprofilen); - if (current) + // Did we bust our current profile..!? + if (cv_currprofile.value == -1) { - CV_StealthSetValue(&cv_currprofile, -1); F_StartIntro(); M_ClearMenus(true); } @@ -5589,7 +5588,7 @@ void M_HandleProfileErase(INT32 choice) else if (M_MenuConfirmPressed(pid)) { if (optionsmenu.eraseprofilen == cv_currprofile.value) - M_StartMessage("This profile will be erased.\nAre you sure you want to proceed?\nDeleting this profile will also\nreturn you to the title screen.\n\n(Press A to confirm)", FUNCPTRCAST(M_EraseProfileResponse), MM_YESNO); + M_StartMessage("Your ""\x85""current profile""\x80"" will be erased.\nAre you sure you want to proceed?\nDeleting this profile will also\nreturn you to the title screen.\n\n(Press A to confirm)", FUNCPTRCAST(M_EraseProfileResponse), MM_YESNO); else M_StartMessage("This profile will be erased.\nAre you sure you want to proceed?\n\n(Press A to confirm)", FUNCPTRCAST(M_EraseProfileResponse), MM_YESNO); diff --git a/src/k_profiles.c b/src/k_profiles.c index 5067b98ae..5f50d189f 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -117,30 +117,40 @@ boolean PR_DeleteProfile(INT32 num) profilesList[i] = profilesList[i+1]; } - // Make sure to move cv_lastprofile (and title profile) values as well! - for (i = 0; i < MAXSPLITSCREENPLAYERS+1; i++) + // Make sure to move cv_lastprofile (and title/current profile) values as well! + for (i = 0; i < MAXSPLITSCREENPLAYERS+2; i++) { - consvar_t *cv = (i == MAXSPLITSCREENPLAYERS) ? &cv_ttlprofilen : &cv_lastprofile[i]; - INT32 profileset = cv->value; + consvar_t *cv; - if (profileset < num) + if (i < MAXSPLITSCREENPLAYERS) + cv = &cv_lastprofile[i]; + else if (i == MAXSPLITSCREENPLAYERS) + cv = &cv_ttlprofilen; + else + cv = &cv_currprofile; + + if (cv->value < num) { // Not affected. continue; } - if (profileset > num) + if (cv->value > num) { // Shift our lastprofile number down to match the new order. - profileset--; - } - else - { - // There's no hope for it. If we were on the deleted profile, default back to guest. - profileset = PROFILE_GUEST; + CV_StealthSetValue(cv, cv->value-1); + continue; } - CV_StealthSetValue(cv, profileset); + if (cv != &cv_currprofile) + { + // There's no hope for it. If we were on the deleted profile, default back to guest. + CV_StealthSetValue(cv, PROFILE_GUEST); + continue; + } + + // Oh boy, now we're really in for it. + CV_StealthSetValue(cv, -1); } } From 9d26fa20407ac81830c3c546c42b837c216dcfa4 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 4 Sep 2022 22:02:27 +0100 Subject: [PATCH 360/379] Fix the vertical position of `%d ADD-ON(S) LOADED` if there are a limited number of add-on results --- src/k_menudraw.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 062eec3b2..8a6c0ae74 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -4361,6 +4361,11 @@ void M_DrawAddons(void) if (m != (ssize_t)sizedirmenu) V_DrawString(19, y-12 + (skullAnimCounter/5), highlightflags, "\x1B"); + if (m < (2*numaddonsshown + 1)) + { + y += ((2*numaddonsshown + 1)-m)*addonsseperation; + } + y -= 2; V_DrawSmallScaledPatch(x, y, ((!majormods) ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+4]); From 0c4dca611dd91014075930c68ae8d30beea4213c Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 4 Sep 2022 22:14:41 +0100 Subject: [PATCH 361/379] Hide Encore on the Gameplay Options menu if not unlocked --- src/k_menu.h | 15 +++++++++++++++ src/k_menudef.c | 2 +- src/k_menufunc.c | 2 ++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/k_menu.h b/src/k_menu.h index eafc8d2cf..53afa40e6 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -325,6 +325,21 @@ extern menu_t OPTIONS_HUDDef; extern menuitem_t OPTIONS_HUDOnline[]; extern menu_t OPTIONS_HUDOnlineDef; +typedef enum +{ + gopt_gamespeed = 0, + gopt_baselapcount, + gopt_frantic, + gopt_encore, + gopt_exitcountdown, + gopt_spacer1, + gopt_timelimit, + gopt_startingbumpers, + gopt_karmacomeback, + gopt_spacer2, + gopt_itemtoggles +} gopt_e; + extern menuitem_t OPTIONS_Gameplay[]; extern menu_t OPTIONS_GameplayDef; diff --git a/src/k_menudef.c b/src/k_menudef.c index 3518d7551..b7a63da36 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -1007,7 +1007,7 @@ menu_t OPTIONS_HUDOnlineDef = { NULL, }; - +// Gameplay options -- see gopt_e menuitem_t OPTIONS_Gameplay[] = { diff --git a/src/k_menufunc.c b/src/k_menufunc.c index e1e14c8ef..c97144d32 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -4417,6 +4417,8 @@ void M_InitOptions(INT32 choice) { OPTIONS_MainDef.menuitems[mopt_gameplay].status = IT_STRING | IT_SUBMENU; OPTIONS_MainDef.menuitems[mopt_server].status = IT_STRING | IT_SUBMENU; + OPTIONS_GameplayDef.menuitems[gopt_encore].status = + (M_SecretUnlocked(SECRET_ENCORE) ? (IT_STRING | IT_CVAR) : IT_DISABLED); } OPTIONS_DataDef.menuitems[dopt_erase].status = (gamestate == GS_MENU From 212907122b9094ecc5af750ed152e83bf5df9a71 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 4 Sep 2022 22:21:42 +0100 Subject: [PATCH 362/379] Repair shitsfree --- src/k_menudraw.c | 3 --- src/k_menufunc.c | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 8a6c0ae74..cc8d65c95 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -3467,9 +3467,6 @@ void M_DrawItemToggles(void) V_DrawScaledPatch(onx-1, ony-2, translucent, W_CachePatchName(K_GetItemPatch(currentMenu->menuitems[itemOn].mvar1, false), PU_CACHE)); } } - - if (shitsfree) - shitsfree--; } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index c97144d32..753a9b767 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -4510,6 +4510,9 @@ void M_OptionsTick(void) if (optionsmenu.currcolour != currentMenu->extra1) M_OptionsChangeBGColour(currentMenu->extra1); + // And one last giggle... + if (shitsfree) + shitsfree--; } boolean M_OptionsInputs(INT32 ch) From bc538a066f593b19c4f2466ee8cdc5eba3f89748 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 4 Sep 2022 21:21:25 -0400 Subject: [PATCH 363/379] Fast falling E-Brake in the air for x4 gravity, at the cost of a tiny bounce on landing. --- src/d_player.h | 2 + src/k_kart.c | 51 ++++++++++++++++++---- src/lua_playerlib.c | 4 ++ src/p_map.c | 3 +- src/p_mobj.c | 53 +++++++++++++++------- src/p_saveg.c | 4 ++ src/p_slopes.c | 104 ++++++++++++++++++++++++++++---------------- src/p_slopes.h | 1 + src/p_user.c | 22 +++++++--- src/sounds.c | 4 +- src/sounds.h | 3 ++ 11 files changed, 180 insertions(+), 71 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 85eec2e3e..86f4c718b 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -443,6 +443,8 @@ typedef struct player_s fixed_t spindashspeed; // Spindash release speed UINT8 spindashboost; // Spindash release boost timer + fixed_t fastfall; // Fast fall momentum + UINT8 numboosts; // Count of how many boosts are being stacked, for after image spawning fixed_t boostpower; // Base boost value, for offroad fixed_t speedboost; // Boost value smoothing for max speed diff --git a/src/k_kart.c b/src/k_kart.c index 189b946cd..b31556fc4 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9025,13 +9025,17 @@ static INT32 K_FlameShieldMax(player_t *player) boolean K_PlayerEBrake(player_t *player) { + if (player->fastfall != 0) + { + return true; + } + return (K_GetKartButtons(player) & BT_EBRAKEMASK) == BT_EBRAKEMASK - && P_IsObjectOnGround(player->mo) == true - && player->drift == 0 - && player->spinouttimer == 0 - && player->justbumped == 0 - && player->spindashboost == 0 - && player->nocontrol == 0; + && player->drift == 0 + && player->spinouttimer == 0 + && player->justbumped == 0 + && player->spindashboost == 0 + && player->nocontrol == 0; } SINT8 K_Sliptiding(player_t *player) @@ -9053,9 +9057,8 @@ void K_KartEbrakeVisuals(player_t *p) mobj_t *spdl; fixed_t sx, sy; - if (K_PlayerEBrake(p)) + if (K_PlayerEBrake(p) == true) { - if (p->ebrakefor % 20 == 0) { wave = P_SpawnMobj(p->mo->x, p->mo->y, p->mo->z, MT_SOFTLANDING); @@ -9092,7 +9095,6 @@ void K_KartEbrakeVisuals(player_t *p) K_FlipFromObject(p->mo->hprev, p->mo); } - if (!p->spindash) { // Spawn downwards fastline @@ -9233,6 +9235,7 @@ static void K_KartSpindashWind(mobj_t *parent) static void K_KartSpindash(player_t *player) { + const boolean onGround = P_IsObjectOnGround(player->mo); const INT16 MAXCHARGETIME = K_GetSpindashChargeTime(player); UINT16 buttons = K_GetKartButtons(player); boolean spawnWind = (leveltime % 2 == 0); @@ -9296,6 +9299,36 @@ static void K_KartSpindash(player_t *player) return; } + // Handle fast falling behaviors first. + if (onGround == false) + { + // Update fastfall. + player->fastfall = player->mo->momz; + player->spindash = 0; + return; + } + else if (player->fastfall != 0) + { + // Handle fastfall bounce. + const fixed_t maxBounce = player->mo->scale * 10; + const fixed_t minBounce = player->mo->scale * 2; + fixed_t bounce = abs(player->fastfall) / 4; + + if (bounce > maxBounce) + { + bounce = maxBounce; + } + + if (bounce > minBounce) + { + S_StartSound(player->mo, sfx_ffbonc); + player->mo->momz = bounce * P_MobjFlip(player->mo); + } + + player->fastfall = 0; + return; + } + if (player->speed == 0 && player->steering != 0 && leveltime % 8 == 0) { // Rubber burn turn sfx diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 1d5f6bee7..19b9b4463 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -274,6 +274,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->spindashspeed); else if (fastcmp(field,"spindashboost")) lua_pushinteger(L, plr->spindashboost); + else if (fastcmp(field,"fastfall")) + lua_pushfixed(L, plr->fastfall); else if (fastcmp(field,"numboosts")) lua_pushinteger(L, plr->numboosts); else if (fastcmp(field,"boostpower")) @@ -632,6 +634,8 @@ static int player_set(lua_State *L) plr->spindashspeed = luaL_checkinteger(L, 3); else if (fastcmp(field,"spindashboost")) plr->spindashboost = luaL_checkinteger(L, 3); + else if (fastcmp(field,"fastfall")) + plr->fastfall = luaL_checkfixed(L, 3); else if (fastcmp(field,"numboosts")) plr->numboosts = luaL_checkinteger(L, 3); else if (fastcmp(field,"boostpower")) diff --git a/src/p_map.c b/src/p_map.c index d667b0346..0bf029ab2 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -2463,9 +2463,8 @@ fixed_t P_GetThingStepUp(mobj_t *thing) const fixed_t maxstepmove = P_BaseStepUp(); fixed_t maxstep = maxstepmove; - if (thing->type == MT_SKIM) + if (thing->player && thing->player->fastfall != 0) { - // Skim special (not needed for kart?) return 0; } diff --git a/src/p_mobj.c b/src/p_mobj.c index 8e6e0eb2a..e6c003829 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1132,6 +1132,12 @@ fixed_t P_GetMobjGravity(mobj_t *mo) { gravityadd = FixedMul(TUMBLEGRAVITY, gravityadd); } + + if (mo->player->fastfall != 0) + { + // Fast falling + gravityadd *= 4; + } } else { @@ -1761,7 +1767,9 @@ void P_XYMovement(mobj_t *mo) if (moved && oldslope && !(mo->flags & MF_NOCLIPHEIGHT)) { // Check to see if we ran off - if (oldslope != mo->standingslope) { // First, compare different slopes + if (oldslope != mo->standingslope) + { + // First, compare different slopes angle_t oldangle, newangle; angle_t moveangle = K_MomentumAngle(mo); @@ -1773,7 +1781,9 @@ void P_XYMovement(mobj_t *mo) newangle = 0; // Now compare the Zs of the different quantizations - if (oldangle-newangle > ANG30 && oldangle-newangle < ANGLE_180) { // Allow for a bit of sticking - this value can be adjusted later + if (oldangle-newangle > ANG30 && oldangle-newangle < ANGLE_180) + { + // Allow for a bit of sticking - this value can be adjusted later mo->standingslope = oldslope; P_SetPitchRollFromSlope(mo, mo->standingslope); P_SlopeLaunch(mo); @@ -1781,26 +1791,37 @@ void P_XYMovement(mobj_t *mo) //CONS_Printf("launched off of slope - "); } - /*CONS_Printf("old angle %f - new angle %f = %f\n", - FIXED_TO_FLOAT(AngleFixed(oldangle)), - FIXED_TO_FLOAT(AngleFixed(newangle)), - FIXED_TO_FLOAT(AngleFixed(oldangle-newangle)) - );*/ - // Sryder 2018-11-26: Don't launch here if it's a slope without physics, we stick to those like glue anyway - } else if (predictedz-mo->z > abs(slopemom.z/2) - && !(mo->standingslope->flags & SL_NOPHYSICS)) { // Now check if we were supposed to stick to this slope + /* + CONS_Printf("old angle %f - new angle %f = %f\n", + FIXED_TO_FLOAT(AngleFixed(oldangle)), + FIXED_TO_FLOAT(AngleFixed(newangle)), + FIXED_TO_FLOAT(AngleFixed(oldangle-newangle)) + ); + */ + + } + else if (predictedz - mo->z > abs(slopemom.z/2) + && P_CanApplySlopePhysics(mo, mo->standingslope) == true) // Sryder 2018-11-26: Don't launch here if it's a slope without physics, we stick to those like glue anyway + { + // Now check if we were supposed to stick to this slope //CONS_Printf("%d-%d > %d\n", (predictedz), (mo->z), (slopemom.z/2)); P_SlopeLaunch(mo); } - } else if (moved && mo->standingslope && predictedz) { + } + else if (moved && mo->standingslope && predictedz) + { angle_t moveangle = K_MomentumAngle(mo); angle_t newangle = FixedMul((signed)mo->standingslope->zangle, FINECOSINE((moveangle - mo->standingslope->xydirection) >> ANGLETOFINESHIFT)); - /*CONS_Printf("flat to angle %f - predicted z of %f\n", - FIXED_TO_FLOAT(AngleFixed(ANGLE_MAX-newangle)), - FIXED_TO_FLOAT(predictedz) - );*/ - if (ANGLE_MAX-newangle > ANG30 && newangle > ANGLE_180) { + /* + CONS_Printf("flat to angle %f - predicted z of %f\n", + FIXED_TO_FLOAT(AngleFixed(ANGLE_MAX-newangle)), + FIXED_TO_FLOAT(predictedz) + ); + */ + + if (ANGLE_MAX-newangle > ANG30 && newangle > ANGLE_180) + { mo->momz = P_MobjFlip(mo)*FRACUNIT/2; mo->z = predictedz + P_MobjFlip(mo); mo->standingslope = NULL; diff --git a/src/p_saveg.c b/src/p_saveg.c index 5569f24d5..22e94a419 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -280,6 +280,8 @@ static void P_NetArchivePlayers(void) WRITEFIXED(save_p, players[i].spindashspeed); WRITEUINT8(save_p, players[i].spindashboost); + WRITEFIXED(save_p, players[i].fastfall); + WRITEUINT8(save_p, players[i].numboosts); WRITEFIXED(save_p, players[i].boostpower); WRITEFIXED(save_p, players[i].speedboost); @@ -565,6 +567,8 @@ static void P_NetUnArchivePlayers(void) players[i].spindashspeed = READFIXED(save_p); players[i].spindashboost = READUINT8(save_p); + players[i].fastfall = READFIXED(save_p); + players[i].numboosts = READUINT8(save_p); players[i].boostpower = READFIXED(save_p); players[i].speedboost = READFIXED(save_p); diff --git a/src/p_slopes.c b/src/p_slopes.c index 3c124a550..6c749506b 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -894,6 +894,40 @@ fixed_t P_GetLightZAt(const lightlist_t *light, fixed_t x, fixed_t y) return light->slope ? P_GetSlopeZAt(light->slope, x, y) : light->height; } +// Returns true if we should run slope physics code on an object. +boolean P_CanApplySlopePhysics(mobj_t *mo, pslope_t *slope) +{ + if (slope == NULL || mo == NULL || P_MobjWasRemoved(mo) == true) + { + // Invalid input. + return false; + } + + if (slope->flags & SL_NOPHYSICS) + { + // Physics are turned off. + return false; + } + + if (slope->normal.x == 0 && slope->normal.y == 0) + { + // Flat slope? No such thing, man. No such thing. + return false; + } + + if (mo->player != NULL) + { + if (K_PlayerEBrake(mo->player) == true) + { + // Spindash negates slopes. + return false; + } + } + + // We can do slope physics. + return true; +} + // // P_QuantizeMomentumToSlope // @@ -923,19 +957,13 @@ void P_ReverseQuantizeMomentumToSlope(vector3_t *momentum, pslope_t *slope) slope->zangle = InvAngle(slope->zangle); } -// SRB2Kart: This fixes all slope-based jumps for different scales in Kart automatically without map tweaking. -// However, they will always feel off every single time... see for yourself: https://cdn.discordapp.com/attachments/270211093761097728/484924392128774165/kart0181.gif -//#define GROWNEVERMISSES - // // P_SlopeLaunch // // Handles slope ejection for objects void P_SlopeLaunch(mobj_t *mo) { - if (!(mo->standingslope->flags & SL_NOPHYSICS) // If there's physics, time for launching. - && (mo->standingslope->normal.x != 0 - || mo->standingslope->normal.y != 0)) + if (P_CanApplySlopePhysics(mo, mo->standingslope) == true) // If there's physics, time for launching. { // Double the pre-rotation Z, then halve the post-rotation Z. This reduces the // vertical launch given from slopes while increasing the horizontal launch @@ -946,19 +974,9 @@ void P_SlopeLaunch(mobj_t *mo) slopemom.z = mo->momz; P_QuantizeMomentumToSlope(&slopemom, mo->standingslope); -#ifdef GROWNEVERMISSES - { - const fixed_t xyscale = mapobjectscale + (mapobjectscale - mo->scale); - const fixed_t zscale = mapobjectscale + (mapobjectscale - mo->scale); - mo->momx = FixedMul(slopemom.x, xyscale); - mo->momy = FixedMul(slopemom.y, xyscale); - mo->momz = FixedMul(slopemom.z, zscale); - } -#else mo->momx = slopemom.x; mo->momy = slopemom.y; mo->momz = slopemom.z; -#endif mo->eflags |= MFE_SLOPELAUNCHED; } @@ -984,14 +1002,19 @@ fixed_t P_GetWallTransferMomZ(mobj_t *mo, pslope_t *slope) vector3_t slopemom, axis; angle_t ang; - if (mo->standingslope->flags & SL_NOPHYSICS) - return 0; + if (P_CanApplySlopePhysics(mo, mo->standingslope) == false) + { + return false; + } // If there's physics, time for launching. // Doesn't kill the vertical momentum as much as P_SlopeLaunch does. ang = slope->zangle + ANG15*((slope->zangle > 0) ? 1 : -1); if (ang > ANGLE_90 && ang < ANGLE_180) - ang = ((slope->zangle > 0) ? ANGLE_90 : InvAngle(ANGLE_90)); // hard cap of directly upwards + { + // hard cap of directly upwards + ang = ((slope->zangle > 0) ? ANGLE_90 : InvAngle(ANGLE_90)); + } slopemom.x = mo->momx; slopemom.y = mo->momy; @@ -1010,13 +1033,16 @@ fixed_t P_GetWallTransferMomZ(mobj_t *mo, pslope_t *slope) void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope) { vector3_t mom; // Ditto. - if (slope->flags & SL_NOPHYSICS || (slope->normal.x == 0 && slope->normal.y == 0)) { // No physics, no need to make anything complicated. + + if (P_CanApplySlopePhysics(thing, slope) == false) // No physics, no need to make anything complicated. + { if (P_MobjFlip(thing)*(thing->momz) < 0) // falling, land on slope { thing->standingslope = slope; P_SetPitchRollFromSlope(thing, slope); thing->momz = -P_MobjFlip(thing); } + return; } @@ -1026,7 +1052,9 @@ void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope) P_ReverseQuantizeMomentumToSlope(&mom, slope); - if (P_MobjFlip(thing)*mom.z < 0) { // falling, land on slope + if (P_MobjFlip(thing)*mom.z < 0) + { + // falling, land on slope thing->momx = mom.x; thing->momy = mom.y; thing->standingslope = slope; @@ -1041,27 +1069,29 @@ void P_ButteredSlope(mobj_t *mo) { fixed_t thrust; - if (!mo->standingslope) - return; - - if (mo->standingslope->flags & SL_NOPHYSICS) - return; // No physics, no butter. - if (mo->flags & (MF_NOCLIPHEIGHT|MF_NOGRAVITY)) return; // don't slide down slopes if you can't touch them or you're not affected by gravity - if (mo->player) { - // SRB2Kart - spindash negates slopes - if (K_PlayerEBrake(mo->player)) - return; + if (P_CanApplySlopePhysics(mo, mo->standingslope) == false) + return; // No physics, no butter. - // Changed in kart to only not apply physics on very slight slopes (I think about 4 degree angles) + if (mo->player != NULL) + { if (abs(mo->standingslope->zdelta) < FRACUNIT/21) - return; // Don't slide on non-steep slopes + { + // Don't slide on non-steep slopes. + // Changed in Ring Racers to only not apply physics on very slight slopes. + // (I think about 4 degree angles.) + return; + } - // This only means you can be stopped on slopes that aren't steeper than 45 degrees - if (abs(mo->standingslope->zdelta) < FRACUNIT/2 && !(mo->player->rmomx || mo->player->rmomy)) - return; // Allow the player to stand still on slopes below a certain steepness + if (abs(mo->standingslope->zdelta) < FRACUNIT/2 + && !(mo->player->rmomx || mo->player->rmomy)) + { + // Allow the player to stand still on slopes below a certain steepness. + // 45 degree angle steep, to be exact. + return; + } } thrust = FINESINE(mo->standingslope->zangle>>ANGLETOFINESHIFT) * 5 / 4 * (mo->eflags & MFE_VERTICALFLIP ? 1 : -1); diff --git a/src/p_slopes.h b/src/p_slopes.h index a72ca1776..6f9764fa9 100644 --- a/src/p_slopes.h +++ b/src/p_slopes.h @@ -82,6 +82,7 @@ fixed_t P_GetFFloorBottomZAt(const ffloor_t *ffloor, fixed_t x, fixed_t y); fixed_t P_GetLightZAt(const lightlist_t *light, fixed_t x, fixed_t y); // Lots of physics-based bullshit +boolean P_CanApplySlopePhysics(mobj_t *mo, pslope_t *slope); void P_QuantizeMomentumToSlope(vector3_t *momentum, pslope_t *slope); void P_ReverseQuantizeMomentumToSlope(vector3_t *momentum, pslope_t *slope); void P_SlopeLaunch(mobj_t *mo); diff --git a/src/p_user.c b/src/p_user.c index 4030232f7..b277dacff 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1905,17 +1905,27 @@ static void P_3dMovement(player_t *player) } if ((totalthrust.x || totalthrust.y) - && player->mo->standingslope && (!(player->mo->standingslope->flags & SL_NOPHYSICS)) && abs(player->mo->standingslope->zdelta) > FRACUNIT/2) { + && player->mo->standingslope != NULL + && (!(player->mo->standingslope->flags & SL_NOPHYSICS)) + && abs(player->mo->standingslope->zdelta) > FRACUNIT/2) + { // Factor thrust to slope, but only for the part pushing up it! // The rest is unaffected. - angle_t thrustangle = R_PointToAngle2(0, 0, totalthrust.x, totalthrust.y)-player->mo->standingslope->xydirection; + angle_t thrustangle = R_PointToAngle2(0, 0, totalthrust.x, totalthrust.y) - player->mo->standingslope->xydirection; - if (player->mo->standingslope->zdelta < 0) { // Direction goes down, so thrustangle needs to face toward - if (thrustangle < ANGLE_90 || thrustangle > ANGLE_270) { + if (player->mo->standingslope->zdelta < 0) + { + // Direction goes down, so thrustangle needs to face toward + if (thrustangle < ANGLE_90 || thrustangle > ANGLE_270) + { P_QuantizeMomentumToSlope(&totalthrust, player->mo->standingslope); } - } else { // Direction goes up, so thrustangle needs to face away - if (thrustangle > ANGLE_90 && thrustangle < ANGLE_270) { + } + else + { + // Direction goes up, so thrustangle needs to face away + if (thrustangle > ANGLE_90 && thrustangle < ANGLE_270) + { P_QuantizeMomentumToSlope(&totalthrust, player->mo->standingslope); } } diff --git a/src/sounds.c b/src/sounds.c index 7cbc0e8c0..9240577fa 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -1110,7 +1110,9 @@ sfxinfo_t S_sfx[NUMSFX] = {"kdtrg3", false, 64, 80, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // High energy, SF_X2AWAYSOUND|SF_X8AWAYSOUND // SRB2kart - Grow/invinc clash - {"parry", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // SF_X8AWAYSOUND + {"parry", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // SF_X8AWAYSOUND + + {"ffbonc", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // SRB2Kart - Engine sounds // Engine class A diff --git a/src/sounds.h b/src/sounds.h index 6d1c51f57..c5eb638e0 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -1176,6 +1176,9 @@ typedef enum // SRB2Kart - Powerup clash SFX sfx_parry, + // Fast fall bounce + sfx_ffbonc, + // Next up: UNIQUE ENGINE SOUNDS! Hoooooo boy... // Engine class A - Low Speed, Low Weight sfx_krta00, From 50314d21cbe682d1b7811c7b5f0353f8d15bd500 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 5 Sep 2022 13:53:52 +0100 Subject: [PATCH 364/379] Since B can be used as a go back button as well, add it to the tooltip. --- src/k_menudef.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_menudef.c b/src/k_menudef.c index b7a63da36..ed1e33c42 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -600,7 +600,7 @@ menuitem_t OPTIONS_ProfileControls[] = { {IT_CONTROL, "A", "Accelerate / Confirm", "PR_BTA", {.routine = M_ProfileSetControl}, gc_a, 0}, - {IT_CONTROL, "B", "Look backwards", + {IT_CONTROL, "B", "Look backwards / Back", "PR_BTB", {.routine = M_ProfileSetControl}, gc_b, 0}, {IT_CONTROL, "C", "Spindash / Extra", From bca3ec7e5a91a7d1fe8e7e6204b27fee87e1388c Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 5 Sep 2022 14:00:26 +0100 Subject: [PATCH 365/379] Change the default highligh to Aqua, for consistency. --- src/k_menu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_menu.h b/src/k_menu.h index 53afa40e6..415c54284 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -1001,7 +1001,7 @@ void M_HandleImageDef(INT32 choice); // K_MENUDRAW.C // flags for text highlights -#define highlightflags V_ORANGEMAP +#define highlightflags V_AQUAMAP #define recommendedflags V_GREENMAP #define warningflags V_GRAYMAP From f07a65d01560b9399c5e9fc31d0d9c6f21435af2 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 5 Sep 2022 14:46:46 +0100 Subject: [PATCH 366/379] Adjust x coordinate of "FREE PLAY" Broken by new font, made sense to change it where that new font was most relevant --- src/k_hud.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_hud.c b/src/k_hud.c index e5d5d0737..9d9c4a418 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -4471,7 +4471,7 @@ void K_drawKartFreePlay(void) if (((leveltime-lt_endtime) % TICRATE) < TICRATE/2) return; - V_DrawKartString((BASEVIDWIDTH - (LAPS_X+1)) - (12*9), // mirror the laps thingy + V_DrawKartString((BASEVIDWIDTH - (LAPS_X+1)) - 72, // mirror the laps thingy LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT, "FREE PLAY"); } From 1eb1309032a37f584a3fd2f93a47ec2bf1e40c63 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 5 Sep 2022 10:41:06 -0400 Subject: [PATCH 367/379] Remove step-down change --- src/p_map.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/p_map.c b/src/p_map.c index 0bf029ab2..6f3b9f9fe 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -2463,11 +2463,6 @@ fixed_t P_GetThingStepUp(mobj_t *thing) const fixed_t maxstepmove = P_BaseStepUp(); fixed_t maxstep = maxstepmove; - if (thing->player && thing->player->fastfall != 0) - { - return 0; - } - if (P_WaterStepUp(thing) == true) { // Add some extra stepmove when waterskipping @@ -2524,16 +2519,20 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) #endif do { - if (thing->flags & MF_NOCLIP) { + if (thing->flags & MF_NOCLIP) + { tryx = x; tryy = y; - } else { + } + else + { if (x-tryx > radius) tryx += radius; else if (x-tryx < -radius) tryx -= radius; else tryx = x; + if (y-tryy > radius) tryy += radius; else if (y-tryy < -radius) From eb7e2287c4ef8209936974e427716df48aee0e6f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 5 Sep 2022 10:54:16 -0400 Subject: [PATCH 368/379] Change bounce behavior - Always allow bounce after fast fall - Allow for more bounces - Lose speed on bounces that don't reach max --- src/k_kart.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index b31556fc4..77ec7c20a 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9311,20 +9311,28 @@ static void K_KartSpindash(player_t *player) { // Handle fastfall bounce. const fixed_t maxBounce = player->mo->scale * 10; - const fixed_t minBounce = player->mo->scale * 2; - fixed_t bounce = abs(player->fastfall) / 4; + const fixed_t minBounce = player->mo->scale; + fixed_t bounce = 2 * abs(player->fastfall) / 3; if (bounce > maxBounce) { bounce = maxBounce; } - - if (bounce > minBounce) + else { - S_StartSound(player->mo, sfx_ffbonc); - player->mo->momz = bounce * P_MobjFlip(player->mo); + // Lose speed on bad bounce. + player->mo->momx /= 2; + player->mo->momy /= 2; + + if (bounce < minBounce) + { + bounce = minBounce; + } } + S_StartSound(player->mo, sfx_ffbonc); + player->mo->momz = bounce * P_MobjFlip(player->mo); + player->fastfall = 0; return; } From 38dc57269992a23d180d0121fc1da97152332da4 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 5 Sep 2022 10:58:50 -0400 Subject: [PATCH 369/379] Reset fast falling in P_ResetPlayer Resets it for springs --- src/p_user.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/p_user.c b/src/p_user.c index b277dacff..215f67ffd 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -473,6 +473,7 @@ void P_ResetPlayer(player_t *player) //player->drift = player->driftcharge = 0; player->trickpanel = 0; player->glanceDir = 0; + player->fastfall = 0; } // From 98ec1779d95a9897b06254e24e2ad0581c921489 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 5 Sep 2022 11:30:12 -0400 Subject: [PATCH 370/379] Reimplement follower hit confirm --- src/k_kart.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index 52ac8c12c..92a83902d 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2883,6 +2883,12 @@ void K_TryHurtSoundExchange(mobj_t *victim, mobj_t *attacker) attacker->player->confirmVictim = (victim->player - players); attacker->player->confirmVictimDelay = TICRATE/2; + + if (attacker->player->follower != NULL) + { + const follower_t *fl = &followers[attacker->player->followerskin]; + attacker->player->follower->movecount = fl->hitconfirmtime; // movecount is used to play the hitconfirm animation for followers. + } } void K_PlayPowerGloatSound(mobj_t *source) From 7a91e5b6b596f062784c63703d1c37514ccbea12 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 5 Sep 2022 11:56:11 -0400 Subject: [PATCH 371/379] Don't allow ebrake in tumble --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 77ec7c20a..e6df044ac 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9032,7 +9032,7 @@ boolean K_PlayerEBrake(player_t *player) return (K_GetKartButtons(player) & BT_EBRAKEMASK) == BT_EBRAKEMASK && player->drift == 0 - && player->spinouttimer == 0 + && P_PlayerInPain(player) == false && player->justbumped == 0 && player->spindashboost == 0 && player->nocontrol == 0; From 3149d7738f76b1f5de99492b38ad702ba5bf8fd1 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 7 Sep 2022 01:31:48 -0400 Subject: [PATCH 372/379] Revert "vsync is not broken anymore thanks to both Eidolon and jart's efforts" This reverts commit 59044e73896d46bf2242a243b69a282cb394a5ea. --- src/sdl/i_video.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 3a5c1d3ab..6ea86037c 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1583,8 +1583,15 @@ static SDL_bool Impl_CreateContext(void) int flags = 0; // Use this to set SDL_RENDERER_* flags now if (usesdl2soft) flags |= SDL_RENDERER_SOFTWARE; +#if 0 + // This shit is BROKEN. + // - The version of SDL we're using cannot toggle VSync at runtime. We'll need a new SDL version implemented to have this work properly. + // - cv_vidwait is initialized before config is loaded, so it's forced to default value at runtime, and forced off when switching. The config loading code would need restructured. + // - With both this & frame interpolation on, I_FinishUpdate takes x10 longer. At this point, it is simpler to use a standard FPS cap. + // So you can probably guess why I'm kinda over this, I'm just disabling it. else if (cv_vidwait.value) flags |= SDL_RENDERER_PRESENTVSYNC; +#endif // 3 August 2022 // Possibly a Windows 11 issue; the default From 813751d9c09d4eb7cdfb7f6155881bc2ddc928a5 Mon Sep 17 00:00:00 2001 From: VelocitOni Date: Wed, 7 Sep 2022 03:45:20 -0400 Subject: [PATCH 373/379] Consistent color scale Driftsparks now go through grey, yellow, red, and blue; instead of yellow, red, blue, purple. This is consistent with every other scale of power in the game. --- src/k_kart.c | 59 ++++++++++++++++++++++++---------------------------- src/p_mobj.c | 18 ++++++++-------- 2 files changed, 36 insertions(+), 41 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 531c66830..9d92e8118 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4297,8 +4297,8 @@ UINT16 K_DriftSparkColor(player_t *player, INT32 charge) if (charge < 0) { - // Stage 0: Yellow - color = SKINCOLOR_GOLD; + // Stage 0: Grey + color = SKINCOLOR_SILVER; } else if (charge >= dsfour) { @@ -4315,7 +4315,7 @@ UINT16 K_DriftSparkColor(player_t *player, INT32 charge) } else if (charge >= dsthree) { - // Stage 3: Purple + // Stage 3: Blue if (charge <= dsthree+(16*3)) { // transition 1 @@ -4324,19 +4324,6 @@ UINT16 K_DriftSparkColor(player_t *player, INT32 charge) else if (charge <= dsthree+(32*3)) { // transition 2 - color = SKINCOLOR_MOONSET; - } - else - { - color = SKINCOLOR_PURPLE; - } - } - else if (charge >= dstwo) - { - // Stage 2: Blue - if (charge <= dstwo+(32*3)) - { - // transition color = SKINCOLOR_NOVA; } else @@ -4344,10 +4331,10 @@ UINT16 K_DriftSparkColor(player_t *player, INT32 charge) color = SKINCOLOR_SAPPHIRE; } } - else if (charge >= dsone) + else if (charge >= dstwo) { - // Stage 1: Red - if (charge <= dsone+(32*3)) + // Stage 2: Red + if (charge <= dstwo+(32*3)) { // transition color = SKINCOLOR_TANGERINE; @@ -4357,6 +4344,19 @@ UINT16 K_DriftSparkColor(player_t *player, INT32 charge) color = SKINCOLOR_KETCHUP; } } + else if (charge >= dsone) + { + // Stage 1: Yellow + if (charge <= dsone+(32*3)) + { + // transition + color = SKINCOLOR_TAN; + } + else + { + color = SKINCOLOR_GOLD; + } + } return color; } @@ -8543,9 +8543,9 @@ INT32 K_GetKartDriftSparkValueForStage(player_t *player, UINT8 stage) } /* -Stage 1: red sparks -Stage 2: blue sparks -Stage 3: purple sparks +Stage 1: yellow sparks +Stage 2: red sparks +Stage 3: blue sparks Stage 4: big large rainbow sparks Stage 0: air failsafe */ @@ -8561,26 +8561,22 @@ void K_SpawnDriftBoostExplosion(player_t *player, int stage) switch (stage) { case 1: - overlay->color = SKINCOLOR_KETCHUP; overlay->fuse = 16; break; case 2: - overlay->color = SKINCOLOR_SAPPHIRE; + overlay->fuse = 32; - S_StartSound(player->mo, sfx_kc5b); break; case 3: - overlay->color = SKINCOLOR_PURPLE; overlay->fuse = 48; S_StartSound(player->mo, sfx_kc5b); break; case 4: - overlay->color = SKINCOLOR_SILVER; overlay->fuse = 120; S_StartSound(player->mo, sfx_kc5b); @@ -8588,7 +8584,6 @@ void K_SpawnDriftBoostExplosion(player_t *player, int stage) break; case 0: - overlay->color = SKINCOLOR_SILVER; overlay->fuse = 16; break; } @@ -8623,7 +8618,7 @@ static void K_KartDrift(player_t *player, boolean onground) if (player->driftcharge < 0) { - // Stage 0: Yellow sparks + // Stage 0: Grey sparks if (!onground) P_Thrust(player->mo, pushdir, player->speed / 8); @@ -8632,7 +8627,7 @@ static void K_KartDrift(player_t *player, boolean onground) } else if (player->driftcharge >= dsone && player->driftcharge < dstwo) { - // Stage 1: Red sparks + // Stage 1: Yellow sparks if (!onground) P_Thrust(player->mo, pushdir, player->speed / 4); @@ -8643,7 +8638,7 @@ static void K_KartDrift(player_t *player, boolean onground) } else if (player->driftcharge < dsthree) { - // Stage 2: Blue sparks + // Stage 2: Red sparks if (!onground) P_Thrust(player->mo, pushdir, player->speed / 3); @@ -8654,7 +8649,7 @@ static void K_KartDrift(player_t *player, boolean onground) } else if (player->driftcharge < dsfour) { - // Stage 3: Purple sparks + // Stage 3: Blue sparks if (!onground) P_Thrust(player->mo, pushdir, ( 5 * player->speed ) / 12); diff --git a/src/p_mobj.c b/src/p_mobj.c index 7f9fee385..d85645b07 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -7125,17 +7125,17 @@ static boolean P_MobjRegularThink(mobj_t *mobj) if (mobj->fuse <= 16) { - mobj->color = SKINCOLOR_KETCHUP; + mobj->color = SKINCOLOR_GOLD; /* don't draw papersprite frames after blue boost */ mobj->renderflags ^= RF_DONTDRAW; } else if (mobj->fuse <= 32) - mobj->color = SKINCOLOR_SAPPHIRE; + mobj->color = SKINCOLOR_KETCHUP; else if (mobj->fuse <= 48) - mobj->color = SKINCOLOR_PURPLE; + mobj->color = SKINCOLOR_SAPPHIRE; else if (mobj->fuse > 48) mobj->color = K_RainbowColor( - (SKINCOLOR_PURPLE - SKINCOLOR_PINK) // Smoothly transition into the other state + (SKINCOLOR_SAPPHIRE - SKINCOLOR_PINK) // Smoothly transition into the other state + ((mobj->fuse - 32) * 2) // Make the color flashing slow down while it runs out ); @@ -7150,14 +7150,14 @@ static boolean P_MobjRegularThink(mobj_t *mobj) } break; - case 3:/* purple boost */ - if ((mobj->fuse == 32)/* to blue*/ - || (mobj->fuse == 16))/* to red*/ + case 3:/* blue boost */ + if ((mobj->fuse == 32)/* to red*/ + || (mobj->fuse == 16))/* to yellow*/ K_SpawnDriftBoostClip(mobj->target->player); break; - case 2:/* blue boost */ - if (mobj->fuse == 16)/* to red*/ + case 2:/* red boost */ + if (mobj->fuse == 16)/* to yellow*/ K_SpawnDriftBoostClip(mobj->target->player); break; From a5043b5be8a82f1794ba206257c42b1493f3fcdc Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 7 Sep 2022 22:08:12 +0100 Subject: [PATCH 374/379] Fixed Oni's controller-related SIGFPE --- src/sdl/i_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index f639826c2..cc2db94a6 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -543,7 +543,7 @@ static INT32 SDLJoyAxis(const Sint16 axis, UINT8 pid) } else { - raxis = (JoyInfo[pid].scale != 1) ? ((raxis / JoyInfo[pid].scale) * JoyInfo[pid].scale) : raxis; + raxis = (abs(JoyInfo[pid].scale) > 1) ? ((raxis / JoyInfo[pid].scale) * JoyInfo[pid].scale) : raxis; #ifdef SDL_JDEADZONE if (-SDL_JDEADZONE <= raxis && raxis <= SDL_JDEADZONE) From badbcb5c8d808577555aca73e4a00a97afd29bc2 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 7 Sep 2022 14:12:20 -0700 Subject: [PATCH 375/379] Fix -Wformat --- src/k_menudraw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index cc8d65c95..76a001544 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -4371,7 +4371,7 @@ void M_DrawAddons(void) m = numwadfiles-(mainwads+2+1); - V_DrawCenteredString(BASEVIDWIDTH/2, y+4, (majormods ? highlightflags : V_TRANSLUCENT), va("%d ADD-ON%s LOADED", m, (m == 1) ? "" : "S")); //+2 for music, sounds, +1 for main.kart + V_DrawCenteredString(BASEVIDWIDTH/2, y+4, (majormods ? highlightflags : V_TRANSLUCENT), va("%ld ADD-ON%s LOADED", (long)m, (m == 1) ? "" : "S")); //+2 for music, sounds, +1 for main.kart } #undef addonsseperation From f6fd8f672853ac863d39a7f4c333836e25fc0b9b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 8 Sep 2022 12:51:47 -0400 Subject: [PATCH 376/379] OpenGL instead of Direct3D11 for Software blitter --- src/sdl/i_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 6ea86037c..488cfb5b7 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1598,7 +1598,7 @@ static SDL_bool Impl_CreateContext(void) // "direct3d" driver (D3D9) causes Drmingw exchndl // to not write RPT files. Every other driver // seems fine. - SDL_SetHint(SDL_HINT_RENDER_DRIVER, "direct3d11"); + SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); if (!renderer) renderer = SDL_CreateRenderer(window, -1, flags); From 5b42b3b41abb62903c0788af3984d0720b415c20 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 9 Sep 2022 13:49:57 -0400 Subject: [PATCH 377/379] Backport mobj interpolators crash fix From commit f009b3d8c9d2273bf2e51ef9a23fbfedc2da09df in the public repo. Porting directly into master because I'm tired of kartdebugwaypoints crashing all the time. --- src/r_fps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/r_fps.c b/src/r_fps.c index 32d24ba10..2c01cc571 100644 --- a/src/r_fps.c +++ b/src/r_fps.c @@ -709,7 +709,7 @@ void R_RemoveMobjInterpolator(mobj_t *mobj) if (interpolated_mobjs_len == 0) return; - for (i = 0; i < interpolated_mobjs_len - 1; i++) + for (i = 0; i < interpolated_mobjs_len; i++) { if (interpolated_mobjs[i] == mobj) { From efc415e53694edf2342e3fcb7a7b0c2da873d0b0 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 11 Nov 2019 00:24:06 +0100 Subject: [PATCH 378/379] fix orbiting items not protecting you against your own items (cherry picked from commit 705851b38a5ab7cfbd810062cfb6b2dd921a6a1e) --- src/k_collide.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_collide.c b/src/k_collide.c index 128364cfc..a23801ccc 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -44,7 +44,7 @@ boolean K_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2) if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0)) return true; - if (((t1->target == t2) || (!(t2->flags & (MF_ENEMY|MF_BOSS)) && (t1->target == t2->target))) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0))) + if (((t1->target == t2) || (!(t2->flags & (MF_ENEMY|MF_BOSS)) && (t1->target == t2->target))) && ((t1->threshold > 0 && t2->type == MT_PLAYER) || (t2->type != MT_PLAYER && t2->threshold > 0))) return true; if (t1->health <= 0 || t2->health <= 0) From 79f6d0643bc3fba812d8769d8e0b01f8567ec944 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 10 Sep 2022 14:46:12 -0400 Subject: [PATCH 379/379] Launch / step-down changes - Launch / wall-transfer off of no-physics slopes - Prevent step down when trying to move upwards - E-brake enables launching again - Removed old comment talking about slope nerf --- src/p_map.c | 3 ++- src/p_mobj.c | 3 +-- src/p_slopes.c | 28 +++++++++++++++++++++++----- src/p_slopes.h | 1 + 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/p_map.c b/src/p_map.c index 6f3b9f9fe..2c21a7718 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -2601,7 +2601,8 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) return false; // mobj must lower itself to fit } } - else if (!(P_MobjTouchingSectorSpecial(thing, 1, 14, false))) // Step down + else if (thing->momz * P_MobjFlip(thing) <= 0 // Step down requires moving down. + && !(P_MobjTouchingSectorSpecial(thing, 1, 14, false))) { // If the floor difference is MAXSTEPMOVE or less, and the sector isn't Section1:14, ALWAYS // step down! Formerly required a Section1:13 sector for the full MAXSTEPMOVE, but no more. diff --git a/src/p_mobj.c b/src/p_mobj.c index 621da9d9f..b12430889 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1800,8 +1800,7 @@ void P_XYMovement(mobj_t *mo) */ } - else if (predictedz - mo->z > abs(slopemom.z/2) - && P_CanApplySlopePhysics(mo, mo->standingslope) == true) // Sryder 2018-11-26: Don't launch here if it's a slope without physics, we stick to those like glue anyway + else if (predictedz - mo->z > abs(slopemom.z / 2)) { // Now check if we were supposed to stick to this slope //CONS_Printf("%d-%d > %d\n", (predictedz), (mo->z), (slopemom.z/2)); diff --git a/src/p_slopes.c b/src/p_slopes.c index 6c749506b..1347a8759 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -928,6 +928,27 @@ boolean P_CanApplySlopePhysics(mobj_t *mo, pslope_t *slope) return true; } +// Returns true if we should run slope launch code on an object. +boolean P_CanApplySlopeLaunch(mobj_t *mo, pslope_t *slope) +{ + if (slope == NULL || mo == NULL || P_MobjWasRemoved(mo) == true) + { + // Invalid input. + return false; + } + + // No physics slopes are fine to launch off of. + + if (slope->normal.x == 0 && slope->normal.y == 0) + { + // Flat slope? No such thing, man. No such thing. + return false; + } + + // We can do slope launching. + return true; +} + // // P_QuantizeMomentumToSlope // @@ -963,11 +984,8 @@ void P_ReverseQuantizeMomentumToSlope(vector3_t *momentum, pslope_t *slope) // Handles slope ejection for objects void P_SlopeLaunch(mobj_t *mo) { - if (P_CanApplySlopePhysics(mo, mo->standingslope) == true) // If there's physics, time for launching. + if (P_CanApplySlopeLaunch(mo, mo->standingslope) == true) // If there's physics, time for launching. { - // Double the pre-rotation Z, then halve the post-rotation Z. This reduces the - // vertical launch given from slopes while increasing the horizontal launch - // given. Good for SRB2's gravity and horizontal speeds. vector3_t slopemom; slopemom.x = mo->momx; slopemom.y = mo->momy; @@ -1002,7 +1020,7 @@ fixed_t P_GetWallTransferMomZ(mobj_t *mo, pslope_t *slope) vector3_t slopemom, axis; angle_t ang; - if (P_CanApplySlopePhysics(mo, mo->standingslope) == false) + if (P_CanApplySlopeLaunch(mo, mo->standingslope) == false) { return false; } diff --git a/src/p_slopes.h b/src/p_slopes.h index 6f9764fa9..5eb4f83bb 100644 --- a/src/p_slopes.h +++ b/src/p_slopes.h @@ -83,6 +83,7 @@ fixed_t P_GetLightZAt(const lightlist_t *light, fixed_t x, fixed_t y); // Lots of physics-based bullshit boolean P_CanApplySlopePhysics(mobj_t *mo, pslope_t *slope); +boolean P_CanApplySlopeLaunch(mobj_t *mo, pslope_t *slope); void P_QuantizeMomentumToSlope(vector3_t *momentum, pslope_t *slope); void P_ReverseQuantizeMomentumToSlope(vector3_t *momentum, pslope_t *slope); void P_SlopeLaunch(mobj_t *mo);