mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Move all specialized code out of k_menufunc.c
Adds new files: - menus/extras-statistics.c - menus/play-online-room-select.c - menus/transient/cup-select.c - menus/transient/explosions.c - menus/transient/gametype.c - menus/transient/message-box.c - menus/transient/virtual-keyboard.c
This commit is contained in:
parent
d5302d9e5d
commit
b26cd786ec
39 changed files with 6889 additions and 6837 deletions
20
src/k_menu.h
20
src/k_menu.h
|
|
@ -556,6 +556,7 @@ extern boolean menuwipe;
|
|||
|
||||
extern consvar_t cv_showfocuslost;
|
||||
extern consvar_t cv_chooseskin, cv_serversort, cv_menujam_update;
|
||||
extern consvar_t cv_autorecord;
|
||||
|
||||
void M_SetMenuDelay(UINT8 i);
|
||||
|
||||
|
|
@ -569,6 +570,17 @@ 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);
|
||||
|
||||
boolean M_ChangeStringCvar(INT32 choice);
|
||||
|
||||
boolean M_NextOpt(void);
|
||||
boolean M_PrevOpt(void);
|
||||
|
||||
boolean M_MenuConfirmPressed(UINT8 pid);
|
||||
boolean M_MenuBackPressed(UINT8 pid);
|
||||
boolean M_MenuExtraPressed(UINT8 pid);
|
||||
boolean M_MenuExtraHeld(UINT8 pid);
|
||||
|
||||
void M_StartControlPanel(void);
|
||||
void M_ClearMenus(boolean callexitmenufunc);
|
||||
void M_SelectableClearMenus(INT32 choice);
|
||||
|
|
@ -577,6 +589,8 @@ void M_GoBack(INT32 choice);
|
|||
void M_Ticker(void);
|
||||
void M_Init(void);
|
||||
|
||||
void M_MenuTypingInput(INT32 key);
|
||||
|
||||
extern menu_t MessageDef;
|
||||
void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtype);
|
||||
void M_StopMessage(INT32 choice);
|
||||
|
|
@ -679,6 +693,7 @@ extern consvar_t *setup_playercvars[MAXSPLITSCREENPLAYERS][SPLITCV_MAX];
|
|||
|
||||
void M_CharacterSelectInit(void);
|
||||
void M_CharacterSelect(INT32 choice);
|
||||
void M_SetupReadyExplosions(boolean charsel, UINT16 basex, UINT16 basey, UINT16 color);
|
||||
boolean M_CharacterSelectHandler(INT32 choice);
|
||||
void M_CharacterSelectTick(void);
|
||||
boolean M_CharacterSelectQuit(void);
|
||||
|
|
@ -723,6 +738,8 @@ boolean M_CanShowLevelInList(INT16 mapnum, levelsearch_t *levelsearch);
|
|||
UINT16 M_CountLevelsToShowInList(levelsearch_t *levelsearch);
|
||||
UINT16 M_GetFirstLevelInList(UINT8 *i, levelsearch_t *levelsearch);
|
||||
UINT16 M_GetNextLevelInList(UINT16 mapnum, UINT8 *i, levelsearch_t *levelsearch);
|
||||
void M_LevelSelectScrollDest(void);
|
||||
boolean M_LevelListFromGametype(INT16 gt);
|
||||
|
||||
void M_LevelSelectInit(INT32 choice);
|
||||
void M_CupSelectHandler(INT32 choice);
|
||||
|
|
@ -730,6 +747,8 @@ void M_CupSelectTick(void);
|
|||
void M_LevelSelectHandler(INT32 choice);
|
||||
void M_LevelSelectTick(void);
|
||||
|
||||
void M_LevelSelected(INT16 add);
|
||||
|
||||
// dummy consvars for GP & match race setup
|
||||
extern consvar_t cv_dummygpdifficulty;
|
||||
extern consvar_t cv_dummykartspeed;
|
||||
|
|
@ -916,6 +935,7 @@ void M_CheckProfileData(INT32 choice); // check if we have profiles.
|
|||
|
||||
// profile selection menu
|
||||
void M_ProfileSelectInit(INT32 choice);
|
||||
void M_FirstPickProfile(INT32 c);
|
||||
void M_HandleProfileSelect(INT32 ch);
|
||||
|
||||
// profile edition
|
||||
|
|
|
|||
6801
src/k_menufunc.c
6801
src/k_menufunc.c
File diff suppressed because it is too large
Load diff
|
|
@ -3,6 +3,7 @@ target_sources(SRB2SDL2 PRIVATE
|
|||
extras-addons.c
|
||||
extras-challenges.c
|
||||
extras-replay-hut.c
|
||||
extras-statistics.c
|
||||
main-1.c
|
||||
main-profile-select.c
|
||||
options-1.c
|
||||
|
|
@ -35,6 +36,7 @@ target_sources(SRB2SDL2 PRIVATE
|
|||
play-online-1.c
|
||||
play-online-host.c
|
||||
play-online-join-ip.c
|
||||
play-online-room-select.c
|
||||
play-online-server-browser.c
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
/// \brief Extras Menu
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../s_sound.h"
|
||||
|
||||
menuitem_t EXTRAS_Main[] =
|
||||
{
|
||||
|
|
@ -35,3 +36,104 @@ menu_t EXTRAS_MainDef = {
|
|||
M_ExtrasInputs
|
||||
};
|
||||
|
||||
// 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)
|
||||
{
|
||||
|
||||
const UINT8 pid = 0;
|
||||
(void) ch;
|
||||
|
||||
if (menucmd[pid].dpad_ud > 0)
|
||||
{
|
||||
extrasmenu.offset += 48;
|
||||
M_NextOpt();
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
|
||||
if (itemOn == 0)
|
||||
extrasmenu.offset -= currentMenu->numitems*48;
|
||||
|
||||
M_SetMenuDelay(pid);
|
||||
return true;
|
||||
}
|
||||
|
||||
else if (menucmd[pid].dpad_ud < 0)
|
||||
{
|
||||
extrasmenu.offset -= 48;
|
||||
M_PrevOpt();
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
|
||||
if (itemOn == currentMenu->numitems-1)
|
||||
extrasmenu.offset += currentMenu->numitems*48;
|
||||
|
||||
M_SetMenuDelay(pid);
|
||||
return true;
|
||||
}
|
||||
|
||||
else if (M_MenuConfirmPressed(pid))
|
||||
{
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
/// \brief Addons menu!
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../filesrch.h" // Addfile
|
||||
#include "../d_main.h"
|
||||
#include "../z_zone.h"
|
||||
#include "../s_sound.h"
|
||||
#include "../v_video.h"
|
||||
|
||||
menuitem_t MISC_AddonsMenu[] =
|
||||
{
|
||||
|
|
@ -25,3 +30,330 @@ menu_t MISC_AddonsDef = {
|
|||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
// Addons menu: (Merely copypasted, original code by toaster)
|
||||
|
||||
static void M_UpdateAddonsSearch(void);
|
||||
consvar_t cv_dummyaddonsearch = CVAR_INIT ("dummyaddonsearch", "", CV_HIDDEN|CV_CALL|CV_NOINIT, NULL, M_UpdateAddonsSearch);
|
||||
|
||||
void M_Addons(INT32 choice)
|
||||
{
|
||||
const char *pathname = ".";
|
||||
|
||||
(void)choice;
|
||||
|
||||
#if 1
|
||||
if (cv_addons_option.value == 0)
|
||||
pathname = addonsdir;
|
||||
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\nPress (B)\n", LOCATIONSTRING1),NULL,MM_NOTHING);
|
||||
return;
|
||||
}
|
||||
else
|
||||
dir_on[menudepthleft] = 0;
|
||||
|
||||
MISC_AddonsDef.lastOn = 0; // Always start on search
|
||||
|
||||
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\nPress (B)\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);
|
||||
}
|
||||
|
||||
// Handles messages for addon errors.
|
||||
void M_AddonsRefresh(void)
|
||||
{
|
||||
if ((refreshdirmenu & REFRESHDIR_NORMAL) && !preparefilemenu(true, false))
|
||||
{
|
||||
UNEXIST;
|
||||
if (refreshdirname)
|
||||
{
|
||||
CLEARNAME;
|
||||
}
|
||||
return;// true;
|
||||
}
|
||||
|
||||
#ifdef DEVELOP
|
||||
prevmajormods = majormods;
|
||||
#else
|
||||
if (!majormods && prevmajormods)
|
||||
prevmajormods = false;
|
||||
#endif
|
||||
|
||||
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\nPress (B)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
|
||||
else
|
||||
message = va("%c%s\x80\nA file was not loaded.\nCheck the console log for more info.\n\nPress (B)\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 info.\n\nPress (B)\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\nPress (B)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
|
||||
prevmajormods = majormods;
|
||||
}
|
||||
|
||||
if (message)
|
||||
{
|
||||
M_StartMessage(message,FUNCPTRCAST(M_AddonsClearName),MM_YESNO);
|
||||
return;// true;
|
||||
}
|
||||
|
||||
S_StartSound(NULL, sfx_s221);
|
||||
CLEARNAME;
|
||||
}
|
||||
|
||||
return;// false;
|
||||
}
|
||||
|
||||
static void M_AddonExec(INT32 ch)
|
||||
{
|
||||
if (ch == MA_YES)
|
||||
{
|
||||
S_StartSound(NULL, sfx_zoom);
|
||||
COM_BufAddText(va("exec \"%s%s\"", menupath, dirmenu[dir_on[menudepthleft]]+DIR_STRING));
|
||||
}
|
||||
}
|
||||
|
||||
static void M_UpdateAddonsSearch(void)
|
||||
{
|
||||
menusearch[0] = strlen(cv_dummyaddonsearch.string);
|
||||
strlcpy(menusearch+1, cv_dummyaddonsearch.string, MAXSTRINGLENGTH);
|
||||
if (!cv_addons_search_case.value)
|
||||
strupr(menusearch+1);
|
||||
|
||||
#if 0 // much slower
|
||||
if (!preparefilemenu(true, false))
|
||||
{
|
||||
UNEXIST;
|
||||
return;
|
||||
}
|
||||
#else // streamlined
|
||||
searchfilemenu(NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void M_HandleAddons(INT32 choice)
|
||||
{
|
||||
const UINT8 pid = 0;
|
||||
boolean exitmenu = false; // exit to previous menu
|
||||
|
||||
(void) choice;
|
||||
|
||||
if (menucmd[pid].dpad_ud > 0)
|
||||
{
|
||||
if (dir_on[menudepthleft] < sizedirmenu-1)
|
||||
{
|
||||
dir_on[menudepthleft]++;
|
||||
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);
|
||||
}
|
||||
else if (M_PrevOpt())
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
}
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
||||
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_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
||||
else if (M_MenuButtonPressed(pid, MBT_R))
|
||||
{
|
||||
UINT8 i;
|
||||
for (i = numaddonsshown; i && (dir_on[menudepthleft]); i--)
|
||||
dir_on[menudepthleft]--;
|
||||
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
||||
else if (M_MenuConfirmPressed(pid))
|
||||
{
|
||||
boolean refresh = true;
|
||||
M_SetMenuDelay(pid);
|
||||
|
||||
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\nPress (B)\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_s3k5b);
|
||||
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\nPress (B)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING);
|
||||
menupath[menupathindex[menudepthleft]] = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case EXT_UP:
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
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\nPress (A) to confirm or (B) to cancel\n\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), dirmenu[dir_on[menudepthleft]]+DIR_STRING),FUNCPTRCAST(M_AddonExec),MM_YESNO);
|
||||
break;
|
||||
|
||||
case EXT_CFG:
|
||||
M_StartMessage(va("%c%s\x80\nThis file may modify your settings.\nAttempt to run anyways? \n\nPress (A) to confirm or (B) to cancel\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), dirmenu[dir_on[menudepthleft]]+DIR_STRING),FUNCPTRCAST(M_AddonExec),MM_YESNO);
|
||||
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;
|
||||
}
|
||||
}
|
||||
else if (M_MenuBackPressed(pid))
|
||||
{
|
||||
exitmenu = true;
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
/// \brief Challenges.
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../m_cond.h" // Condition Sets
|
||||
#include "../m_random.h" // And just some randomness for the exits.
|
||||
#include "../z_zone.h"
|
||||
#include "../r_skins.h"
|
||||
#include "../s_sound.h"
|
||||
|
||||
menuitem_t MISC_ChallengesStatsDummyMenu[] =
|
||||
{
|
||||
|
|
@ -23,6 +28,8 @@ menu_t MISC_ChallengesDef = {
|
|||
M_ChallengesInputs,
|
||||
};
|
||||
|
||||
// This must be defined here so it can take sizeof
|
||||
// MISC_ChallengesStatsDummyMenu :V
|
||||
menu_t MISC_StatisticsDef = {
|
||||
sizeof (MISC_ChallengesStatsDummyMenu)/sizeof (menuitem_t),
|
||||
&MainDef,
|
||||
|
|
@ -37,3 +44,558 @@ menu_t MISC_StatisticsDef = {
|
|||
NULL,
|
||||
M_StatisticsInputs,
|
||||
};
|
||||
|
||||
struct challengesmenu_s challengesmenu;
|
||||
|
||||
menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu)
|
||||
{
|
||||
UINT8 i;
|
||||
|
||||
M_UpdateUnlockablesAndExtraEmblems(false);
|
||||
|
||||
if ((challengesmenu.pending = challengesmenu.requestnew = (M_GetNextAchievedUnlock() < MAXUNLOCKABLES)))
|
||||
{
|
||||
MISC_ChallengesDef.prevMenu = desiredmenu;
|
||||
}
|
||||
|
||||
if (challengesmenu.pending || desiredmenu == NULL)
|
||||
{
|
||||
challengesmenu.currentunlock = MAXUNLOCKABLES;
|
||||
challengesmenu.unlockcondition = NULL;
|
||||
|
||||
M_PopulateChallengeGrid();
|
||||
if (gamedata->challengegrid)
|
||||
challengesmenu.extradata = M_ChallengeGridExtraData();
|
||||
|
||||
memset(setup_explosions, 0, sizeof(setup_explosions));
|
||||
memset(&challengesmenu.unlockcount, 0, sizeof(challengesmenu.unlockcount));
|
||||
for (i = 0; i < MAXUNLOCKABLES; i++)
|
||||
{
|
||||
if (!unlockables[i].conditionset)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
challengesmenu.unlockcount[CC_TOTAL]++;
|
||||
|
||||
if (!gamedata->unlocked[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
challengesmenu.unlockcount[CC_UNLOCKED]++;
|
||||
}
|
||||
|
||||
return &MISC_ChallengesDef;
|
||||
}
|
||||
|
||||
return desiredmenu;
|
||||
}
|
||||
|
||||
static void M_ChallengesAutoFocus(UINT8 unlockid, boolean fresh)
|
||||
{
|
||||
UINT8 i;
|
||||
SINT8 work;
|
||||
|
||||
if (unlockid >= MAXUNLOCKABLES)
|
||||
return;
|
||||
|
||||
challengesmenu.currentunlock = unlockid;
|
||||
challengesmenu.unlockcondition = M_BuildConditionSetString(challengesmenu.currentunlock);
|
||||
challengesmenu.unlockanim = 0;
|
||||
|
||||
if (gamedata->challengegrid == NULL || challengesmenu.extradata == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < (CHALLENGEGRIDHEIGHT * gamedata->challengegridwidth); i++)
|
||||
{
|
||||
if (gamedata->challengegrid[i] != unlockid)
|
||||
{
|
||||
// Not what we're looking for.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (challengesmenu.extradata[i] & CHE_CONNECTEDLEFT)
|
||||
{
|
||||
// no need to check for CHE_CONNECTEDUP in linear iteration
|
||||
continue;
|
||||
}
|
||||
|
||||
// Helper calculation for non-fresh scrolling.
|
||||
work = (challengesmenu.col + challengesmenu.focusx);
|
||||
|
||||
challengesmenu.col = challengesmenu.hilix = i/CHALLENGEGRIDHEIGHT;
|
||||
challengesmenu.row = challengesmenu.hiliy = i%CHALLENGEGRIDHEIGHT;
|
||||
|
||||
if (fresh)
|
||||
{
|
||||
// We're just entering the menu. Immediately jump to the desired position...
|
||||
challengesmenu.focusx = 0;
|
||||
// ...and since the menu is even-width, randomly select whether it's left or right of center.
|
||||
if (!unlockables[unlockid].majorunlock
|
||||
&& M_RandomChance(FRACUNIT/2))
|
||||
challengesmenu.focusx--;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're jumping between multiple unlocks in sequence. Get the difference (looped from -range/2 < work <= range/2).
|
||||
work -= challengesmenu.col;
|
||||
if (work <= -gamedata->challengegridwidth/2)
|
||||
work += gamedata->challengegridwidth;
|
||||
else if (work >= gamedata->challengegridwidth/2)
|
||||
work -= gamedata->challengegridwidth;
|
||||
|
||||
if (work > 0)
|
||||
{
|
||||
// We only need to scroll as far as the rightward edge.
|
||||
if (unlockables[unlockid].majorunlock)
|
||||
{
|
||||
work--;
|
||||
challengesmenu.col++;
|
||||
if (challengesmenu.col >= gamedata->challengegridwidth)
|
||||
challengesmenu.col = 0;
|
||||
}
|
||||
|
||||
// Offset right, scroll left?
|
||||
if (work > LEFTUNLOCKSCROLL)
|
||||
{
|
||||
work -= LEFTUNLOCKSCROLL;
|
||||
challengesmenu.focusx = LEFTUNLOCKSCROLL;
|
||||
}
|
||||
else
|
||||
{
|
||||
challengesmenu.focusx = work;
|
||||
work = 0;
|
||||
}
|
||||
}
|
||||
else if (work < 0)
|
||||
{
|
||||
// Offset left, scroll right?
|
||||
if (work < -RIGHTUNLOCKSCROLL)
|
||||
{
|
||||
challengesmenu.focusx = -RIGHTUNLOCKSCROLL;
|
||||
work += RIGHTUNLOCKSCROLL;
|
||||
}
|
||||
else
|
||||
{
|
||||
challengesmenu.focusx = work;
|
||||
work = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're right where we want to be.
|
||||
challengesmenu.focusx = 0;
|
||||
}
|
||||
|
||||
// And put the pixel-based scrolling in play, too.
|
||||
challengesmenu.offset = -work*16;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void M_Challenges(INT32 choice)
|
||||
{
|
||||
UINT8 i;
|
||||
(void)choice;
|
||||
|
||||
M_InterruptMenuWithChallenges(NULL);
|
||||
MISC_ChallengesDef.prevMenu = currentMenu;
|
||||
|
||||
if (gamedata->challengegrid != NULL && !challengesmenu.pending)
|
||||
{
|
||||
UINT8 selection[MAXUNLOCKABLES];
|
||||
UINT8 numunlocks = 0;
|
||||
|
||||
// Get a random available unlockable.
|
||||
for (i = 0; i < MAXUNLOCKABLES; i++)
|
||||
{
|
||||
if (!unlockables[i].conditionset)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!gamedata->unlocked[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
selection[numunlocks++] = i;
|
||||
}
|
||||
|
||||
if (!numunlocks)
|
||||
{
|
||||
// ...OK, get a random unlockable.
|
||||
for (i = 0; i < MAXUNLOCKABLES; i++)
|
||||
{
|
||||
if (!unlockables[i].conditionset)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
selection[numunlocks++] = i;
|
||||
}
|
||||
}
|
||||
|
||||
M_ChallengesAutoFocus(selection[M_RandomKey(numunlocks)], true);
|
||||
}
|
||||
|
||||
M_SetupNextMenu(&MISC_ChallengesDef, false);
|
||||
}
|
||||
|
||||
void M_ChallengesTick(void)
|
||||
{
|
||||
const UINT8 pid = 0;
|
||||
UINT8 i, newunlock = MAXUNLOCKABLES;
|
||||
boolean fresh = (challengesmenu.currentunlock >= MAXUNLOCKABLES);
|
||||
|
||||
// Ticking
|
||||
challengesmenu.ticker++;
|
||||
challengesmenu.offset /= 2;
|
||||
for (i = 0; i < CSEXPLOSIONS; i++)
|
||||
{
|
||||
if (setup_explosions[i].tics > 0)
|
||||
setup_explosions[i].tics--;
|
||||
}
|
||||
if (challengesmenu.unlockcount[CC_ANIM] > 0)
|
||||
challengesmenu.unlockcount[CC_ANIM]--;
|
||||
M_CupSelectTick();
|
||||
|
||||
if (challengesmenu.pending)
|
||||
{
|
||||
// Pending mode.
|
||||
|
||||
if (challengesmenu.requestnew)
|
||||
{
|
||||
// The menu apparatus is requesting a new unlock.
|
||||
challengesmenu.requestnew = false;
|
||||
if ((newunlock = M_GetNextAchievedUnlock()) < MAXUNLOCKABLES)
|
||||
{
|
||||
// We got one!
|
||||
M_ChallengesAutoFocus(newunlock, fresh);
|
||||
}
|
||||
else
|
||||
{
|
||||
// All done! Let's save the unlocks we've busted open.
|
||||
challengesmenu.pending = false;
|
||||
G_SaveGameData();
|
||||
}
|
||||
}
|
||||
else if (challengesmenu.fade < 5)
|
||||
{
|
||||
// Fade increase.
|
||||
challengesmenu.fade++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unlock sequence.
|
||||
tic_t nexttime = M_MenuExtraHeld(pid) ? (UNLOCKTIME*2) : MAXUNLOCKTIME;
|
||||
|
||||
if (++challengesmenu.unlockanim >= nexttime)
|
||||
{
|
||||
challengesmenu.requestnew = true;
|
||||
}
|
||||
|
||||
if (challengesmenu.currentunlock < MAXUNLOCKABLES
|
||||
&& challengesmenu.unlockanim == UNLOCKTIME)
|
||||
{
|
||||
// Unlock animation... also tied directly to the actual unlock!
|
||||
gamedata->unlocked[challengesmenu.currentunlock] = true;
|
||||
M_UpdateUnlockablesAndExtraEmblems(true);
|
||||
|
||||
// Update shown description just in case..?
|
||||
challengesmenu.unlockcondition = M_BuildConditionSetString(challengesmenu.currentunlock);
|
||||
|
||||
challengesmenu.unlockcount[CC_TALLY]++;
|
||||
challengesmenu.unlockcount[CC_ANIM]++;
|
||||
|
||||
Z_Free(challengesmenu.extradata);
|
||||
if ((challengesmenu.extradata = M_ChallengeGridExtraData()))
|
||||
{
|
||||
unlockable_t *ref = &unlockables[challengesmenu.currentunlock];
|
||||
UINT16 bombcolor = SKINCOLOR_NONE;
|
||||
|
||||
if (ref->color != SKINCOLOR_NONE && ref->color < numskincolors)
|
||||
{
|
||||
bombcolor = ref->color;
|
||||
}
|
||||
else switch (ref->type)
|
||||
{
|
||||
case SECRET_SKIN:
|
||||
{
|
||||
INT32 skin = M_UnlockableSkinNum(ref);
|
||||
if (skin != -1)
|
||||
{
|
||||
bombcolor = skins[skin].prefcolor;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SECRET_FOLLOWER:
|
||||
{
|
||||
INT32 skin = M_UnlockableFollowerNum(ref);
|
||||
if (skin != -1)
|
||||
{
|
||||
bombcolor = K_GetEffectiveFollowerColor(followers[skin].defaultcolor, cv_playercolor[0].value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (bombcolor == SKINCOLOR_NONE)
|
||||
{
|
||||
bombcolor = cv_playercolor[0].value;
|
||||
}
|
||||
|
||||
i = (ref->majorunlock && M_RandomChance(FRACUNIT/2)) ? 1 : 0;
|
||||
M_SetupReadyExplosions(false, challengesmenu.hilix, challengesmenu.hiliy+i, bombcolor);
|
||||
if (ref->majorunlock)
|
||||
{
|
||||
M_SetupReadyExplosions(false, challengesmenu.hilix+1, challengesmenu.hiliy+(1-i), bombcolor);
|
||||
}
|
||||
|
||||
S_StartSound(NULL, sfx_s3k4e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Tick down the tally. (currently not visible)
|
||||
/*if ((challengesmenu.ticker & 1)
|
||||
&& challengesmenu.unlockcount[CC_TALLY] > 0)
|
||||
{
|
||||
challengesmenu.unlockcount[CC_TALLY]--;
|
||||
challengesmenu.unlockcount[CC_UNLOCKED]++;
|
||||
}*/
|
||||
|
||||
if (challengesmenu.fade > 0)
|
||||
{
|
||||
// Fade decrease.
|
||||
challengesmenu.fade--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean M_ChallengesInputs(INT32 ch)
|
||||
{
|
||||
const UINT8 pid = 0;
|
||||
UINT8 i;
|
||||
const boolean start = M_MenuButtonPressed(pid, MBT_START);
|
||||
const boolean move = (menucmd[pid].dpad_ud != 0 || menucmd[pid].dpad_lr != 0);
|
||||
(void) ch;
|
||||
|
||||
if (challengesmenu.fade)
|
||||
{
|
||||
;
|
||||
}
|
||||
#ifdef DEVELOP
|
||||
else if (M_MenuExtraPressed(pid) && challengesmenu.extradata) // debugging
|
||||
{
|
||||
if (challengesmenu.currentunlock < MAXUNLOCKABLES)
|
||||
{
|
||||
Z_Free(gamedata->challengegrid);
|
||||
gamedata->challengegrid = NULL;
|
||||
gamedata->challengegridwidth = 0;
|
||||
M_PopulateChallengeGrid();
|
||||
Z_Free(challengesmenu.extradata);
|
||||
challengesmenu.extradata = M_ChallengeGridExtraData();
|
||||
|
||||
M_ChallengesAutoFocus(challengesmenu.currentunlock, true);
|
||||
|
||||
challengesmenu.pending = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
if (M_MenuBackPressed(pid) || start)
|
||||
{
|
||||
M_GoBack(0);
|
||||
M_SetMenuDelay(pid);
|
||||
|
||||
Z_Free(challengesmenu.extradata);
|
||||
challengesmenu.extradata = NULL;
|
||||
|
||||
challengesmenu.unlockcondition = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (challengesmenu.extradata != NULL && move)
|
||||
{
|
||||
// Determine movement around the grid
|
||||
// For right/down movement, we can pre-determine the number of steps based on extradata.
|
||||
// For left/up movement, we can't - we have to be ready to iterate twice, and break early if we don't run into a large tile.
|
||||
|
||||
if (menucmd[pid].dpad_ud > 0)
|
||||
{
|
||||
i = 2;
|
||||
while (i > 0)
|
||||
{
|
||||
if (challengesmenu.row < CHALLENGEGRIDHEIGHT-1)
|
||||
{
|
||||
challengesmenu.row++;
|
||||
}
|
||||
else
|
||||
{
|
||||
challengesmenu.row = 0;
|
||||
}
|
||||
if (!(challengesmenu.extradata[
|
||||
(challengesmenu.col * CHALLENGEGRIDHEIGHT)
|
||||
+ challengesmenu.row]
|
||||
& CHE_CONNECTEDUP))
|
||||
{
|
||||
break;
|
||||
}
|
||||
i--;
|
||||
}
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
else if (menucmd[pid].dpad_ud < 0)
|
||||
{
|
||||
i = (challengesmenu.extradata[
|
||||
(challengesmenu.col * CHALLENGEGRIDHEIGHT)
|
||||
+ challengesmenu.row]
|
||||
& CHE_CONNECTEDUP) ? 2 : 1;
|
||||
while (i > 0)
|
||||
{
|
||||
if (challengesmenu.row > 0)
|
||||
{
|
||||
challengesmenu.row--;
|
||||
}
|
||||
else
|
||||
{
|
||||
challengesmenu.row = CHALLENGEGRIDHEIGHT-1;
|
||||
}
|
||||
i--;
|
||||
}
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
||||
if (menucmd[pid].dpad_lr > 0)
|
||||
{
|
||||
i = 2;
|
||||
while (i > 0)
|
||||
{
|
||||
// Slide the focus counter to movement, if we can.
|
||||
if (challengesmenu.focusx > -RIGHTUNLOCKSCROLL)
|
||||
{
|
||||
challengesmenu.focusx--;
|
||||
}
|
||||
|
||||
// Step the actual column right.
|
||||
if (challengesmenu.col < gamedata->challengegridwidth-1)
|
||||
{
|
||||
challengesmenu.col++;
|
||||
}
|
||||
else
|
||||
{
|
||||
challengesmenu.col = 0;
|
||||
}
|
||||
|
||||
if (!(challengesmenu.extradata[
|
||||
(challengesmenu.col * CHALLENGEGRIDHEIGHT)
|
||||
+ challengesmenu.row]
|
||||
& CHE_CONNECTEDLEFT))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
i--;
|
||||
}
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
else if (menucmd[pid].dpad_lr < 0)
|
||||
{
|
||||
i = (challengesmenu.extradata[
|
||||
(challengesmenu.col * CHALLENGEGRIDHEIGHT)
|
||||
+ challengesmenu.row]
|
||||
& CHE_CONNECTEDLEFT) ? 2 : 1;
|
||||
while (i > 0)
|
||||
{
|
||||
// Slide the focus counter to movement, if we can.
|
||||
if (challengesmenu.focusx < LEFTUNLOCKSCROLL)
|
||||
{
|
||||
challengesmenu.focusx++;
|
||||
}
|
||||
|
||||
// Step the actual column left.
|
||||
if (challengesmenu.col > 0)
|
||||
{
|
||||
challengesmenu.col--;
|
||||
}
|
||||
else
|
||||
{
|
||||
challengesmenu.col = gamedata->challengegridwidth-1;
|
||||
}
|
||||
|
||||
i--;
|
||||
}
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
||||
// After movement has been determined, figure out the current selection.
|
||||
i = (challengesmenu.col * CHALLENGEGRIDHEIGHT) + challengesmenu.row;
|
||||
challengesmenu.currentunlock = (gamedata->challengegrid[i]);
|
||||
challengesmenu.unlockcondition = M_BuildConditionSetString(challengesmenu.currentunlock);
|
||||
|
||||
challengesmenu.hilix = challengesmenu.col;
|
||||
challengesmenu.hiliy = challengesmenu.row;
|
||||
|
||||
if (challengesmenu.currentunlock < MAXUNLOCKABLES
|
||||
&& unlockables[challengesmenu.currentunlock].majorunlock)
|
||||
{
|
||||
// Adjust highlight coordinates up/to the left for large tiles.
|
||||
|
||||
if (challengesmenu.hiliy > 0 && (challengesmenu.extradata[i] & CHE_CONNECTEDUP))
|
||||
{
|
||||
challengesmenu.hiliy--;
|
||||
}
|
||||
|
||||
if ((challengesmenu.extradata[i] & CHE_CONNECTEDLEFT))
|
||||
{
|
||||
if (challengesmenu.hilix > 0)
|
||||
{
|
||||
challengesmenu.hilix--;
|
||||
}
|
||||
else
|
||||
{
|
||||
challengesmenu.hilix = gamedata->challengegridwidth-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (M_MenuConfirmPressed(pid)
|
||||
&& challengesmenu.currentunlock < MAXUNLOCKABLES
|
||||
&& gamedata->unlocked[challengesmenu.currentunlock])
|
||||
{
|
||||
switch (unlockables[challengesmenu.currentunlock].type)
|
||||
{
|
||||
case SECRET_ALTTITLE:
|
||||
CV_AddValue(&cv_alttitle, 1);
|
||||
S_StartSound(NULL, sfx_s3kc3s);
|
||||
M_SetMenuDelay(pid);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
/// \brief Extras Menu: Replay Hut
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../filesrch.h" // Addfile
|
||||
#include "../d_main.h"
|
||||
#include "../s_sound.h"
|
||||
#include "../v_video.h"
|
||||
#include "../z_zone.h"
|
||||
|
||||
// extras menu: replay hut
|
||||
menuitem_t EXTRAS_ReplayHut[] =
|
||||
|
|
@ -60,3 +65,228 @@ menu_t EXTRAS_ReplayStartDef =
|
|||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
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, sizeof extrasmenu.demolist[i].filepath,
|
||||
// 255 = UINT8 limit. dirmenu entries are restricted to this length (see DIR_LEN).
|
||||
"%s%.255s", 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\nPress (B)\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)
|
||||
{
|
||||
|
||||
const UINT8 pid = 0;
|
||||
(void) choice;
|
||||
|
||||
if (menucmd[pid].dpad_ud < 0)
|
||||
{
|
||||
if (dir_on[menudepthleft])
|
||||
dir_on[menudepthleft]--;
|
||||
else
|
||||
return;
|
||||
//M_PrevOpt();
|
||||
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
extrasmenu.replayScrollTitle = 0; extrasmenu.replayScrollDelay = TICRATE; extrasmenu.replayScrollDir = 1;
|
||||
}
|
||||
|
||||
else if (menucmd[pid].dpad_ud > 0)
|
||||
{
|
||||
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_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
extrasmenu.replayScrollTitle = 0; extrasmenu.replayScrollDelay = TICRATE; extrasmenu.replayScrollDir = 1;
|
||||
}
|
||||
|
||||
else if (M_MenuBackPressed(pid))
|
||||
{
|
||||
M_SetMenuDelay(pid);
|
||||
M_QuitReplayHut();
|
||||
}
|
||||
|
||||
else if (M_MenuConfirmPressed(pid))
|
||||
{
|
||||
M_SetMenuDelay(pid);
|
||||
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\nPress (B)\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_s3k5b);
|
||||
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\nPress (B)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING);
|
||||
menupath[menupathindex[menudepthleft]] = 0;
|
||||
}
|
||||
break;
|
||||
case EXT_UP:
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
99
src/menus/extras-statistics.c
Normal file
99
src/menus/extras-statistics.c
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/// \file menus/extras-challenges.c
|
||||
/// \brief Statistics menu
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../z_zone.h"
|
||||
#include "../m_cond.h" // Condition Sets
|
||||
#include "../s_sound.h"
|
||||
|
||||
struct statisticsmenu_s statisticsmenu;
|
||||
|
||||
void M_Statistics(INT32 choice)
|
||||
{
|
||||
UINT16 i = 0;
|
||||
|
||||
(void)choice;
|
||||
|
||||
statisticsmenu.maplist = Z_Malloc(sizeof(UINT16) * nummapheaders, PU_STATIC, NULL);
|
||||
statisticsmenu.nummaps = 0;
|
||||
|
||||
for (i = 0; i < nummapheaders; i++)
|
||||
{
|
||||
if (!mapheaderinfo[i])
|
||||
continue;
|
||||
|
||||
// Check for no visibility + legacy box
|
||||
if (mapheaderinfo[i]->menuflags & (LF2_NOTIMEATTACK|LF2_HIDEINSTATS|LF2_HIDEINMENU))
|
||||
continue;
|
||||
|
||||
// Check for completion
|
||||
if ((mapheaderinfo[i]->menuflags & LF2_FINISHNEEDED)
|
||||
&& !(mapheaderinfo[i]->mapvisited & MV_BEATEN))
|
||||
continue;
|
||||
|
||||
// Check for unlock
|
||||
if (M_MapLocked(i+1))
|
||||
continue;
|
||||
|
||||
statisticsmenu.maplist[statisticsmenu.nummaps++] = i;
|
||||
}
|
||||
statisticsmenu.maplist[statisticsmenu.nummaps] = NEXTMAP_INVALID;
|
||||
statisticsmenu.maxscroll = (statisticsmenu.nummaps + M_CountMedals(true, true) + 2) - 10;
|
||||
statisticsmenu.location = 0;
|
||||
|
||||
if (statisticsmenu.maxscroll < 0)
|
||||
{
|
||||
statisticsmenu.maxscroll = 0;
|
||||
}
|
||||
|
||||
MISC_StatisticsDef.prevMenu = currentMenu;
|
||||
M_SetupNextMenu(&MISC_StatisticsDef, false);
|
||||
}
|
||||
|
||||
boolean M_StatisticsInputs(INT32 ch)
|
||||
{
|
||||
const UINT8 pid = 0;
|
||||
|
||||
(void)ch;
|
||||
|
||||
if (M_MenuBackPressed(pid))
|
||||
{
|
||||
M_GoBack(0);
|
||||
M_SetMenuDelay(pid);
|
||||
|
||||
Z_Free(statisticsmenu.maplist);
|
||||
statisticsmenu.maplist = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (M_MenuExtraPressed(pid))
|
||||
{
|
||||
if (statisticsmenu.location > 0)
|
||||
{
|
||||
statisticsmenu.location = 0;
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
}
|
||||
else if (menucmd[pid].dpad_ud > 0)
|
||||
{
|
||||
if (statisticsmenu.location < statisticsmenu.maxscroll)
|
||||
{
|
||||
statisticsmenu.location++;
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
}
|
||||
else if (menucmd[pid].dpad_ud < 0)
|
||||
{
|
||||
if (statisticsmenu.location > 0)
|
||||
{
|
||||
statisticsmenu.location--;
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -11,6 +11,13 @@
|
|||
// point to submenus.
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../m_random.h"
|
||||
#include "../s_sound.h"
|
||||
#include "../i_time.h"
|
||||
#include "../v_video.h"
|
||||
#include "../z_zone.h"
|
||||
#include "../i_video.h" // I_FinishUpdate
|
||||
#include "../i_system.h" // I_Sleep
|
||||
|
||||
menuitem_t MainMenu[] =
|
||||
{
|
||||
|
|
@ -32,3 +39,74 @@ menuitem_t MainMenu[] =
|
|||
};
|
||||
|
||||
menu_t MainDef = KARTGAMEMODEMENU(MainMenu, NULL);
|
||||
|
||||
// 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_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\nPress (A) to confirm or (B) to cancel", FUNCPTRCAST(M_QuitResponse), MM_YESNO);
|
||||
}
|
||||
|
||||
void M_QuitResponse(INT32 ch)
|
||||
{
|
||||
tic_t ptime;
|
||||
INT32 mrand;
|
||||
|
||||
if (ch == MA_YES)
|
||||
{
|
||||
if (!(netgame || cht_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(cv_sleep.value);
|
||||
I_UpdateTime(cv_timescale.value);
|
||||
}
|
||||
}
|
||||
I_Quit();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
/// \brief Options Menu
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../k_grandprix.h" // K_CanChangeRules
|
||||
#include "../m_cond.h" // Condition Sets
|
||||
#include "../s_sound.h"
|
||||
|
||||
// options menu -- see mopt_e
|
||||
menuitem_t OPTIONS_Main[] =
|
||||
|
|
@ -47,3 +50,181 @@ menu_t OPTIONS_MainDef = {
|
|||
NULL,
|
||||
M_OptionsInputs
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
OPTIONS_MainDef.menuitems[mopt_gameplay].status = IT_STRING | IT_TRANSTEXT;
|
||||
OPTIONS_MainDef.menuitems[mopt_server].status = IT_STRING | IT_TRANSTEXT;
|
||||
|
||||
// enable gameplay & server options under the right circumstances.
|
||||
if (gamestate == GS_MENU
|
||||
|| ((server || IsPlayerAdmin(consoleplayer)) && K_CanChangeRules(false)))
|
||||
{
|
||||
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, false) ? (IT_STRING | IT_CVAR) : IT_DISABLED);
|
||||
}
|
||||
|
||||
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...
|
||||
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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
optionsmenu.topty = 70+1;
|
||||
|
||||
// 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.
|
||||
}
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
// Move the button for cool animations
|
||||
if (currentMenu == &OPTIONS_MainDef)
|
||||
{
|
||||
M_OptionsQuit(); // ...So now this is used here.
|
||||
}
|
||||
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)
|
||||
{
|
||||
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;
|
||||
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);
|
||||
|
||||
// And one last giggle...
|
||||
if (shitsfree)
|
||||
shitsfree--;
|
||||
}
|
||||
|
||||
boolean M_OptionsInputs(INT32 ch)
|
||||
{
|
||||
|
||||
const UINT8 pid = 0;
|
||||
(void)ch;
|
||||
|
||||
if (menucmd[pid].dpad_ud > 0)
|
||||
{
|
||||
M_SetMenuDelay(pid);
|
||||
optionsmenu.offset += 48;
|
||||
M_NextOpt();
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
|
||||
if (itemOn == 0)
|
||||
optionsmenu.offset -= currentMenu->numitems*48;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (menucmd[pid].dpad_ud < 0)
|
||||
{
|
||||
M_SetMenuDelay(pid);
|
||||
optionsmenu.offset -= 48;
|
||||
M_PrevOpt();
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
|
||||
if (itemOn == currentMenu->numitems-1)
|
||||
optionsmenu.offset += currentMenu->numitems*48;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (M_MenuConfirmPressed(pid))
|
||||
{
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,3 +51,10 @@ menu_t OPTIONS_DataAddonDef = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
void Addons_option_Onchange(void)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
/// \brief Erase Data Menu
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../s_sound.h"
|
||||
#include "../m_cond.h" // Condition Sets
|
||||
#include "../f_finale.h"
|
||||
|
||||
menuitem_t OPTIONS_DataErase[] =
|
||||
{
|
||||
|
|
@ -40,3 +43,42 @@ menu_t OPTIONS_DataEraseDef = {
|
|||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void M_EraseDataResponse(INT32 ch)
|
||||
{
|
||||
if (ch == MA_NO)
|
||||
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
|
||||
gamedata->totalplaytime = 0;
|
||||
gamedata->matchesplayed = 0;
|
||||
}
|
||||
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\nPress (A) to confirm or (B) to cancel\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), FUNCPTRCAST(M_EraseDataResponse), MM_YESNO);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
/// \brief Erase Profile Menu
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../s_sound.h"
|
||||
#include "../f_finale.h"
|
||||
|
||||
menuitem_t OPTIONS_DataProfileErase[] =
|
||||
{
|
||||
|
|
@ -22,3 +24,84 @@ menu_t OPTIONS_DataProfileEraseDef = {
|
|||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
// 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\nPress (B)", 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);
|
||||
|
||||
// Did we bust our current profile..!?
|
||||
if (cv_currprofile.value == -1)
|
||||
{
|
||||
F_StartIntro();
|
||||
M_ClearMenus(true);
|
||||
}
|
||||
else if (optionsmenu.eraseprofilen > PR_GetNumProfiles()-1)
|
||||
{
|
||||
optionsmenu.eraseprofilen--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_s3k5b);
|
||||
optionsmenu.eraseprofilen++;
|
||||
|
||||
if (optionsmenu.eraseprofilen > np)
|
||||
optionsmenu.eraseprofilen = 1;
|
||||
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
else if (menucmd[pid].dpad_ud < 0)
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
|
||||
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("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\nPress (A) to confirm or (B) to cancel", FUNCPTRCAST(M_EraseProfileResponse), MM_YESNO);
|
||||
else
|
||||
M_StartMessage("This profile will be erased.\nAre you sure you want to proceed?\n\nPress (A) to confirm or (B) to cancel", FUNCPTRCAST(M_EraseProfileResponse), MM_YESNO);
|
||||
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,3 +44,43 @@ menu_t OPTIONS_DataScreenshotDef = {
|
|||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void Screenshot_option_Onchange(void)
|
||||
{
|
||||
// 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);
|
||||
|
||||
}
|
||||
|
||||
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 Moviemode_option_Onchange(void)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
/// \brief Random Item Toggles
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../s_sound.h"
|
||||
|
||||
menuitem_t OPTIONS_GameplayItems[] =
|
||||
{
|
||||
|
|
@ -60,3 +61,122 @@ menu_t OPTIONS_GameplayItemsDef = {
|
|||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void M_HandleItemToggles(INT32 choice)
|
||||
{
|
||||
const INT32 width = 8, height = 4;
|
||||
INT32 column = itemOn/height, row = itemOn%height;
|
||||
INT16 next;
|
||||
UINT8 i;
|
||||
boolean exitmenu = false;
|
||||
const UINT8 pid = 0;
|
||||
|
||||
(void) choice;
|
||||
|
||||
if (menucmd[pid].dpad_lr > 0)
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
column++;
|
||||
if (((column*height)+row) >= currentMenu->numitems)
|
||||
column = 0;
|
||||
next = min(((column*height)+row), currentMenu->numitems-1);
|
||||
itemOn = next;
|
||||
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
||||
else if (menucmd[pid].dpad_lr < 0)
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
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;
|
||||
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
||||
else if (menucmd[pid].dpad_ud > 0)
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
row = (row+1) % height;
|
||||
if (((column*height)+row) >= currentMenu->numitems)
|
||||
row = 0;
|
||||
next = min(((column*height)+row), currentMenu->numitems-1);
|
||||
itemOn = next;
|
||||
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
||||
else if (menucmd[pid].dpad_ud < 0)
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
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_MenuConfirmPressed(pid))
|
||||
{
|
||||
M_SetMenuDelay(pid);
|
||||
if (currentMenu->menuitems[itemOn].mvar1 == 255)
|
||||
{
|
||||
//S_StartSound(NULL, sfx_s26d);
|
||||
if (!shitsfree)
|
||||
{
|
||||
shitsfree = TICRATE;
|
||||
S_StartSound(NULL, sfx_itfree);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (currentMenu->menuitems[itemOn].mvar1 == 0)
|
||||
{
|
||||
INT32 v = cv_items[0].value;
|
||||
S_StartSound(NULL, sfx_s1b4);
|
||||
for (i = 0; i < NUMKARTRESULTS-1; i++)
|
||||
{
|
||||
if (cv_items[i].value == v)
|
||||
CV_AddValue(&cv_items[i], 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentMenu->menuitems[itemOn].mvar2)
|
||||
{
|
||||
S_StartSound(NULL, currentMenu->menuitems[itemOn].mvar2);
|
||||
}
|
||||
else
|
||||
{
|
||||
S_StartSound(NULL, sfx_s1ba);
|
||||
}
|
||||
CV_AddValue(&cv_items[currentMenu->menuitems[itemOn].mvar1-1], 1);
|
||||
}
|
||||
}
|
||||
|
||||
else if (M_MenuBackPressed(pid))
|
||||
{
|
||||
M_SetMenuDelay(pid);
|
||||
exitmenu = true;
|
||||
}
|
||||
|
||||
if (exitmenu)
|
||||
{
|
||||
if (currentMenu->prevMenu)
|
||||
M_SetupNextMenu(currentMenu->prevMenu, false);
|
||||
else
|
||||
M_ClearMenus(true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
/// \brief Profiles Menu
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../s_sound.h"
|
||||
|
||||
// profile select
|
||||
menuitem_t OPTIONS_Profiles[] = {
|
||||
|
|
@ -23,3 +24,195 @@ menu_t OPTIONS_ProfilesDef = {
|
|||
NULL,
|
||||
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_dummyprofilekickstart = CVAR_INIT ("dummyprofilekickstart", "Off", CV_HIDDEN, CV_OnOff, NULL);
|
||||
|
||||
void M_ProfileSelectInit(INT32 choice)
|
||||
{
|
||||
(void)choice;
|
||||
optionsmenu.profilemenu = true;
|
||||
|
||||
M_SetupNextMenu(&OPTIONS_ProfilesDef, false);
|
||||
}
|
||||
|
||||
// Select the current profile for menu use and go to maindef.
|
||||
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.
|
||||
|
||||
PR_ApplyProfile(optionsmenu.profilen, 0);
|
||||
M_SetupNextMenu(M_InterruptMenuWithChallenges(&MainDef), false);
|
||||
|
||||
// Tell the game this is the last profile we picked.
|
||||
CV_StealthSetValue(&cv_ttlprofilen, 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);
|
||||
// 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;
|
||||
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
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
void M_HandleProfileSelect(INT32 ch)
|
||||
{
|
||||
const UINT8 pid = 0;
|
||||
INT32 maxp = PR_GetNumProfiles();
|
||||
boolean creatable = (maxp < MAXPROFILES);
|
||||
(void) ch;
|
||||
|
||||
if (!creatable)
|
||||
{
|
||||
maxp = MAXPROFILES;
|
||||
}
|
||||
|
||||
if (menucmd[pid].dpad_lr > 0)
|
||||
{
|
||||
optionsmenu.profilen++;
|
||||
optionsmenu.offset += (128 + 128/8);
|
||||
|
||||
if (optionsmenu.profilen > maxp)
|
||||
{
|
||||
optionsmenu.profilen = 0;
|
||||
optionsmenu.offset -= (128 + 128/8)*(maxp+1);
|
||||
}
|
||||
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
|
||||
}
|
||||
else if (menucmd[pid].dpad_lr < 0)
|
||||
{
|
||||
optionsmenu.profilen--;
|
||||
optionsmenu.offset -= (128 + 128/8);
|
||||
|
||||
if (optionsmenu.profilen < 0)
|
||||
{
|
||||
optionsmenu.profilen = maxp;
|
||||
optionsmenu.offset += (128 + 128/8)*(maxp+1);
|
||||
}
|
||||
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
||||
else if (M_MenuConfirmPressed(pid))
|
||||
{
|
||||
|
||||
// Boot profile setup has already been done.
|
||||
if (cv_currprofile.value > -1)
|
||||
{
|
||||
|
||||
if (optionsmenu.profilen == 0) // Guest profile, you can't edit that one!
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3k7b);
|
||||
M_StartMessage(M_GetText("The Guest profile cannot be edited.\nCreate a new profile instead."), NULL, MM_NOTHING);
|
||||
M_SetMenuDelay(pid);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
M_SetMenuDelay(pid);
|
||||
return;
|
||||
}
|
||||
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_StartEditProfile(MA_YES);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're on the profile selection screen.
|
||||
if (creatable && optionsmenu.profilen == maxp)
|
||||
{
|
||||
M_StartEditProfile(MA_YES);
|
||||
M_SetMenuDelay(pid);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
#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\nPress (A) to confirm or (B) to cancel"), FUNCPTRCAST(M_FirstPickProfile), MM_YESNO);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
M_FirstPickProfile(MA_YES);
|
||||
M_SetMenuDelay(pid);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (M_MenuBackPressed(pid))
|
||||
{
|
||||
optionsmenu.resetprofilemenu = true;
|
||||
M_GoBack(0);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
||||
if (menutransition.tics == 0 && optionsmenu.resetprofile)
|
||||
{
|
||||
optionsmenu.profile = NULL; // Make sure to reset that when transitions are done.'
|
||||
optionsmenu.resetprofile = false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
/// \brief Profile Editor
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../s_sound.h"
|
||||
|
||||
menuitem_t OPTIONS_EditProfile[] = {
|
||||
{IT_STRING | IT_CVAR | IT_CV_STRING, "Profile Name", "6-character long name to identify this Profile.",
|
||||
|
|
@ -35,3 +36,145 @@ menu_t OPTIONS_EditProfileDef = {
|
|||
NULL,
|
||||
M_ProfileEditInputs,
|
||||
};
|
||||
|
||||
// Returns true if the profile can be saved, false otherwise. Also starts messages if necessary.
|
||||
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++)
|
||||
{
|
||||
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 name.\nThis must be changed to be able to save."), 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)
|
||||
{
|
||||
|
||||
(void) ch;
|
||||
const UINT8 pid = 0;
|
||||
|
||||
if (M_MenuBackPressed(pid))
|
||||
{
|
||||
if (M_ProfileEditEnd(pid))
|
||||
{
|
||||
M_ProfileEditExit();
|
||||
if (cv_currprofile.value == -1)
|
||||
M_SetupNextMenu(&MAIN_ProfilesDef, false);
|
||||
else
|
||||
M_GoBack(0);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (M_MenuConfirmPressed(pid))
|
||||
{
|
||||
if (currentMenu->menuitems[itemOn].status & IT_TRANSTEXT)
|
||||
return true; // No.
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle some actions in profile editing
|
||||
void M_HandleProfileEdit(void)
|
||||
{
|
||||
// Always copy the profile name and player name in the profile.
|
||||
if (optionsmenu.profile)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
M_OptionsTick(); // Has to be afterwards because this can unset optionsmenu.profile
|
||||
}
|
||||
|
||||
// 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);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
else
|
||||
{
|
||||
M_StartMessage(M_GetText("Are you sure you wish to\nselect this profile?\n\nPress (A) to confirm or (B) to cancel"), FUNCPTRCAST(M_FirstPickProfile), MM_YESNO);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
}
|
||||
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.lastkey = 0;
|
||||
optionsmenu.keyheldfor = 0;
|
||||
|
||||
optionsmenu.contx = optionsmenu.tcontx = controlleroffsets[gc_a][0];
|
||||
optionsmenu.conty = optionsmenu.tconty = controlleroffsets[gc_a][1];
|
||||
|
||||
M_SetupNextMenu(&OPTIONS_ProfileControlsDef, false); // Don't set device here anymore.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
/// \brief Profile Controls Editor
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../s_sound.h"
|
||||
#include "../i_joy.h" // for joystick menu controls
|
||||
|
||||
menuitem_t OPTIONS_ProfileControls[] = {
|
||||
|
||||
|
|
@ -108,3 +110,339 @@ menu_t OPTIONS_ProfileControlsDef = {
|
|||
NULL,
|
||||
M_ProfileControlsInputs,
|
||||
};
|
||||
|
||||
// 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)
|
||||
{
|
||||
UINT8 i;
|
||||
|
||||
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("SetDeviceOnPress: Device for %d set to %d\n", 0, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
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 = 0; // we've gone past the max, just stop.
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void M_ProfileTryController(INT32 choice)
|
||||
{
|
||||
(void)choice;
|
||||
|
||||
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);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
void M_ProfileControlsConfirm(INT32 choice)
|
||||
{
|
||||
(void)choice;
|
||||
|
||||
//M_StartMessage(M_GetText("Exiting will save the control changes\nfor this Profile.\nIs this okay?\n\nPress (A) to confirm or (B) to cancel"), 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;
|
||||
(void)ch;
|
||||
|
||||
// By default, accept all inputs.
|
||||
if (optionsmenu.trycontroller)
|
||||
{
|
||||
if (menucmd[pid].dpad_ud || menucmd[pid].dpad_lr || menucmd[pid].buttons)
|
||||
{
|
||||
optionsmenu.trycontroller = 5*TICRATE;
|
||||
}
|
||||
else
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
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...
|
||||
|
||||
if (M_MenuExtraPressed(pid))
|
||||
{
|
||||
// check if we're on a valid menu option...
|
||||
if (currentMenu->menuitems[itemOn].mvar1)
|
||||
{
|
||||
// clear controls for that key
|
||||
INT32 i;
|
||||
|
||||
for (i = 0; i < MAXINPUTMAPPING; i++)
|
||||
optionsmenu.tempcontrols[currentMenu->menuitems[itemOn].mvar1][i] = KEY_NULL;
|
||||
|
||||
S_StartSound(NULL, sfx_s3k66);
|
||||
}
|
||||
M_SetMenuDelay(pid);
|
||||
return true;
|
||||
}
|
||||
else if (M_MenuBackPressed(pid))
|
||||
{
|
||||
M_ProfileControlsConfirm(0);
|
||||
M_SetMenuDelay(pid);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void M_ProfileSetControl(INT32 ch)
|
||||
{
|
||||
INT32 controln = currentMenu->menuitems[itemOn].mvar1;
|
||||
UINT8 i;
|
||||
(void) ch;
|
||||
|
||||
optionsmenu.bindcontrol = 1; // Default to control #1
|
||||
|
||||
for (i = 0; i < MAXINPUTMAPPING; i++)
|
||||
{
|
||||
if (optionsmenu.tempcontrols[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;
|
||||
}
|
||||
|
||||
// Map the event to the profile.
|
||||
|
||||
#define KEYHOLDFOR 1
|
||||
void M_MapProfileControl(event_t *ev)
|
||||
{
|
||||
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.
|
||||
INT32 i;
|
||||
|
||||
//SetDeviceOnPress(); // Update cv_usejoystick
|
||||
|
||||
// Only consider keydown and joystick events to make sure we ignore ev_mouse and other events
|
||||
// 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 >= JOYAXES)
|
||||
{
|
||||
#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));
|
||||
|
||||
i = ev->data1;
|
||||
|
||||
if (i >= JOYANALOGS)
|
||||
{
|
||||
// The trigger axes are handled specially.
|
||||
i -= JOYANALOGS;
|
||||
|
||||
if (responsivelr)
|
||||
{
|
||||
c = KEY_AXIS1 + (JOYANALOGS * 4) + (i * 2);
|
||||
}
|
||||
else if (responsiveud)
|
||||
{
|
||||
c = KEY_AXIS1 + (JOYANALOGS * 4) + (i * 2) + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Actual analog sticks
|
||||
|
||||
// Only consider unambiguous assignment.
|
||||
if (responsivelr == responsiveud)
|
||||
return;
|
||||
|
||||
if (responsivelr)
|
||||
{
|
||||
if (ev->data2 < 0)
|
||||
{
|
||||
// Left
|
||||
c = KEY_AXIS1 + (i * 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Right
|
||||
c = KEY_AXIS1 + (i * 4) + 1;
|
||||
}
|
||||
}
|
||||
else //if (responsiveud)
|
||||
{
|
||||
if (ev->data3 < 0)
|
||||
{
|
||||
// Up
|
||||
c = KEY_AXIS1 + (i * 4) + 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Down
|
||||
c = KEY_AXIS1 + (i * 4) + 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// safety result
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
// Set menu delay regardless of what we're doing to avoid stupid stuff.
|
||||
M_SetMenuDelay(0);
|
||||
|
||||
// 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++)
|
||||
{
|
||||
if (optionsmenu.tempcontrols[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.tempcontrols[controln][where] = c;
|
||||
optionsmenu.bindcontrol = 0; // not binding anymore
|
||||
|
||||
// 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.
|
||||
{
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
/// \brief Video modes (resolutions)
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../i_video.h"
|
||||
#include "../s_sound.h"
|
||||
|
||||
menuitem_t OPTIONS_VideoModes[] = {
|
||||
|
||||
|
|
@ -24,3 +26,169 @@ menu_t OPTIONS_VideoModesDef = {
|
|||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
// setup video mode menu
|
||||
void M_VideoModeMenu(INT32 choice)
|
||||
{
|
||||
INT32 i, j, vdup, nummodes;
|
||||
UINT32 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();
|
||||
|
||||
// DOS does not skip mode 0, because mode 0 is ALWAYS present
|
||||
i = 0;
|
||||
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)
|
||||
{
|
||||
|
||||
const UINT8 pid = 0;
|
||||
(void)ch;
|
||||
|
||||
if (optionsmenu.vidm_testingmode > 0)
|
||||
{
|
||||
// change back to the previous mode quickly
|
||||
if (M_MenuBackPressed(pid))
|
||||
{
|
||||
setmodeneeded = optionsmenu.vidm_previousmode + 1;
|
||||
optionsmenu.vidm_testingmode = 0;
|
||||
}
|
||||
else if (M_MenuConfirmPressed(pid))
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
optionsmenu.vidm_testingmode = 0; // stop testing
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (menucmd[pid].dpad_ud > 0)
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
if (++optionsmenu.vidm_selected >= optionsmenu.vidm_nummodes)
|
||||
optionsmenu.vidm_selected = 0;
|
||||
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
||||
else if (menucmd[pid].dpad_ud < 0)
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
if (--optionsmenu.vidm_selected < 0)
|
||||
optionsmenu.vidm_selected = optionsmenu.vidm_nummodes - 1;
|
||||
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
||||
else if (menucmd[pid].dpad_lr < 0)
|
||||
{
|
||||
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;
|
||||
if (optionsmenu.vidm_selected >= optionsmenu.vidm_nummodes)
|
||||
optionsmenu.vidm_selected = optionsmenu.vidm_nummodes - 1;
|
||||
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
||||
else if (menucmd[pid].dpad_lr > 0)
|
||||
{
|
||||
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;
|
||||
if (optionsmenu.vidm_selected >= optionsmenu.vidm_nummodes)
|
||||
optionsmenu.vidm_selected = optionsmenu.vidm_nummodes - 1;
|
||||
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
||||
else if (M_MenuConfirmPressed(pid))
|
||||
{
|
||||
M_SetMenuDelay(pid);
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
else if (M_MenuBackPressed(pid))
|
||||
{
|
||||
M_SetMenuDelay(pid);
|
||||
if (currentMenu->prevMenu)
|
||||
M_SetupNextMenu(currentMenu->prevMenu, false);
|
||||
else
|
||||
M_ClearMenus(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -2,6 +2,7 @@
|
|||
/// \brief Local Play, gamemode selection menu
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../m_cond.h" // Condition Sets
|
||||
|
||||
menuitem_t PLAY_GamemodesMenu[] =
|
||||
{
|
||||
|
|
@ -21,3 +22,48 @@ menuitem_t PLAY_GamemodesMenu[] =
|
|||
};
|
||||
|
||||
menu_t PLAY_GamemodesDef = KARTGAMEMODEMENU(PLAY_GamemodesMenu, &PLAY_MainDef);
|
||||
|
||||
void M_SetupGametypeMenu(INT32 choice)
|
||||
{
|
||||
(void)choice;
|
||||
|
||||
PLAY_GamemodesDef.prevMenu = currentMenu;
|
||||
|
||||
// Battle and Capsules (and Special) disabled
|
||||
PLAY_GamemodesMenu[1].status = IT_DISABLED;
|
||||
PLAY_GamemodesMenu[2].status = IT_DISABLED;
|
||||
PLAY_GamemodesMenu[3].status = IT_DISABLED;
|
||||
|
||||
if (cv_splitplayers.value > 1)
|
||||
{
|
||||
// Re-add Battle
|
||||
PLAY_GamemodesMenu[1].status = IT_STRING | IT_CALL;
|
||||
}
|
||||
else
|
||||
{
|
||||
boolean anyunlocked = false;
|
||||
|
||||
if (M_SecretUnlocked(SECRET_BREAKTHECAPSULES, true))
|
||||
{
|
||||
// Re-add Capsules
|
||||
PLAY_GamemodesMenu[2].status = IT_STRING | IT_CALL;
|
||||
anyunlocked = true;
|
||||
}
|
||||
|
||||
if (M_SecretUnlocked(SECRET_SPECIALATTACK, true))
|
||||
{
|
||||
// Re-add Special
|
||||
PLAY_GamemodesMenu[3].status = IT_STRING | IT_CALL;
|
||||
anyunlocked = true;
|
||||
}
|
||||
|
||||
if (!anyunlocked)
|
||||
{
|
||||
// Only one non-Back entry, let's skip straight to Race.
|
||||
M_SetupRaceMenu(-1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
M_SetupNextMenu(&PLAY_GamemodesDef, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
/// \brief Race Mode Menu
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../m_cond.h" // Condition Sets
|
||||
|
||||
menuitem_t PLAY_RaceGamemodesMenu[] =
|
||||
{
|
||||
|
|
@ -18,3 +19,22 @@ menuitem_t PLAY_RaceGamemodesMenu[] =
|
|||
};
|
||||
|
||||
menu_t PLAY_RaceGamemodesDef = KARTGAMEMODEMENU(PLAY_RaceGamemodesMenu, &PLAY_GamemodesDef);
|
||||
|
||||
void M_SetupRaceMenu(INT32 choice)
|
||||
{
|
||||
(void)choice;
|
||||
|
||||
PLAY_RaceGamemodesDef.prevMenu = currentMenu;
|
||||
|
||||
// Time Attack disabled
|
||||
PLAY_RaceGamemodesMenu[2].status = IT_DISABLED;
|
||||
|
||||
// Time Attack is 1P only
|
||||
if (cv_splitplayers.value <= 1
|
||||
&& M_SecretUnlocked(SECRET_TIMEATTACK, true))
|
||||
{
|
||||
PLAY_RaceGamemodesMenu[2].status = IT_STRING | IT_CALL;
|
||||
}
|
||||
|
||||
M_SetupNextMenu(&PLAY_RaceGamemodesDef, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
/// \brief difficulty selection -- see drace_e
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../m_cond.h" // Condition Sets
|
||||
|
||||
menuitem_t PLAY_RaceDifficulty[] =
|
||||
{
|
||||
|
|
@ -45,3 +46,65 @@ menu_t PLAY_RaceDifficultyDef = {
|
|||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
consvar_t cv_dummygpdifficulty = CVAR_INIT ("dummygpdifficulty", "Normal", CV_HIDDEN, gpdifficulty_cons_t, 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 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"},
|
||||
{10, "Lv.10"},
|
||||
{11, "Lv.11"},
|
||||
{12, "Lv.12"},
|
||||
{13, "Lv.MAX"},
|
||||
{0, NULL}
|
||||
};
|
||||
consvar_t cv_dummymatchbots = CVAR_INIT ("dummymatchbots", "Off", CV_HIDDEN, dummymatchbots_cons_t, NULL);
|
||||
|
||||
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;
|
||||
|
||||
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[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[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, false))
|
||||
{
|
||||
PLAY_RaceDifficulty[drace_encore].status = IT_STRING2|IT_CVAR; // Encore on/off
|
||||
}
|
||||
|
||||
M_SetupNextMenu(&PLAY_RaceDifficultyDef, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,12 @@
|
|||
/// \brief Race Time Attack Menu
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../r_local.h" // SplitScreen_OnChange
|
||||
#include "../s_sound.h"
|
||||
#include "../f_finale.h" // F_WipeStartScreen
|
||||
#include "../v_video.h"
|
||||
#include "../d_main.h" // srb2home
|
||||
#include "../m_misc.h" // M_MkdirEach
|
||||
|
||||
// see ta_e
|
||||
menuitem_t PLAY_TimeAttack[] =
|
||||
|
|
@ -114,3 +120,91 @@ menu_t PLAY_TAGhostsDef = {
|
|||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
// autorecord demos for time attack
|
||||
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);
|
||||
|
||||
// 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;
|
||||
char nameofdemo[256];
|
||||
|
||||
(void)choice;
|
||||
|
||||
modeattacking = ATTACKING_TIME;
|
||||
|
||||
if ((gametypes[levellist.newgametype]->rules & GTR_CIRCUIT)
|
||||
&& (mapheaderinfo[levellist.choosemap]->numlaps != 1))
|
||||
{
|
||||
modeattacking |= ATTACKING_LAP;
|
||||
}
|
||||
|
||||
// Still need to reset devmode
|
||||
cht_debug = 0;
|
||||
emeralds = 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(levellist.newgametype, false);
|
||||
|
||||
gpath = va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s",
|
||||
srb2home, timeattackfolder);
|
||||
M_MkdirEach(gpath, M_PathParts(gpath) - 3, 0755);
|
||||
|
||||
strcat(gpath, PATHSEP);
|
||||
strcat(gpath, 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,3 +30,55 @@ menu_t PLAY_MP_OptSelectDef = {
|
|||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
struct mpmenu_s mpmenu;
|
||||
|
||||
// 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 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 UINT32 forbidden = GTR_FORBIDMP;
|
||||
|
||||
(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
|
||||
|
||||
// Guarantee menugametype is good
|
||||
M_NextMenuGametype(forbidden);
|
||||
M_PrevMenuGametype(forbidden);
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
/// \brief MULTIPLAYER HOST SCREEN -- see mhost_e
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../s_sound.h"
|
||||
|
||||
// MULTIPLAYER HOST SCREEN -- see mhost_e
|
||||
menuitem_t PLAY_MP_Host[] =
|
||||
|
|
@ -39,3 +40,73 @@ menu_t PLAY_MP_HostDef = {
|
|||
M_MPResetOpts,
|
||||
NULL
|
||||
};
|
||||
|
||||
void M_MPHostInit(INT32 choice)
|
||||
{
|
||||
|
||||
(void)choice;
|
||||
mpmenu.modewinextend[0][0] = 1;
|
||||
M_SetupNextMenu(&PLAY_MP_HostDef, true);
|
||||
itemOn = mhost_go;
|
||||
}
|
||||
|
||||
void M_HandleHostMenuGametype(INT32 choice)
|
||||
{
|
||||
const UINT8 pid = 0;
|
||||
const UINT32 forbidden = GTR_FORBIDMP;
|
||||
|
||||
(void)choice;
|
||||
|
||||
if (M_MenuBackPressed(pid))
|
||||
{
|
||||
M_GoBack(0);
|
||||
M_SetMenuDelay(pid);
|
||||
return;
|
||||
}
|
||||
else if (menucmd[pid].dpad_lr > 0 || M_MenuConfirmPressed(pid))
|
||||
{
|
||||
M_NextMenuGametype(forbidden);
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
else if (menucmd[pid].dpad_lr < 0)
|
||||
{
|
||||
M_PrevMenuGametype(forbidden);
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
||||
if (menucmd[pid].dpad_ud > 0)
|
||||
{
|
||||
M_NextOpt();
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
else if (menucmd[pid].dpad_ud < 0)
|
||||
{
|
||||
M_PrevOpt();
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
}
|
||||
|
||||
void M_MPSetupNetgameMapSelect(INT32 choice)
|
||||
{
|
||||
(void)choice;
|
||||
|
||||
// Yep, we'll be starting a netgame.
|
||||
levellist.netgame = true;
|
||||
// Make sure we reset those
|
||||
levellist.levelsearch.timeattack = false;
|
||||
levellist.levelsearch.checklocked = true;
|
||||
cupgrid.grandprix = false;
|
||||
|
||||
// okay this is REALLY stupid but this fixes the host menu re-folding on itself when we go back.
|
||||
mpmenu.modewinextend[0][0] = 1;
|
||||
|
||||
if (!M_LevelListFromGametype(menugametype))
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3kb2);
|
||||
M_StartMessage(va("No levels available for\n%s Mode!\n\nPress (B)\n", gametypes[menugametype]->name), NULL, MM_NOTHING);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@
|
|||
/// \brief MULTIPLAYER JOIN BY IP
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../v_video.h"
|
||||
#include "../i_system.h" // I_OsPolling
|
||||
#include "../i_video.h" // I_UpdateNoBlit
|
||||
#include "../m_misc.h" // NUMLOGIP
|
||||
|
||||
menuitem_t PLAY_MP_JoinIP[] =
|
||||
{
|
||||
|
|
@ -41,3 +45,66 @@ menu_t PLAY_MP_JoinIPDef = {
|
|||
M_MPResetOpts,
|
||||
M_JoinIPInputs
|
||||
};
|
||||
|
||||
consvar_t cv_dummyip = CVAR_INIT ("dummyip", "", CV_HIDDEN, NULL, NULL);
|
||||
|
||||
void M_MPJoinIPInit(INT32 choice)
|
||||
{
|
||||
|
||||
(void)choice;
|
||||
mpmenu.modewinextend[2][0] = 1;
|
||||
M_SetupNextMenu(&PLAY_MP_JoinIPDef, true);
|
||||
}
|
||||
|
||||
// Attempts to join a given IP from the menu.
|
||||
void M_JoinIP(const char *ipa)
|
||||
{
|
||||
if (*(ipa) == '\0') // Jack shit
|
||||
{
|
||||
M_StartMessage("Please specify an address.\n", NULL, MM_NOTHING);
|
||||
return;
|
||||
}
|
||||
|
||||
COM_BufAddText(va("connect \"%s\"\n", ipa));
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
boolean M_JoinIPInputs(INT32 ch)
|
||||
{
|
||||
|
||||
const UINT8 pid = 0;
|
||||
(void) ch;
|
||||
|
||||
if (itemOn == 1) // connect field
|
||||
{
|
||||
// enter: connect
|
||||
if (M_MenuConfirmPressed(pid))
|
||||
{
|
||||
M_JoinIP(cv_dummyip.string);
|
||||
M_SetMenuDelay(pid);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
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])
|
||||
M_JoinIP(joinedIPlist[index][0]);
|
||||
else
|
||||
S_StartSound(NULL, sfx_lose);
|
||||
|
||||
return true; // eat input.
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
66
src/menus/play-online-room-select.c
Normal file
66
src/menus/play-online-room-select.c
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/// \file menus/play-online-room-select.c
|
||||
/// \brief MULTIPLAYER ROOM SELECT MENU
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../s_sound.h"
|
||||
|
||||
menuitem_t PLAY_MP_RoomSelect[] =
|
||||
{
|
||||
{IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, {.routine = 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,
|
||||
0, 0,
|
||||
M_DrawMPRoomSelect,
|
||||
M_MPRoomSelectTick,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
void M_MPRoomSelect(INT32 choice)
|
||||
{
|
||||
const UINT8 pid = 0;
|
||||
(void) choice;
|
||||
|
||||
if (menucmd[pid].dpad_lr)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
else if (M_MenuConfirmPressed(pid))
|
||||
{
|
||||
M_ServersMenu(0);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
}
|
||||
|
||||
void M_MPRoomSelectTick(void)
|
||||
{
|
||||
mpmenu.ticker++;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -1,30 +1,15 @@
|
|||
/// \file menus/play-online-server-browser.c
|
||||
/// \brief Online server, CORE / MODDED
|
||||
/// \brief MULTIPLAYER ROOM FETCH / REFRESH THREADS
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../v_video.h"
|
||||
#include "../i_system.h" // I_OsPolling
|
||||
#include "../i_video.h" // I_UpdateNoBlit
|
||||
|
||||
// MULTIPLAYER ROOM SELECT (CORE / MODDED)
|
||||
menuitem_t PLAY_MP_RoomSelect[] =
|
||||
{
|
||||
{IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, {.routine = M_MPRoomSelect}, 0, 0},
|
||||
};
|
||||
#ifdef SERVERLISTDEBUG
|
||||
#include "../m_random.h"
|
||||
#endif
|
||||
|
||||
menu_t PLAY_MP_RoomSelectDef = {
|
||||
sizeof (PLAY_MP_RoomSelect) / sizeof (menuitem_t),
|
||||
&PLAY_MP_OptSelectDef,
|
||||
0,
|
||||
PLAY_MP_RoomSelect,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
M_DrawMPRoomSelect,
|
||||
M_MPRoomSelectTick,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
// SERVER BROWSER
|
||||
menuitem_t PLAY_MP_ServerBrowser[] =
|
||||
{
|
||||
|
||||
|
|
@ -51,3 +36,459 @@ menu_t PLAY_MP_ServerBrowserDef = {
|
|||
NULL,
|
||||
M_ServerBrowserInputs
|
||||
};
|
||||
|
||||
static CV_PossibleValue_t serversort_cons_t[] = {
|
||||
{0,"Ping"},
|
||||
{1,"AVG. Power Level"},
|
||||
{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);
|
||||
|
||||
// for server fetch threads...
|
||||
M_waiting_mode_t m_waiting_mode = M_NOT_WAITING;
|
||||
|
||||
// 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();
|
||||
|
||||
}
|
||||
|
||||
#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(PR_UNDEFINED, 500, 1500);
|
||||
serverlist[i].info.time = P_RandomRange(PR_UNDEFINED, 1, 8); // ping
|
||||
|
||||
strcpy(serverlist[i].info.servername, va("Serv %d", i+1));
|
||||
|
||||
strcpy(serverlist[i].info.gametypename, i & 1 ? "Race" : "Battle");
|
||||
|
||||
P_RandomRange(PR_UNDEFINED, 0, 5); // change results...
|
||||
serverlist[i].info.kartvars = P_RandomRange(PR_UNDEFINED, 0, 3) & SV_SPEEDMASK;
|
||||
|
||||
serverlist[i].info.modifiedgame = P_RandomRange(PR_UNDEFINED, 0, 1);
|
||||
|
||||
CONS_Printf("Serv %d | %d...\n", i, serverlist[i].info.modifiedgame);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SERVERLISTDEBUG
|
||||
|
||||
// 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
target_sources(SRB2SDL2 PRIVATE
|
||||
cup-select.c
|
||||
explosions.c
|
||||
level-select.c
|
||||
gametype.c
|
||||
manual.c
|
||||
message-box.c
|
||||
pause-game.c
|
||||
pause-replay.c
|
||||
virtual-keyboard.c
|
||||
)
|
||||
|
|
|
|||
195
src/menus/transient/cup-select.c
Normal file
195
src/menus/transient/cup-select.c
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
/// \file menus/transient/cup-select.c
|
||||
/// \brief Cup Select
|
||||
|
||||
#include "../../k_menu.h"
|
||||
#include "../../s_sound.h"
|
||||
#include "../../f_finale.h" // F_WipeStartScreen
|
||||
#include "../../v_video.h"
|
||||
#include "../../k_grandprix.h"
|
||||
#include "../../r_local.h" // SplitScreen_OnChange
|
||||
|
||||
menuitem_t PLAY_CupSelect[] =
|
||||
{
|
||||
{IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, {.routine = M_CupSelectHandler}, 0, 0},
|
||||
};
|
||||
|
||||
menu_t PLAY_CupSelectDef = {
|
||||
sizeof(PLAY_CupSelect) / sizeof(menuitem_t),
|
||||
&PLAY_RaceGamemodesDef,
|
||||
0,
|
||||
PLAY_CupSelect,
|
||||
0, 0,
|
||||
0, 0,
|
||||
2, 5,
|
||||
M_DrawCupSelect,
|
||||
M_CupSelectTick,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
struct cupgrid_s cupgrid;
|
||||
|
||||
void M_CupSelectHandler(INT32 choice)
|
||||
{
|
||||
const UINT8 pid = 0;
|
||||
|
||||
(void)choice;
|
||||
|
||||
if (menucmd[pid].dpad_lr > 0)
|
||||
{
|
||||
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;
|
||||
if (cupgrid.pageno == 0)
|
||||
cupgrid.pageno = cupgrid.numpages-1;
|
||||
else
|
||||
cupgrid.pageno--;
|
||||
}
|
||||
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_MenuConfirmPressed(pid) /*|| M_MenuButtonPressed(pid, MBT_START)*/)
|
||||
{
|
||||
INT16 count;
|
||||
cupheader_t *newcup = cupgrid.builtgrid[CUPMENU_CURSORID];
|
||||
cupheader_t *oldcup = levellist.levelsearch.cup;
|
||||
|
||||
M_SetMenuDelay(pid);
|
||||
|
||||
levellist.levelsearch.cup = newcup;
|
||||
count = M_CountLevelsToShowInList(&levellist.levelsearch);
|
||||
|
||||
if ((!newcup)
|
||||
|| (count <= 0)
|
||||
|| (cupgrid.grandprix == true && newcup->cachedlevels[0] == NEXTMAP_INVALID))
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3kb2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cupgrid.grandprix == true)
|
||||
{
|
||||
INT32 levelNum;
|
||||
UINT8 ssplayers = cv_splitplayers.value-1;
|
||||
|
||||
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));
|
||||
|
||||
if (cv_maxconnections.value < ssplayers+1)
|
||||
CV_SetValue(&cv_maxconnections, ssplayers+1);
|
||||
|
||||
if (splitscreen != ssplayers)
|
||||
{
|
||||
splitscreen = ssplayers;
|
||||
SplitScreen_OnChange();
|
||||
}
|
||||
|
||||
// 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(levellist.newgametype, levellist.netgame);
|
||||
}
|
||||
|
||||
levelNum = grandprixinfo.cup->cachedlevels[0];
|
||||
|
||||
D_MapChange(
|
||||
levelNum + 1,
|
||||
GT_RACE,
|
||||
grandprixinfo.encore,
|
||||
true,
|
||||
1,
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
M_ClearMenus(true);
|
||||
}
|
||||
else if (count == 1)
|
||||
{
|
||||
PLAY_TimeAttackDef.transitionID = currentMenu->transitionID+1;
|
||||
M_LevelSelected(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep cursor position if you select the same cup again, reset if it's a different cup
|
||||
if (oldcup != newcup || levellist.cursor >= count)
|
||||
{
|
||||
levellist.cursor = 0;
|
||||
}
|
||||
|
||||
M_LevelSelectScrollDest();
|
||||
levellist.y = levellist.dest;
|
||||
|
||||
M_SetupNextMenu(&PLAY_LevelSelectDef, false);
|
||||
S_StartSound(NULL, sfx_s3k63);
|
||||
}
|
||||
}
|
||||
else if (M_MenuBackPressed(pid))
|
||||
{
|
||||
M_SetMenuDelay(pid);
|
||||
|
||||
if (currentMenu->prevMenu)
|
||||
M_SetupNextMenu(currentMenu->prevMenu, false);
|
||||
else
|
||||
M_ClearMenus(true);
|
||||
}
|
||||
}
|
||||
|
||||
void M_CupSelectTick(void)
|
||||
{
|
||||
cupgrid.previewanim++;
|
||||
}
|
||||
63
src/menus/transient/explosions.c
Normal file
63
src/menus/transient/explosions.c
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/// \file menus/transient/explosions.c
|
||||
/// \brief Explosions used on the character select grid and
|
||||
/// challenges grid.
|
||||
|
||||
#include "../../k_menu.h"
|
||||
#include "../../m_cond.h" // Condition Sets
|
||||
|
||||
struct setup_explosions_s setup_explosions[CSEXPLOSIONS];
|
||||
|
||||
void M_SetupReadyExplosions(boolean charsel, UINT16 basex, UINT16 basey, UINT16 color)
|
||||
{
|
||||
UINT8 i, j;
|
||||
UINT8 e = 0;
|
||||
UINT16 maxx = (charsel ? 9 : gamedata->challengegridwidth);
|
||||
UINT16 maxy = (charsel ? 9 : CHALLENGEGRIDHEIGHT);
|
||||
|
||||
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++)
|
||||
{
|
||||
INT16 x = basex, y = basey;
|
||||
|
||||
switch (j)
|
||||
{
|
||||
case 0: x += offset; break;
|
||||
case 1: x -= offset; break;
|
||||
case 2: y += offset; break;
|
||||
case 3: y -= offset; break;
|
||||
}
|
||||
|
||||
if ((y < 0 || y >= maxy))
|
||||
continue;
|
||||
|
||||
if (charsel || !challengegridloops)
|
||||
{
|
||||
if (x < 0 || x >= maxx)
|
||||
continue;
|
||||
}
|
||||
|
||||
setup_explosions[e].tics = t;
|
||||
setup_explosions[e].color = color;
|
||||
setup_explosions[e].x = x;
|
||||
setup_explosions[e].y = y;
|
||||
|
||||
while (setup_explosions[e].tics)
|
||||
{
|
||||
e++;
|
||||
if (e == CSEXPLOSIONS)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/menus/transient/gametype.c
Normal file
34
src/menus/transient/gametype.c
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/// \file menus/transient/gametype.c
|
||||
/// \brief Gametype selection
|
||||
|
||||
#include "../../k_menu.h"
|
||||
|
||||
INT16 menugametype = GT_RACE;
|
||||
|
||||
void M_NextMenuGametype(UINT32 forbidden)
|
||||
{
|
||||
const INT16 currentmenugametype = menugametype;
|
||||
do
|
||||
{
|
||||
menugametype++;
|
||||
if (menugametype >= numgametypes)
|
||||
menugametype = 0;
|
||||
|
||||
if (!(gametypes[menugametype]->rules & forbidden))
|
||||
break;
|
||||
} while (menugametype != currentmenugametype);
|
||||
}
|
||||
|
||||
void M_PrevMenuGametype(UINT32 forbidden)
|
||||
{
|
||||
const INT16 currentmenugametype = menugametype;
|
||||
do
|
||||
{
|
||||
if (menugametype == 0)
|
||||
menugametype = numgametypes;
|
||||
menugametype--;
|
||||
|
||||
if (!(gametypes[menugametype]->rules & forbidden))
|
||||
break;
|
||||
} while (menugametype != currentmenugametype);
|
||||
}
|
||||
|
|
@ -2,26 +2,12 @@
|
|||
/// \brief Level Select
|
||||
|
||||
#include "../../k_menu.h"
|
||||
|
||||
menuitem_t PLAY_CupSelect[] =
|
||||
{
|
||||
{IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, {.routine = M_CupSelectHandler}, 0, 0},
|
||||
};
|
||||
|
||||
menu_t PLAY_CupSelectDef = {
|
||||
sizeof(PLAY_CupSelect) / sizeof(menuitem_t),
|
||||
&PLAY_RaceGamemodesDef,
|
||||
0,
|
||||
PLAY_CupSelect,
|
||||
0, 0,
|
||||
0, 0,
|
||||
2, 5,
|
||||
M_DrawCupSelect,
|
||||
M_CupSelectTick,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
#include "../../m_cond.h" // Condition Sets
|
||||
#include "../../z_zone.h"
|
||||
#include "../../s_sound.h"
|
||||
#include "../../r_local.h" // SplitScreen_OnChange
|
||||
#include "../../f_finale.h" // F_WipeStartScreen
|
||||
#include "../../v_video.h"
|
||||
|
||||
menuitem_t PLAY_LevelSelect[] =
|
||||
{
|
||||
|
|
@ -42,3 +28,524 @@ menu_t PLAY_LevelSelectDef = {
|
|||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
struct levellist_s levellist;
|
||||
|
||||
//
|
||||
// M_CanShowLevelInList
|
||||
//
|
||||
// Determines whether to show a given map in the various level-select lists.
|
||||
//
|
||||
boolean M_CanShowLevelInList(INT16 mapnum, levelsearch_t *levelsearch)
|
||||
{
|
||||
if (!levelsearch)
|
||||
return false;
|
||||
|
||||
if (mapnum >= nummapheaders)
|
||||
return false;
|
||||
|
||||
// Does the map exist?
|
||||
if (!mapheaderinfo[mapnum])
|
||||
return false;
|
||||
|
||||
// Does the map have a name?
|
||||
if (!mapheaderinfo[mapnum]->lvlttl[0])
|
||||
return false;
|
||||
|
||||
// Does the map have a LUMP?
|
||||
if (mapheaderinfo[mapnum]->lumpnum == LUMPERROR)
|
||||
return false;
|
||||
|
||||
// Check for TOL
|
||||
if (!(mapheaderinfo[mapnum]->typeoflevel & levelsearch->typeoflevel))
|
||||
return false;
|
||||
|
||||
// Should the map be hidden?
|
||||
if (mapheaderinfo[mapnum]->menuflags & LF2_HIDEINMENU)
|
||||
return false;
|
||||
|
||||
// I don't know why, but some may have exceptions.
|
||||
if (levelsearch->timeattack && (mapheaderinfo[mapnum]->menuflags & LF2_NOTIMEATTACK))
|
||||
return false;
|
||||
|
||||
// Don't permit cup when no cup requested (also no dupes in time attack)
|
||||
if (levelsearch->cupmode
|
||||
&& (levelsearch->timeattack || !levelsearch->cup)
|
||||
&& mapheaderinfo[mapnum]->cup != levelsearch->cup)
|
||||
return false;
|
||||
|
||||
// Finally, the most complex check: does the map have lock conditions?
|
||||
if (levelsearch->checklocked)
|
||||
{
|
||||
// Check for completion
|
||||
if ((mapheaderinfo[mapnum]->menuflags & LF2_FINISHNEEDED)
|
||||
&& !(mapheaderinfo[mapnum]->mapvisited & MV_BEATEN))
|
||||
return false;
|
||||
|
||||
// Check for unlock
|
||||
if (M_MapLocked(mapnum+1))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Survived our checks.
|
||||
return true;
|
||||
}
|
||||
|
||||
UINT16 M_CountLevelsToShowInList(levelsearch_t *levelsearch)
|
||||
{
|
||||
INT16 i, count = 0;
|
||||
|
||||
if (!levelsearch)
|
||||
return false;
|
||||
|
||||
if (levelsearch->cup)
|
||||
{
|
||||
if (levelsearch->checklocked && M_CupLocked(levelsearch->cup))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < CUPCACHE_MAX; i++)
|
||||
{
|
||||
if (!M_CanShowLevelInList(levelsearch->cup->cachedlevels[i], levelsearch))
|
||||
continue;
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
for (i = 0; i < nummapheaders; i++)
|
||||
if (M_CanShowLevelInList(i, levelsearch))
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
UINT16 M_GetFirstLevelInList(UINT8 *i, levelsearch_t *levelsearch)
|
||||
{
|
||||
INT16 mapnum = NEXTMAP_INVALID;
|
||||
|
||||
if (!levelsearch)
|
||||
return false;
|
||||
|
||||
if (levelsearch->cup)
|
||||
{
|
||||
if (levelsearch->checklocked && M_CupLocked(levelsearch->cup))
|
||||
{
|
||||
*i = CUPCACHE_MAX;
|
||||
return NEXTMAP_INVALID;
|
||||
}
|
||||
|
||||
*i = 0;
|
||||
mapnum = NEXTMAP_INVALID;
|
||||
for (; *i < CUPCACHE_MAX; (*i)++)
|
||||
{
|
||||
if (!M_CanShowLevelInList(levelsearch->cup->cachedlevels[*i], levelsearch))
|
||||
continue;
|
||||
mapnum = levelsearch->cup->cachedlevels[*i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (mapnum = 0; mapnum < nummapheaders; mapnum++)
|
||||
if (M_CanShowLevelInList(mapnum, levelsearch))
|
||||
break;
|
||||
}
|
||||
|
||||
return mapnum;
|
||||
}
|
||||
|
||||
UINT16 M_GetNextLevelInList(UINT16 mapnum, UINT8 *i, levelsearch_t *levelsearch)
|
||||
{
|
||||
if (!levelsearch)
|
||||
return false;
|
||||
|
||||
if (levelsearch->cup)
|
||||
{
|
||||
mapnum = NEXTMAP_INVALID;
|
||||
(*i)++;
|
||||
for (; *i < CUPCACHE_MAX; (*i)++)
|
||||
{
|
||||
if (!M_CanShowLevelInList(levelsearch->cup->cachedlevels[*i], levelsearch))
|
||||
continue;
|
||||
mapnum = levelsearch->cup->cachedlevels[*i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mapnum++;
|
||||
while (!M_CanShowLevelInList(mapnum, levelsearch) && mapnum < nummapheaders)
|
||||
mapnum++;
|
||||
}
|
||||
|
||||
return mapnum;
|
||||
}
|
||||
|
||||
void M_LevelSelectScrollDest(void)
|
||||
{
|
||||
UINT16 m = M_CountLevelsToShowInList(&levellist.levelsearch)-1;
|
||||
|
||||
levellist.dest = (6*levellist.cursor);
|
||||
|
||||
if (levellist.dest < 3)
|
||||
levellist.dest = 3;
|
||||
|
||||
if (levellist.dest > (6*m)-3)
|
||||
levellist.dest = (6*m)-3;
|
||||
}
|
||||
|
||||
// Builds the level list we'll be using from the gametype we're choosing and send us to the apropriate menu.
|
||||
boolean M_LevelListFromGametype(INT16 gt)
|
||||
{
|
||||
static boolean first = true;
|
||||
UINT8 temp = 0;
|
||||
|
||||
if (first || gt != levellist.newgametype || levellist.guessgt != MAXGAMETYPES)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
cupgrid.cappages = 0;
|
||||
cupgrid.builtgrid = NULL;
|
||||
}
|
||||
|
||||
levellist.newgametype = gt;
|
||||
|
||||
levellist.levelsearch.typeoflevel = G_TOLFlag(gt);
|
||||
if (levellist.levelsearch.timeattack == true && gt == GT_SPECIAL)
|
||||
{
|
||||
// Sneak in an extra.
|
||||
levellist.levelsearch.typeoflevel |= G_TOLFlag(GT_VERSUS);
|
||||
levellist.guessgt = gt;
|
||||
}
|
||||
else
|
||||
{
|
||||
levellist.guessgt = MAXGAMETYPES;
|
||||
}
|
||||
|
||||
levellist.levelsearch.cupmode = (!(gametypes[gt]->rules & GTR_NOCUPSELECT));
|
||||
levellist.levelsearch.cup = NULL;
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
// 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.levelsearch.cupmode)
|
||||
{
|
||||
levelsearch_t templevelsearch = levellist.levelsearch; // full copy
|
||||
size_t currentid = 0, highestunlockedid = 0;
|
||||
const size_t pagelen = sizeof(cupheader_t*) * (CUPMENU_COLUMNS * CUPMENU_ROWS);
|
||||
boolean foundany = false;
|
||||
|
||||
templevelsearch.cup = kartcupheaders;
|
||||
|
||||
#if 0
|
||||
// Make sure there's valid cups before going to this menu. -- rip sweet prince
|
||||
if (templevelsearch.cup == NULL)
|
||||
I_Error("Can you really call this a racing game, I didn't recieve any Cups on my pillow or anything");
|
||||
#endif
|
||||
|
||||
if (cupgrid.cappages == 0)
|
||||
{
|
||||
cupgrid.cappages = 2;
|
||||
cupgrid.builtgrid = Z_Calloc(
|
||||
cupgrid.cappages * pagelen,
|
||||
PU_STATIC,
|
||||
NULL);
|
||||
|
||||
if (!cupgrid.builtgrid)
|
||||
{
|
||||
I_Error("M_LevelListFromGametype: Not enough memory to allocate builtgrid");
|
||||
}
|
||||
}
|
||||
memset(cupgrid.builtgrid, 0, cupgrid.cappages * pagelen);
|
||||
|
||||
while (templevelsearch.cup)
|
||||
{
|
||||
templevelsearch.checklocked = false;
|
||||
if (!M_CountLevelsToShowInList(&templevelsearch))
|
||||
{
|
||||
// No valid maps, skip.
|
||||
templevelsearch.cup = templevelsearch.cup->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
foundany = true;
|
||||
|
||||
if ((currentid * sizeof(cupheader_t*)) >= cupgrid.cappages * pagelen)
|
||||
{
|
||||
// Double the size of the buffer, and clear the other stuff.
|
||||
const size_t firstlen = cupgrid.cappages * pagelen;
|
||||
cupgrid.builtgrid = Z_Realloc(cupgrid.builtgrid,
|
||||
firstlen * 2,
|
||||
PU_STATIC, NULL);
|
||||
|
||||
if (!cupgrid.builtgrid)
|
||||
{
|
||||
I_Error("M_LevelListFromGametype: Not enough memory to reallocate builtgrid");
|
||||
}
|
||||
|
||||
memset(cupgrid.builtgrid + firstlen, 0, firstlen);
|
||||
cupgrid.cappages *= 2;
|
||||
}
|
||||
|
||||
cupgrid.builtgrid[currentid] = templevelsearch.cup;
|
||||
|
||||
templevelsearch.checklocked = true;
|
||||
if (M_GetFirstLevelInList(&temp, &templevelsearch) != NEXTMAP_INVALID)
|
||||
{
|
||||
highestunlockedid = currentid;
|
||||
if (Playing() && mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->cup == templevelsearch.cup)
|
||||
{
|
||||
cupgrid.x = currentid % CUPMENU_COLUMNS;
|
||||
cupgrid.y = (currentid / CUPMENU_COLUMNS) % CUPMENU_ROWS;
|
||||
cupgrid.pageno = currentid / (CUPMENU_COLUMNS * CUPMENU_ROWS);
|
||||
}
|
||||
}
|
||||
|
||||
currentid++;
|
||||
templevelsearch.cup = templevelsearch.cup->next;
|
||||
}
|
||||
|
||||
if (foundany == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
cupgrid.numpages = (highestunlockedid / (CUPMENU_COLUMNS * CUPMENU_ROWS)) + 1;
|
||||
if (cupgrid.pageno >= cupgrid.numpages)
|
||||
{
|
||||
cupgrid.pageno = 0;
|
||||
}
|
||||
|
||||
PLAY_CupSelectDef.prevMenu = currentMenu;
|
||||
PLAY_LevelSelectDef.prevMenu = &PLAY_CupSelectDef;
|
||||
M_SetupNextMenu(&PLAY_CupSelectDef, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Okay, just a list of maps then.
|
||||
|
||||
if (M_GetFirstLevelInList(&temp, &levellist.levelsearch) == NEXTMAP_INVALID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reset position properly if you go back & forth between gametypes
|
||||
if (levellist.levelsearch.cup)
|
||||
{
|
||||
levellist.cursor = 0;
|
||||
levellist.levelsearch.cup = NULL;
|
||||
}
|
||||
|
||||
M_LevelSelectScrollDest();
|
||||
levellist.y = levellist.dest;
|
||||
|
||||
PLAY_LevelSelectDef.prevMenu = currentMenu;
|
||||
M_SetupNextMenu(&PLAY_LevelSelectDef, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Init level select for use in local play using the last choice we made.
|
||||
// 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)
|
||||
{
|
||||
INT32 gt = currentMenu->menuitems[itemOn].mvar2;
|
||||
|
||||
(void)choice;
|
||||
|
||||
// Make sure this is reset as we'll only be using this function for offline games!
|
||||
levellist.netgame = false;
|
||||
levellist.levelsearch.checklocked = true;
|
||||
|
||||
switch (currentMenu->menuitems[itemOn].mvar1)
|
||||
{
|
||||
case 0:
|
||||
cupgrid.grandprix = false;
|
||||
levellist.levelsearch.timeattack = false;
|
||||
break;
|
||||
case 1:
|
||||
cupgrid.grandprix = false;
|
||||
levellist.levelsearch.timeattack = true;
|
||||
break;
|
||||
case 2:
|
||||
cupgrid.grandprix = true;
|
||||
levellist.levelsearch.timeattack = false;
|
||||
break;
|
||||
default:
|
||||
CONS_Alert(CONS_WARNING, "Bad level select init\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (gt == -1)
|
||||
{
|
||||
gt = menugametype;
|
||||
}
|
||||
|
||||
if (!M_LevelListFromGametype(gt))
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3kb2);
|
||||
M_StartMessage(va("No levels available for\n%s Mode!\n\nPress (B)\n", gametypes[gt]->name), NULL, MM_NOTHING);
|
||||
}
|
||||
}
|
||||
|
||||
void M_LevelSelected(INT16 add)
|
||||
{
|
||||
UINT8 i = 0;
|
||||
INT16 map = M_GetFirstLevelInList(&i, &levellist.levelsearch);
|
||||
|
||||
while (add > 0)
|
||||
{
|
||||
map = M_GetNextLevelInList(map, &i, &levellist.levelsearch);
|
||||
|
||||
if (map >= nummapheaders)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
add--;
|
||||
}
|
||||
|
||||
if (map >= nummapheaders)
|
||||
{
|
||||
// This shouldn't happen
|
||||
return;
|
||||
}
|
||||
|
||||
levellist.choosemap = map;
|
||||
|
||||
if (levellist.levelsearch.timeattack)
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3k63);
|
||||
|
||||
if (levellist.guessgt != MAXGAMETYPES)
|
||||
levellist.newgametype = G_GuessGametypeByTOL(levellist.levelsearch.typeoflevel);
|
||||
|
||||
PLAY_TimeAttackDef.lastOn = ta_start;
|
||||
PLAY_TimeAttackDef.prevMenu = currentMenu;
|
||||
M_SetupNextMenu(&PLAY_TimeAttackDef, false);
|
||||
}
|
||||
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
|
||||
cht_debug = 0;
|
||||
|
||||
if (demo.playback)
|
||||
G_StopDemo();
|
||||
if (metalrecording)
|
||||
G_StopMetalDemo();
|
||||
|
||||
/*if (levellist.choosemap == 0)
|
||||
levellist.choosemap = G_RandMap(G_TOLFlag(levellist.newgametype), -1, 0, 0, false, NULL);*/
|
||||
|
||||
if (cv_maxconnections.value < ssplayers+1)
|
||||
CV_SetValue(&cv_maxconnections, 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(levellist.newgametype, levellist.netgame);
|
||||
|
||||
CV_StealthSet(&cv_kartbot, cv_dummymatchbots.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_kartencore.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);
|
||||
}
|
||||
}
|
||||
|
||||
void M_LevelSelectHandler(INT32 choice)
|
||||
{
|
||||
INT16 maxlevels = M_CountLevelsToShowInList(&levellist.levelsearch);
|
||||
const UINT8 pid = 0;
|
||||
|
||||
(void)choice;
|
||||
|
||||
if (levellist.y != levellist.dest)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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_MenuConfirmPressed(pid) /*|| M_MenuButtonPressed(pid, MBT_START)*/)
|
||||
{
|
||||
M_SetMenuDelay(pid);
|
||||
|
||||
PLAY_TimeAttackDef.transitionID = currentMenu->transitionID;
|
||||
M_LevelSelected(levellist.cursor);
|
||||
}
|
||||
else if (M_MenuBackPressed(pid))
|
||||
{
|
||||
M_SetMenuDelay(pid);
|
||||
|
||||
if (currentMenu->prevMenu)
|
||||
M_SetupNextMenu(currentMenu->prevMenu, false);
|
||||
else
|
||||
M_ClearMenus(true);
|
||||
}
|
||||
}
|
||||
|
||||
void M_LevelSelectTick(void)
|
||||
{
|
||||
|
||||
INT16 dist = levellist.dest - levellist.y;
|
||||
|
||||
if (abs(dist) == 1) // cheating to avoid off by 1 errors with divisions.
|
||||
levellist.y = levellist.dest;
|
||||
else
|
||||
levellist.y += dist/2;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
/// \brief Manual
|
||||
|
||||
#include "../../k_menu.h"
|
||||
#include "../../s_sound.h"
|
||||
|
||||
menuitem_t MISC_Manual[] = {
|
||||
{IT_NOTHING | IT_KEYHANDLER, "MANUAL00", NULL, NULL, {.routine = M_HandleImageDef}, 0, 0},
|
||||
|
|
@ -21,3 +22,52 @@ menuitem_t MISC_Manual[] = {
|
|||
};
|
||||
|
||||
menu_t MISC_ManualDef = IMAGEDEF(MISC_Manual);
|
||||
|
||||
// Handles the ImageDefs. Just a specialized function that
|
||||
// uses left and right movement.
|
||||
void M_HandleImageDef(INT32 choice)
|
||||
{
|
||||
const UINT8 pid = 0;
|
||||
boolean exitmenu = false;
|
||||
(void) choice;
|
||||
|
||||
if (menucmd[pid].dpad_lr > 0)
|
||||
{
|
||||
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_MenuConfirmPressed(pid) || M_MenuButtonPressed(pid, MBT_X) || M_MenuButtonPressed(pid, MBT_Y))
|
||||
{
|
||||
exitmenu = true;
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
||||
|
||||
if (exitmenu)
|
||||
{
|
||||
if (currentMenu->prevMenu)
|
||||
M_SetupNextMenu(currentMenu->prevMenu, false);
|
||||
else
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
200
src/menus/transient/message-box.c
Normal file
200
src/menus/transient/message-box.c
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
// \file menus/transient/message-box.c
|
||||
// \brief MESSAGE BOX (aka: a hacked, cobbled together menu)
|
||||
|
||||
#include "../../k_menu.h"
|
||||
#include "../../z_zone.h"
|
||||
|
||||
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, // extra1, extra2
|
||||
0, 0, // transition tics
|
||||
NULL, // drawing routine ->
|
||||
NULL, // ticker routine
|
||||
NULL, // init routine
|
||||
NULL, // quit routine
|
||||
NULL // input routine
|
||||
};
|
||||
|
||||
// message prompt struct
|
||||
struct menumessage_s menumessage;
|
||||
|
||||
//
|
||||
// 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)
|
||||
{
|
||||
const UINT8 pid = 0;
|
||||
size_t max = 0, start = 0, strlines = 0, i;
|
||||
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, etc. but who cares.
|
||||
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 if (message[i] & 0x80)
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(menumessage.message, string, MAXMENUMESSAGE);
|
||||
menumessage.flags = itemtype;
|
||||
*(void**)&menumessage.routine = routine;
|
||||
menumessage.fadetimer = (gamestate == GS_WAITINGPLAYERS) ? 9 : 1;
|
||||
menumessage.active = true;
|
||||
|
||||
start = 0;
|
||||
max = 0;
|
||||
|
||||
if (!routine || menumessage.flags == MM_NOTHING)
|
||||
{
|
||||
menumessage.flags = MM_NOTHING;
|
||||
menumessage.routine = M_StopMessage;
|
||||
}
|
||||
|
||||
// event routine
|
||||
if (menumessage.flags == MM_EVENTHANDLER)
|
||||
{
|
||||
*(void**)&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++)
|
||||
{
|
||||
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;
|
||||
if (i > max)
|
||||
max = i;
|
||||
}
|
||||
}
|
||||
|
||||
menumessage.x = (INT16)((BASEVIDWIDTH - 8*max-16)/2);
|
||||
menumessage.y = (INT16)((BASEVIDHEIGHT - M_StringHeight(message))/2);
|
||||
|
||||
menumessage.m = (INT16)((strlines<<8)+max);
|
||||
|
||||
M_SetMenuDelay(pid); // Set menu delay to avoid setting off any of the handlers.
|
||||
}
|
||||
|
||||
void M_StopMessage(INT32 choice)
|
||||
{
|
||||
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_MenuConfirmPressed(pid);
|
||||
boolean btnok = M_MenuBackPressed(pid);
|
||||
|
||||
if (menumessage.fadetimer < 9)
|
||||
menumessage.fadetimer++;
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
/// \brief In-game/pause menus
|
||||
|
||||
#include "../../k_menu.h"
|
||||
#include "../../k_grandprix.h" // K_CanChangeRules
|
||||
#include "../../s_sound.h"
|
||||
|
||||
// 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 :)
|
||||
|
|
@ -68,3 +70,286 @@ menu_t PAUSE_MainDef = {
|
|||
NULL,
|
||||
M_PauseInputs
|
||||
};
|
||||
|
||||
static void Dummymenuplayer_OnChange(void);
|
||||
|
||||
static CV_PossibleValue_t dummymenuplayer_cons_t[] = {{0, "NOPE"}, {1, "P1"}, {2, "P2"}, {3, "P3"}, {4, "P4"}, {0, NULL}};
|
||||
|
||||
//static consvar_t cv_dummymenuplayer = CVAR_INIT ("dummymenuplayer", "P1", CV_HIDDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange);
|
||||
consvar_t cv_dummymenuplayer = CVAR_INIT ("dummymenuplayer", "P1", CV_HIDDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange);
|
||||
|
||||
struct pausemenu_s pausemenu;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Pause menu!
|
||||
void M_OpenPauseMenu(void)
|
||||
{
|
||||
INT32 i = 0;
|
||||
|
||||
currentMenu = &PAUSE_MainDef;
|
||||
|
||||
// Ready the variables
|
||||
pausemenu.ticker = 0;
|
||||
|
||||
pausemenu.offset = 0;
|
||||
pausemenu.openoffset = 256;
|
||||
pausemenu.closing = false;
|
||||
|
||||
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:
|
||||
|
||||
PAUSE_Main[mpause_addons].status = IT_DISABLED;
|
||||
PAUSE_Main[mpause_changegametype].status = IT_DISABLED;
|
||||
PAUSE_Main[mpause_switchmap].status = IT_DISABLED;
|
||||
PAUSE_Main[mpause_restartmap].status = IT_DISABLED;
|
||||
PAUSE_Main[mpause_tryagain].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_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 (K_CanChangeRules(false))
|
||||
{
|
||||
PAUSE_Main[mpause_psetup].status = IT_STRING | IT_CALL;
|
||||
|
||||
if (server || IsPlayerAdmin(consoleplayer))
|
||||
{
|
||||
PAUSE_Main[mpause_changegametype].status = IT_STRING | IT_KEYHANDLER;
|
||||
menugametype = gametype;
|
||||
|
||||
PAUSE_Main[mpause_switchmap].status = IT_STRING | IT_CALL;
|
||||
PAUSE_Main[mpause_restartmap].status = IT_STRING | IT_CALL;
|
||||
PAUSE_Main[mpause_addons].status = IT_STRING | IT_CALL;
|
||||
}
|
||||
}
|
||||
else if (!netgame && !demo.playback)
|
||||
{
|
||||
boolean retryallowed = (modeattacking != ATTACKING_NONE);
|
||||
if (G_GametypeUsesLives())
|
||||
{
|
||||
for (i = 0; i <= splitscreen; i++)
|
||||
{
|
||||
if (players[g_localplayers[i]].lives <= 1)
|
||||
continue;
|
||||
retryallowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (retryallowed)
|
||||
{
|
||||
PAUSE_Main[mpause_tryagain].status = IT_STRING | IT_CALL;
|
||||
}
|
||||
}
|
||||
|
||||
if (G_GametypeHasSpectators())
|
||||
{
|
||||
if (splitscreen)
|
||||
PAUSE_Main[mpause_spectatemenu].status = IT_STRING|IT_SUBMENU;
|
||||
else
|
||||
{
|
||||
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(INT32 choice)
|
||||
{
|
||||
(void)choice;
|
||||
// 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;
|
||||
pausemenu.ticker++;
|
||||
|
||||
if (pausemenu.closing)
|
||||
{
|
||||
pausemenu.openoffset *= 2;
|
||||
if (pausemenu.openoffset > 255)
|
||||
M_ClearMenus(true);
|
||||
|
||||
}
|
||||
else
|
||||
pausemenu.openoffset /= 2;
|
||||
}
|
||||
|
||||
boolean M_PauseInputs(INT32 ch)
|
||||
{
|
||||
|
||||
const UINT8 pid = 0;
|
||||
(void) ch;
|
||||
|
||||
if (pausemenu.closing)
|
||||
return true; // Don't allow inputs.
|
||||
|
||||
if (menucmd[pid].dpad_ud < 0)
|
||||
{
|
||||
M_SetMenuDelay(pid);
|
||||
pausemenu.offset -= 50; // Each item is spaced by 50 px
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
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_s3k5b);
|
||||
M_NextOpt();
|
||||
M_SetMenuDelay(pid);
|
||||
return true;
|
||||
}
|
||||
|
||||
else if (M_MenuBackPressed(pid) || M_MenuButtonPressed(pid, MBT_START))
|
||||
{
|
||||
M_QuitPauseMenu(-1);
|
||||
M_SetMenuDelay(pid);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Change gametype
|
||||
void M_HandlePauseMenuGametype(INT32 choice)
|
||||
{
|
||||
const UINT8 pid = 0;
|
||||
const UINT32 forbidden = GTR_FORBIDMP;
|
||||
|
||||
(void)choice;
|
||||
|
||||
if (M_MenuConfirmPressed(pid))
|
||||
{
|
||||
if (menugametype != gametype)
|
||||
{
|
||||
M_ClearMenus(true);
|
||||
COM_ImmedExecute(va("randommap -gt %s", gametypes[menugametype]->name));
|
||||
return;
|
||||
}
|
||||
|
||||
M_SetMenuDelay(pid);
|
||||
S_StartSound(NULL, sfx_s3k7b);
|
||||
}
|
||||
else if (M_MenuExtraPressed(pid))
|
||||
{
|
||||
menugametype = gametype;
|
||||
M_SetMenuDelay(pid);
|
||||
S_StartSound(NULL, sfx_s3k7b);
|
||||
}
|
||||
else if (menucmd[pid].dpad_lr > 0)
|
||||
{
|
||||
M_NextMenuGametype(forbidden);
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
else if (menucmd[pid].dpad_lr < 0)
|
||||
{
|
||||
M_PrevMenuGametype(forbidden);
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
}
|
||||
|
||||
// Restart map
|
||||
void M_RestartMap(INT32 choice)
|
||||
{
|
||||
(void)choice;
|
||||
M_ClearMenus(false);
|
||||
COM_ImmedExecute("restartlevel");
|
||||
}
|
||||
|
||||
// Try again
|
||||
void M_TryAgain(INT32 choice)
|
||||
{
|
||||
(void)choice;
|
||||
if (demo.playback)
|
||||
return;
|
||||
|
||||
if (netgame || !Playing()) // Should never happen!
|
||||
return;
|
||||
|
||||
M_ClearMenus(false);
|
||||
|
||||
if (modeattacking != ATTACKING_NONE)
|
||||
{
|
||||
G_CheckDemoStatus(); // Cancel recording
|
||||
M_StartTimeAttack(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
G_SetRetryFlag();
|
||||
}
|
||||
}
|
||||
|
||||
// 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(-1);
|
||||
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.\n\nPress (B)\n"), NULL, MM_NOTHING);
|
||||
return;
|
||||
}
|
||||
M_QuitPauseMenu(-1);
|
||||
COM_ImmedExecute("changeteam playing");
|
||||
}
|
||||
|
||||
static void M_ExitGameResponse(INT32 ch)
|
||||
{
|
||||
if (ch != MA_YES)
|
||||
return;
|
||||
|
||||
if (modeattacking)
|
||||
{
|
||||
M_EndModeAttackRun();
|
||||
}
|
||||
else
|
||||
{
|
||||
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\nreturn to the menu?\nPress (A) to confirm or (B) to cancel\n"), FUNCPTRCAST(M_ExitGameResponse), MM_YESNO);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,12 @@
|
|||
/// \brief Replay popup menu
|
||||
|
||||
#include "../../k_menu.h"
|
||||
#include "../../s_sound.h"
|
||||
#include "../../p_tick.h" // leveltime
|
||||
#include "../../i_time.h"
|
||||
#include "../../r_main.h" // R_ExecuteSetViewSize
|
||||
#include "../../p_local.h" // P_InitCameraCmd
|
||||
#include "../../d_main.h" // D_StartTitle
|
||||
|
||||
menuitem_t PAUSE_PlaybackMenu[] =
|
||||
{
|
||||
|
|
@ -38,3 +44,153 @@ menu_t PAUSE_PlaybackMenuDef = {
|
|||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
void M_EndModeAttackRun(void)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
G_StopDemo();
|
||||
|
||||
if (demo.inreplayhut)
|
||||
M_ReplayHut(choice);
|
||||
else if (modeattacking)
|
||||
M_EndModeAttackRun();
|
||||
else
|
||||
D_StartTitle();
|
||||
}
|
||||
|
|
|
|||
226
src/menus/transient/virtual-keyboard.c
Normal file
226
src/menus/transient/virtual-keyboard.c
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
/// \file menus/transient/virtual-keyboard.c
|
||||
/// \brief Keyboard input
|
||||
|
||||
#include "../../k_menu.h"
|
||||
#include "../../s_sound.h"
|
||||
#include "../../hu_stuff.h" // shiftxform
|
||||
|
||||
// Typing "sub"-menu
|
||||
struct menutyping_s menutyping;
|
||||
|
||||
// keyboard layouts
|
||||
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}
|
||||
};
|
||||
|
||||
boolean M_ChangeStringCvar(INT32 choice)
|
||||
{
|
||||
consvar_t *cv = currentMenu->menuitems[itemOn].itemaction.cvar;
|
||||
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;
|
||||
}
|
||||
|
||||
// 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[menutyping.keyboardy][menutyping.keyboardx])
|
||||
menutyping.keyboardx--;
|
||||
}
|
||||
|
||||
static boolean M_IsTypingKey(INT32 key)
|
||||
{
|
||||
return key == KEY_BACKSPACE || key == KEY_ENTER ||
|
||||
key == KEY_ESCAPE || key == KEY_DEL || isprint(key);
|
||||
}
|
||||
|
||||
void M_MenuTypingInput(INT32 key)
|
||||
{
|
||||
|
||||
const UINT8 pid = 0;
|
||||
|
||||
// Fade-in
|
||||
|
||||
if (menutyping.menutypingclose) // closing
|
||||
{
|
||||
menutyping.menutypingfade--;
|
||||
if (!menutyping.menutypingfade)
|
||||
menutyping.active = false;
|
||||
|
||||
return; // prevent inputs while closing the menu.
|
||||
}
|
||||
else // opening
|
||||
{
|
||||
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 (!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
|
||||
&& !(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))
|
||||
{
|
||||
menutyping.keyboardtyping = true;
|
||||
}
|
||||
}
|
||||
else // Keyboard inputs.
|
||||
{
|
||||
// On the flipside, if we're pressing any keyboard input, switch to controller inputs.
|
||||
if (!M_IsTypingKey(key) && (
|
||||
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
|
||||
))
|
||||
{
|
||||
menutyping.keyboardtyping = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// OTHERWISE, process keyboard inputs for typing!
|
||||
if (key == KEY_ENTER || key == KEY_ESCAPE)
|
||||
{
|
||||
menutyping.menutypingclose = true; // close menu.
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
menutyping.keyboardy++;
|
||||
if (menutyping.keyboardy > 4)
|
||||
menutyping.keyboardy = 0;
|
||||
|
||||
M_UpdateKeyboardX();
|
||||
M_SetMenuDelay(pid);
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
}
|
||||
else if (menucmd[pid].dpad_ud < 0) // up
|
||||
{
|
||||
menutyping.keyboardy--;
|
||||
if (menutyping.keyboardy < 0)
|
||||
menutyping.keyboardy = 4;
|
||||
|
||||
M_UpdateKeyboardX();
|
||||
M_SetMenuDelay(pid);
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
}
|
||||
else if (menucmd[pid].dpad_lr > 0) // right
|
||||
{
|
||||
menutyping.keyboardx++;
|
||||
if (!virtualKeyboard[menutyping.keyboardy][menutyping.keyboardx])
|
||||
menutyping.keyboardx = 0;
|
||||
|
||||
M_SetMenuDelay(pid);
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
}
|
||||
else if (menucmd[pid].dpad_lr < 0) // left
|
||||
{
|
||||
menutyping.keyboardx--;
|
||||
if (menutyping.keyboardx < 0)
|
||||
{
|
||||
menutyping.keyboardx = 12;
|
||||
M_UpdateKeyboardX();
|
||||
}
|
||||
M_SetMenuDelay(pid);
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
}
|
||||
else if (M_MenuConfirmPressed(pid))
|
||||
{
|
||||
// Add the character. First though, check what we're pressing....
|
||||
INT16 c = virtualKeyboard[menutyping.keyboardy][menutyping.keyboardx];
|
||||
if (menutyping.keyboardshift ^ menutyping.keyboardcapslock)
|
||||
c = shift_virtualKeyboard[menutyping.keyboardy][menutyping.keyboardx];
|
||||
|
||||
if (c == KEY_RSHIFT)
|
||||
menutyping.keyboardshift = !menutyping.keyboardshift;
|
||||
else if (c == KEY_CAPSLOCK)
|
||||
menutyping.keyboardcapslock = !menutyping.keyboardcapslock;
|
||||
else if (c == KEY_ENTER)
|
||||
{
|
||||
menutyping.menutypingclose = true; // close menu.
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
M_ChangeStringCvar((INT32)c); // Write!
|
||||
menutyping.keyboardshift = false; // undo shift if it had been pressed
|
||||
}
|
||||
|
||||
M_SetMenuDelay(pid);
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue