mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
More opportunities for early rejection in table-wide searches, in anticipation of future work.
- Many circumstances independently implemented string name comparisons. Most of these have been converted to use R_SkinAvailable, with one exception.
- M_CharacterSelectInit already needs to iterate over the characters, so produce the skin hash independently.
1545 lines
35 KiB
C
1545 lines
35 KiB
C
/// \file menus/play-char-select.c
|
|
/// \brief Character Select
|
|
|
|
#include "../k_menu.h"
|
|
#include "../r_skins.h"
|
|
#include "../s_sound.h"
|
|
#include "../k_grandprix.h" // K_CanChangeRules
|
|
#include "../m_cond.h" // Condition Sets
|
|
|
|
menuitem_t PLAY_CharSelect[] =
|
|
{
|
|
{IT_NOTHING, NULL, NULL, NULL, {NULL}, 0, 0},
|
|
};
|
|
|
|
menu_t PLAY_CharSelectDef = {
|
|
sizeof (PLAY_CharSelect) / sizeof (menuitem_t),
|
|
&MainDef,
|
|
0,
|
|
PLAY_CharSelect,
|
|
0, 0,
|
|
0, 0,
|
|
0,
|
|
NULL,
|
|
0, 0,
|
|
M_DrawCharacterSelect,
|
|
M_CharacterSelectTick,
|
|
M_CharacterSelectInit,
|
|
M_CharacterSelectQuit,
|
|
M_CharacterSelectHandler
|
|
};
|
|
|
|
static CV_PossibleValue_t skins_cons_t[MAXSKINS+1] = {{1, DEFAULTSKIN}};
|
|
consvar_t cv_chooseskin = CVAR_INIT ("chooseskin", DEFAULTSKIN, CV_HIDDEN, skins_cons_t, NULL);
|
|
|
|
static void Splitplayers_OnChange(void);
|
|
CV_PossibleValue_t splitplayers_cons_t[] = {{1, "One"}, {2, "Two"}, {3, "Three"}, {4, "Four"}, {0, NULL}};
|
|
consvar_t cv_splitplayers = CVAR_INIT ("splitplayers", "One", CV_CALL, splitplayers_cons_t, Splitplayers_OnChange);
|
|
|
|
UINT16 nummenucolors = 0;
|
|
|
|
// Character Select!
|
|
// @TODO: Splitscreen handling when profiles are added into the game. ...I probably won't be the one to handle this however. -Lat'
|
|
|
|
struct setup_chargrid_s setup_chargrid[9][9];
|
|
setup_player_t setup_player[MAXSPLITSCREENPLAYERS];
|
|
|
|
UINT8 setup_followercategories[MAXFOLLOWERCATEGORIES][2];
|
|
UINT8 setup_numfollowercategories;
|
|
|
|
UINT8 setup_numplayers = 0; // This variable is very important, it was extended to determine how many players exist in ALL menus.
|
|
tic_t setup_animcounter = 0;
|
|
|
|
UINT8 setup_page = 0;
|
|
UINT8 setup_maxpage = 0; // For charsel page to identify alts easier...
|
|
|
|
void M_AddMenuColor(UINT16 color) {
|
|
menucolor_t *c;
|
|
|
|
if (color >= numskincolors) {
|
|
CONS_Printf("M_AddMenuColor: color %d does not exist.",color);
|
|
return;
|
|
}
|
|
|
|
// SRB2Kart: I do not understand vanilla doesn't need this but WE do???!?!??!
|
|
if (!skincolors[color].accessible) {
|
|
return;
|
|
}
|
|
|
|
c = (menucolor_t *)malloc(sizeof(menucolor_t));
|
|
c->color = color;
|
|
if (menucolorhead == NULL) {
|
|
c->next = c;
|
|
c->prev = c;
|
|
menucolorhead = c;
|
|
menucolortail = c;
|
|
} else {
|
|
c->next = menucolorhead;
|
|
c->prev = menucolortail;
|
|
menucolortail->next = c;
|
|
menucolorhead->prev = c;
|
|
menucolortail = c;
|
|
}
|
|
|
|
nummenucolors++;
|
|
}
|
|
|
|
void M_MoveColorBefore(UINT16 color, UINT16 targ) {
|
|
menucolor_t *look, *c = NULL, *t = NULL;
|
|
|
|
if (color == targ)
|
|
return;
|
|
if (color >= numskincolors) {
|
|
CONS_Printf("M_MoveColorBefore: color %d does not exist.",color);
|
|
return;
|
|
}
|
|
if (targ >= numskincolors) {
|
|
CONS_Printf("M_MoveColorBefore: target color %d does not exist.",targ);
|
|
return;
|
|
}
|
|
|
|
for (look=menucolorhead;;look=look->next) {
|
|
if (look->color == color)
|
|
c = look;
|
|
else if (look->color == targ)
|
|
t = look;
|
|
if (c != NULL && t != NULL)
|
|
break;
|
|
if (look==menucolortail)
|
|
return;
|
|
}
|
|
|
|
if (c == t->prev)
|
|
return;
|
|
|
|
if (t==menucolorhead)
|
|
menucolorhead = c;
|
|
if (c==menucolortail)
|
|
menucolortail = c->prev;
|
|
|
|
c->prev->next = c->next;
|
|
c->next->prev = c->prev;
|
|
|
|
c->prev = t->prev;
|
|
c->next = t;
|
|
t->prev->next = c;
|
|
t->prev = c;
|
|
}
|
|
|
|
void M_MoveColorAfter(UINT16 color, UINT16 targ) {
|
|
menucolor_t *look, *c = NULL, *t = NULL;
|
|
|
|
if (color == targ)
|
|
return;
|
|
if (color >= numskincolors) {
|
|
CONS_Printf("M_MoveColorAfter: color %d does not exist.\n",color);
|
|
return;
|
|
}
|
|
if (targ >= numskincolors) {
|
|
CONS_Printf("M_MoveColorAfter: target color %d does not exist.\n",targ);
|
|
return;
|
|
}
|
|
|
|
for (look=menucolorhead;;look=look->next) {
|
|
if (look->color == color)
|
|
c = look;
|
|
else if (look->color == targ)
|
|
t = look;
|
|
if (c != NULL && t != NULL)
|
|
break;
|
|
if (look==menucolortail)
|
|
return;
|
|
}
|
|
|
|
if (t == c->prev)
|
|
return;
|
|
|
|
if (t==menucolortail)
|
|
menucolortail = c;
|
|
else if (c==menucolortail)
|
|
menucolortail = c->prev;
|
|
|
|
c->prev->next = c->next;
|
|
c->next->prev = c->prev;
|
|
|
|
c->next = t->next;
|
|
c->prev = t;
|
|
t->next->prev = c;
|
|
t->next = c;
|
|
}
|
|
|
|
UINT16 M_GetColorBefore(UINT16 color, UINT16 amount, boolean follower)
|
|
{
|
|
menucolor_t *look = NULL;
|
|
|
|
for (; amount > 0; amount--)
|
|
{
|
|
if (follower == true)
|
|
{
|
|
if (color == FOLLOWERCOLOR_OPPOSITE)
|
|
{
|
|
look = menucolortail;
|
|
color = menucolortail->color;
|
|
continue;
|
|
}
|
|
|
|
if (color == FOLLOWERCOLOR_MATCH)
|
|
{
|
|
look = NULL;
|
|
color = FOLLOWERCOLOR_OPPOSITE;
|
|
continue;
|
|
}
|
|
|
|
if (color == menucolorhead->color)
|
|
{
|
|
look = NULL;
|
|
color = FOLLOWERCOLOR_MATCH;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (color == 0 || color >= numskincolors)
|
|
{
|
|
CONS_Printf("M_GetColorBefore: color %d does not exist.\n",color);
|
|
return 0;
|
|
}
|
|
|
|
if (look == NULL)
|
|
{
|
|
for (look = menucolorhead;; look = look->next)
|
|
{
|
|
if (look->color == color)
|
|
{
|
|
break;
|
|
}
|
|
if (look == menucolortail)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
look = look->prev;
|
|
color = look->color;
|
|
}
|
|
return color;
|
|
}
|
|
|
|
UINT16 M_GetColorAfter(UINT16 color, UINT16 amount, boolean follower)
|
|
{
|
|
menucolor_t *look = NULL;
|
|
|
|
for (; amount > 0; amount--)
|
|
{
|
|
if (follower == true)
|
|
{
|
|
if (color == menucolortail->color)
|
|
{
|
|
look = NULL;
|
|
color = FOLLOWERCOLOR_OPPOSITE;
|
|
continue;
|
|
}
|
|
|
|
if (color == FOLLOWERCOLOR_OPPOSITE)
|
|
{
|
|
look = NULL;
|
|
color = FOLLOWERCOLOR_MATCH;
|
|
continue;
|
|
}
|
|
|
|
if (color == FOLLOWERCOLOR_MATCH)
|
|
{
|
|
look = menucolorhead;
|
|
color = menucolorhead->color;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (color == 0 || color >= numskincolors)
|
|
{
|
|
CONS_Printf("M_GetColorAfter: color %d does not exist.\n",color);
|
|
return 0;
|
|
}
|
|
|
|
if (look == NULL)
|
|
{
|
|
for (look = menucolorhead;; look = look->next)
|
|
{
|
|
if (look->color == color)
|
|
{
|
|
break;
|
|
}
|
|
if (look == menucolortail)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
look = look->next;
|
|
color = look->color;
|
|
}
|
|
return color;
|
|
}
|
|
|
|
void M_InitPlayerSetupColors(void) {
|
|
UINT8 i;
|
|
numskincolors = SKINCOLOR_FIRSTFREESLOT;
|
|
menucolorhead = menucolortail = NULL;
|
|
for (i=0; i<numskincolors; i++)
|
|
M_AddMenuColor(i);
|
|
}
|
|
|
|
void M_FreePlayerSetupColors(void) {
|
|
menucolor_t *look = menucolorhead, *tmp;
|
|
|
|
if (menucolorhead==NULL)
|
|
return;
|
|
|
|
while (true) {
|
|
if (look != menucolortail) {
|
|
tmp = look;
|
|
look = look->next;
|
|
free(tmp);
|
|
} else {
|
|
free(look);
|
|
return;
|
|
}
|
|
}
|
|
|
|
menucolorhead = menucolortail = NULL;
|
|
}
|
|
|
|
// sets up the grid pos for the skin used by the profile.
|
|
static void M_SetupProfileGridPos(setup_player_t *p)
|
|
{
|
|
profile_t *pr = PR_GetProfile(p->profilen);
|
|
INT32 i = R_SkinAvailable(pr->skinname);
|
|
INT32 alt = 0; // Hey it's my character's name!
|
|
|
|
// While we're here, read follower values.
|
|
p->followern = K_FollowerAvailable(pr->follower);
|
|
|
|
if (p->followern < 0 || p->followern >= numfollowers || followers[p->followern].category >= numfollowercategories || !K_FollowerUsable(p->followern))
|
|
p->followercategory = p->followern = -1;
|
|
else
|
|
p->followercategory = followers[p->followern].category;
|
|
|
|
p->followercolor = pr->followercolor;
|
|
|
|
if (!R_SkinUsable(g_localplayers[0], i, false))
|
|
{
|
|
i = GetSkinNumClosestToStats(skins[i].kartspeed, skins[i].kartweight, skins[i].flags, false);
|
|
}
|
|
|
|
// Now position the grid for skin
|
|
p->gridx = skins[i].kartspeed-1;
|
|
p->gridy = skins[i].kartweight-1;
|
|
|
|
// Now this put our cursor on the good alt
|
|
while (alt < setup_chargrid[p->gridx][p->gridy].numskins && setup_chargrid[p->gridx][p->gridy].skinlist[alt] != i)
|
|
alt++;
|
|
|
|
p->clonenum = alt;
|
|
p->color = PR_GetProfileColor(pr);
|
|
}
|
|
|
|
static void M_SetupMidGameGridPos(setup_player_t *p, UINT8 num)
|
|
{
|
|
INT32 i = R_SkinAvailable(cv_skin[num].zstring);
|
|
INT32 alt = 0; // Hey it's my character's name!
|
|
|
|
// While we're here, read follower values.
|
|
p->followern = cv_follower[num].value;
|
|
p->followercolor = cv_followercolor[num].value;
|
|
|
|
if (p->followern < 0 || p->followern >= numfollowers || followers[p->followern].category >= numfollowercategories || !K_FollowerUsable(p->followern))
|
|
p->followercategory = p->followern = -1;
|
|
else
|
|
p->followercategory = followers[p->followern].category;
|
|
|
|
// Now position the grid for skin
|
|
p->gridx = skins[i].kartspeed-1;
|
|
p->gridy = skins[i].kartweight-1;
|
|
|
|
// Now this put our cursor on the good alt
|
|
while (alt < setup_chargrid[p->gridx][p->gridy].numskins && setup_chargrid[p->gridx][p->gridy].skinlist[alt] != i)
|
|
alt++;
|
|
|
|
p->clonenum = alt;
|
|
p->color = cv_playercolor[num].value;
|
|
return; // we're done here
|
|
}
|
|
|
|
|
|
void M_CharacterSelectInit(void)
|
|
{
|
|
UINT8 i, j;
|
|
setup_maxpage = 0;
|
|
|
|
// While we're editing profiles, don't unset the devices for p1
|
|
if (gamestate == GS_MENU)
|
|
{
|
|
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
|
{
|
|
// Un-set devices for all players if not editing profile
|
|
if (!optionsmenu.profile)
|
|
{
|
|
G_SetDeviceForPlayer(i, -1);
|
|
CONS_Printf("M_CharacterSelectInit: Device for %d set to %d\n", i, -1);
|
|
}
|
|
}
|
|
}
|
|
|
|
memset(setup_chargrid, -1, sizeof(setup_chargrid));
|
|
for (i = 0; i < 9; i++)
|
|
{
|
|
for (j = 0; j < 9; j++)
|
|
setup_chargrid[i][j].numskins = 0;
|
|
}
|
|
|
|
memset(setup_player, 0, sizeof(setup_player));
|
|
setup_numplayers = 0;
|
|
|
|
memset(setup_explosions, 0, sizeof(setup_explosions));
|
|
setup_animcounter = 0;
|
|
|
|
UINT32 localskinhash[MAXSPLITSCREENPLAYERS];
|
|
|
|
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
|
{
|
|
// Default to no follower / match colour.
|
|
setup_player[i].followern = -1;
|
|
setup_player[i].followercategory = -1;
|
|
setup_player[i].followercolor = FOLLOWERCOLOR_MATCH;
|
|
|
|
// Set default selected profile to the last used profile for each player:
|
|
// (Make sure we don't overshoot it somehow if we deleted profiles or whatnot)
|
|
setup_player[i].profilen = min(cv_lastprofile[i].value, PR_GetNumProfiles());
|
|
|
|
// Assistant for comparisons.
|
|
localskinhash[i] = quickncasehash(cv_skin[i].string, SKINNAMESIZE);
|
|
}
|
|
|
|
for (i = 0; i < numskins; i++)
|
|
{
|
|
UINT8 x = skins[i].kartspeed-1;
|
|
UINT8 y = skins[i].kartweight-1;
|
|
|
|
if (!R_SkinUsable(g_localplayers[0], i, false))
|
|
continue;
|
|
|
|
if (setup_chargrid[x][y].numskins >= MAXCLONES)
|
|
CONS_Alert(CONS_ERROR, "Max character alts reached for %d,%d\n", x+1, y+1);
|
|
else
|
|
{
|
|
setup_chargrid[x][y].skinlist[setup_chargrid[x][y].numskins] = i;
|
|
setup_chargrid[x][y].numskins++;
|
|
|
|
setup_maxpage = max(setup_maxpage, setup_chargrid[x][y].numskins-1);
|
|
}
|
|
|
|
for (j = 0; j < MAXSPLITSCREENPLAYERS; j++)
|
|
{
|
|
// See also R_SkinAvailable
|
|
|
|
if (localskinhash[j] != skins[i].namehash)
|
|
continue;
|
|
|
|
if (!stricmp(cv_skin[j].string, skins[i].name))
|
|
continue;
|
|
|
|
{
|
|
setup_player[j].gridx = x;
|
|
setup_player[j].gridy = y;
|
|
setup_player[j].color = skins[i].prefcolor;
|
|
|
|
// If we're on prpfile select, skip straight to CSSTEP_CHARS
|
|
// do the same if we're midgame, but make sure to consider splitscreen properly.
|
|
if ((optionsmenu.profile && j == 0) || (gamestate != GS_MENU && j <= splitscreen))
|
|
{
|
|
if (optionsmenu.profile) // In menu, setting up profile character/follower
|
|
{
|
|
setup_player[j].profilen = optionsmenu.profilen;
|
|
PR_ApplyProfileLight(setup_player[j].profilen, 0);
|
|
M_SetupProfileGridPos(&setup_player[j]);
|
|
}
|
|
else // gamestate != GS_MENU, in that case, assume this is whatever profile we chose to play with.
|
|
{
|
|
setup_player[j].profilen = cv_lastprofile[j].value;
|
|
M_SetupMidGameGridPos(&setup_player[j], j);
|
|
}
|
|
|
|
// Don't reapply the profile here, it was already applied.
|
|
setup_player[j].mdepth = CSSTEP_CHARS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
setup_numfollowercategories = 0;
|
|
for (i = 0; i < numfollowercategories; i++)
|
|
{
|
|
if (followercategories[i].numincategory == 0)
|
|
continue;
|
|
|
|
setup_followercategories[setup_numfollowercategories][0] = 0;
|
|
|
|
for (j = 0; j < numfollowers; j++)
|
|
{
|
|
if (followers[j].category != i)
|
|
continue;
|
|
|
|
if (!K_FollowerUsable(j))
|
|
continue;
|
|
|
|
setup_followercategories[setup_numfollowercategories][0]++;
|
|
setup_followercategories[setup_numfollowercategories][1] = i;
|
|
}
|
|
|
|
if (!setup_followercategories[setup_numfollowercategories][0])
|
|
continue;
|
|
|
|
setup_numfollowercategories++;
|
|
}
|
|
|
|
setup_page = 0;
|
|
}
|
|
|
|
void M_CharacterSelect(INT32 choice)
|
|
{
|
|
(void)choice;
|
|
PLAY_CharSelectDef.music = currentMenu->music;
|
|
PLAY_CharSelectDef.prevMenu = currentMenu;
|
|
M_SetupNextMenu(&PLAY_CharSelectDef, false);
|
|
}
|
|
|
|
|
|
// Gets the selected follower's state for a given setup player.
|
|
static void M_GetFollowerState(setup_player_t *p)
|
|
{
|
|
|
|
p->follower_state = &states[followers[p->followern].followstate];
|
|
|
|
if (p->follower_state->frame & FF_ANIMATE)
|
|
p->follower_tics = p->follower_state->var2; // support for FF_ANIMATE
|
|
else
|
|
p->follower_tics = p->follower_state->tics;
|
|
|
|
p->follower_frame = p->follower_state->frame & FF_FRAMEMASK;
|
|
}
|
|
|
|
static boolean M_DeviceAvailable(INT32 deviceID, UINT8 numPlayers)
|
|
{
|
|
INT32 i;
|
|
|
|
if (numPlayers == 0)
|
|
{
|
|
// All of them are available!
|
|
return true;
|
|
}
|
|
|
|
for (i = 0; i < numPlayers; i++)
|
|
{
|
|
int player_device = G_GetDeviceForPlayer(i);
|
|
if (player_device == -1)
|
|
{
|
|
continue;
|
|
}
|
|
if (player_device == deviceID)
|
|
{
|
|
// This one's already being used.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// This device is good to go.
|
|
return true;
|
|
}
|
|
|
|
static boolean M_HandlePressStart(setup_player_t *p, UINT8 num)
|
|
{
|
|
INT32 i, j;
|
|
INT32 num_gamepads_available;
|
|
|
|
if (optionsmenu.profile)
|
|
return false; // Don't allow for the possibility of SOMEHOW another player joining in.
|
|
|
|
// Detect B press first ... this means P1 can actually exit out of the menu.
|
|
if (M_MenuBackPressed(num))
|
|
{
|
|
M_SetMenuDelay(num);
|
|
|
|
if (num == 0)
|
|
{
|
|
// We're done here.
|
|
memset(setup_player, 0, sizeof(setup_player)); // Reset this to avoid funky things with profile display.
|
|
M_GoBack(0);
|
|
return true;
|
|
}
|
|
|
|
// Don't allow this press to ever count as "start".
|
|
return false;
|
|
}
|
|
|
|
if (num != setup_numplayers)
|
|
{
|
|
// Only detect devices for the last player.
|
|
return false;
|
|
}
|
|
|
|
// Now detect new devices trying to join.
|
|
num_gamepads_available = G_GetNumAvailableGamepads();
|
|
for (i = 0; i < num_gamepads_available + 1; i++)
|
|
{
|
|
INT32 device = 0;
|
|
|
|
if (i > 0)
|
|
{
|
|
device = G_GetAvailableGamepadDevice(i - 1);
|
|
}
|
|
|
|
if (device == KEYBOARD_MOUSE_DEVICE && num != 0)
|
|
{
|
|
// Only player 1 can be assigned to the KBM device.
|
|
continue;
|
|
}
|
|
|
|
if (G_IsDeviceResponding(device) != true)
|
|
{
|
|
// No buttons are being pushed.
|
|
continue;
|
|
}
|
|
|
|
if (M_DeviceAvailable(device, setup_numplayers) == true)
|
|
{
|
|
// Available!! Let's use this one!!
|
|
|
|
// if P1 is setting up using keyboard (device 0), save their last used device.
|
|
// this is to allow them to retain controller usage when they play alone.
|
|
// Because let's face it, when you test mods, you're often lazy to grab your controller for menuing :)
|
|
if (i == 0 && num == 0)
|
|
{
|
|
setup_player[num].ponedevice = G_GetDeviceForPlayer(num);
|
|
}
|
|
else if (num)
|
|
{
|
|
// For any player past player 1, set controls to default profile controls, otherwise it's generally awful to do any menuing...
|
|
memcpy(&gamecontrol[num], gamecontroldefault, sizeof(gamecontroldefault));
|
|
}
|
|
|
|
G_SetDeviceForPlayer(num, device);
|
|
CONS_Printf("M_HandlePressStart: Device for %d set to %d\n", num, device);
|
|
|
|
for (j = num + 1; j < MAXSPLITSCREENPLAYERS; j++)
|
|
{
|
|
// Un-set devices for other players.
|
|
G_SetDeviceForPlayer(j, -1);
|
|
CONS_Printf("M_HandlePressStart: Device for %d set to %d\n", j, -1);
|
|
}
|
|
|
|
//setup_numplayers++;
|
|
p->mdepth = CSSTEP_PROFILE;
|
|
S_StartSound(NULL, sfx_s3k65);
|
|
|
|
// Prevent quick presses for multiple players
|
|
for (j = 0; j < MAXSPLITSCREENPLAYERS; j++)
|
|
{
|
|
setup_player[j].delay = MENUDELAYTIME;
|
|
M_SetMenuDelay(j);
|
|
menucmd[j].buttonsHeld |= MBT_X;
|
|
}
|
|
|
|
G_ResetAllDeviceResponding();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num)
|
|
{
|
|
const UINT8 maxp = PR_GetNumProfiles() -1;
|
|
UINT8 realnum = num; // Used for profile when using splitdevice.
|
|
UINT8 i;
|
|
|
|
if (cv_splitdevice.value)
|
|
num = 0;
|
|
|
|
if (menucmd[num].dpad_ud > 0)
|
|
{
|
|
p->profilen++;
|
|
if (p->profilen > maxp)
|
|
p->profilen = 0;
|
|
|
|
S_StartSound(NULL, sfx_s3k5b);
|
|
M_SetMenuDelay(num);
|
|
}
|
|
else if (menucmd[num].dpad_ud < 0)
|
|
{
|
|
if (p->profilen == 0)
|
|
p->profilen = maxp;
|
|
else
|
|
p->profilen--;
|
|
|
|
S_StartSound(NULL, sfx_s3k5b);
|
|
M_SetMenuDelay(num);
|
|
}
|
|
else if (M_MenuBackPressed(num))
|
|
{
|
|
if (num == setup_numplayers-1)
|
|
{
|
|
|
|
p->mdepth = CSSTEP_NONE;
|
|
S_StartSound(NULL, sfx_s3k5b);
|
|
|
|
// Prevent quick presses for multiple players
|
|
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
|
{
|
|
setup_player[i].delay = MENUDELAYTIME;
|
|
M_SetMenuDelay(i);
|
|
menucmd[i].buttonsHeld |= MBT_X;
|
|
}
|
|
|
|
G_SetDeviceForPlayer(num, -1);
|
|
CONS_Printf("M_HandleCSelectProfile: Device for %d set to %d\n", num, -1);
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
S_StartSound(NULL, sfx_s3kb2);
|
|
}
|
|
|
|
M_SetMenuDelay(num);
|
|
}
|
|
else if (M_MenuConfirmPressed(num))
|
|
{
|
|
SINT8 belongsTo = -1;
|
|
|
|
if (p->profilen != PROFILE_GUEST)
|
|
{
|
|
for (i = 0; i < setup_numplayers; i++)
|
|
{
|
|
if (setup_player[i].mdepth > CSSTEP_PROFILE
|
|
&& setup_player[i].profilen == p->profilen)
|
|
{
|
|
belongsTo = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (belongsTo != -1 && belongsTo != num)
|
|
{
|
|
S_StartSound(NULL, sfx_s3k7b);
|
|
M_SetMenuDelay(num);
|
|
return false;
|
|
}
|
|
|
|
// Apply the profile.
|
|
PR_ApplyProfile(p->profilen, realnum); // Otherwise P1 would inherit the last player's profile in splitdevice and that's not what we want...
|
|
M_SetupProfileGridPos(p);
|
|
|
|
p->changeselect = 0;
|
|
|
|
if (p->profilen == PROFILE_GUEST)
|
|
{
|
|
// Guest profile, always ask for options.
|
|
p->mdepth = CSSTEP_CHARS;
|
|
}
|
|
else
|
|
{
|
|
p->mdepth = CSSTEP_ASKCHANGES;
|
|
}
|
|
|
|
S_StartSound(NULL, sfx_s3k63);
|
|
}
|
|
else if (M_MenuExtraPressed(num))
|
|
{
|
|
UINT8 yourprofile = min(cv_lastprofile[realnum].value, PR_GetNumProfiles());
|
|
if (p->profilen == yourprofile)
|
|
p->profilen = PROFILE_GUEST;
|
|
else
|
|
p->profilen = yourprofile;
|
|
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
|
|
M_SetMenuDelay(num);
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
static void M_HandleCharAskChange(setup_player_t *p, UINT8 num)
|
|
{
|
|
|
|
if (cv_splitdevice.value)
|
|
num = 0;
|
|
|
|
// there's only 2 options so lol
|
|
if (menucmd[num].dpad_ud != 0)
|
|
{
|
|
p->changeselect = (p->changeselect == 0) ? 1 : 0;
|
|
|
|
S_StartSound(NULL, sfx_s3k5b);
|
|
M_SetMenuDelay(num);
|
|
}
|
|
else if (M_MenuBackPressed(num))
|
|
{
|
|
p->changeselect = 0;
|
|
p->mdepth = CSSTEP_PROFILE;
|
|
|
|
S_StartSound(NULL, sfx_s3k5b);
|
|
M_SetMenuDelay(num);
|
|
}
|
|
else if (M_MenuConfirmPressed(num))
|
|
{
|
|
if (!p->changeselect)
|
|
{
|
|
// no changes
|
|
M_GetFollowerState(p);
|
|
p->mdepth = CSSTEP_READY;
|
|
p->delay = TICRATE;
|
|
|
|
S_StartSound(NULL, sfx_s3k4e);
|
|
M_SetupReadyExplosions(true, p->gridx, p->gridy, p->color);
|
|
}
|
|
else
|
|
{
|
|
// changes
|
|
p->mdepth = CSSTEP_CHARS;
|
|
S_StartSound(NULL, sfx_s3k63);
|
|
}
|
|
|
|
M_SetMenuDelay(num);
|
|
}
|
|
}
|
|
|
|
static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num)
|
|
{
|
|
UINT8 numclones;
|
|
INT32 skin;
|
|
boolean forceskin = (Playing() && K_CanChangeRules(true) == true) && (cv_forceskin.value != -1);
|
|
|
|
if (cv_splitdevice.value)
|
|
num = 0;
|
|
|
|
if (menucmd[num].dpad_ud > 0)
|
|
{
|
|
p->gridy++;
|
|
if (p->gridy > 8)
|
|
p->gridy = 0;
|
|
S_StartSound(NULL, sfx_s3k5b);
|
|
M_SetMenuDelay(num);
|
|
}
|
|
else if (menucmd[num].dpad_ud < 0)
|
|
{
|
|
p->gridy--;
|
|
if (p->gridy < 0)
|
|
p->gridy = 8;
|
|
S_StartSound(NULL, sfx_s3k5b);
|
|
M_SetMenuDelay(num);
|
|
}
|
|
|
|
if (menucmd[num].dpad_lr > 0)
|
|
{
|
|
p->gridx++;
|
|
if (p->gridx > 8)
|
|
p->gridx = 0;
|
|
S_StartSound(NULL, sfx_s3k5b);
|
|
M_SetMenuDelay(num);
|
|
}
|
|
else if (menucmd[num].dpad_lr < 0)
|
|
{
|
|
p->gridx--;
|
|
if (p->gridx < 0)
|
|
p->gridx = 8;
|
|
S_StartSound(NULL, sfx_s3k5b);
|
|
M_SetMenuDelay(num);
|
|
}
|
|
else if (M_MenuExtraPressed(num))
|
|
{
|
|
p->gridx /= 3;
|
|
p->gridx = (3*p->gridx) + 1;
|
|
p->gridy /= 3;
|
|
p->gridy = (3*p->gridy) + 1;
|
|
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
|
|
M_SetMenuDelay(num);
|
|
}
|
|
|
|
// try to set the clone num to the page # if possible.
|
|
p->clonenum = setup_page;
|
|
|
|
// Process this after possible pad movement,
|
|
// this makes sure we don't have a weird ghost hover on a character with no clones.
|
|
numclones = setup_chargrid[p->gridx][p->gridy].numskins;
|
|
|
|
if (p->clonenum >= numclones)
|
|
p->clonenum = 0;
|
|
|
|
if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/)
|
|
{
|
|
if (forceskin)
|
|
{
|
|
if ((p->gridx != skins[cv_forceskin.value].kartspeed-1)
|
|
|| (p->gridy != skins[cv_forceskin.value].kartweight-1))
|
|
{
|
|
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kb2
|
|
}
|
|
else
|
|
{
|
|
p->mdepth = CSSTEP_COLORS;
|
|
S_StartSound(NULL, sfx_s3k63);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
skin = setup_chargrid[p->gridx][p->gridy].skinlist[setup_page];
|
|
if (setup_page >= setup_chargrid[p->gridx][p->gridy].numskins || skin == -1)
|
|
{
|
|
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kb2
|
|
}
|
|
else
|
|
{
|
|
if (setup_page+1 == setup_chargrid[p->gridx][p->gridy].numskins)
|
|
p->mdepth = CSSTEP_COLORS; // Skip clones menu if there are none on this page.
|
|
else
|
|
p->mdepth = CSSTEP_ALTS;
|
|
|
|
S_StartSound(NULL, sfx_s3k63);
|
|
}
|
|
}
|
|
|
|
M_SetMenuDelay(num);
|
|
}
|
|
else if (M_MenuBackPressed(num))
|
|
{
|
|
// for profiles / gameplay, exit out of the menu instantly,
|
|
// we don't want to go to the input detection menu.
|
|
if (optionsmenu.profile || gamestate != GS_MENU)
|
|
{
|
|
memset(setup_player, 0, sizeof(setup_player)); // Reset setup_player otherwise it does some VERY funky things.
|
|
M_SetMenuDelay(0);
|
|
M_GoBack(0);
|
|
return true;
|
|
}
|
|
else // in main menu
|
|
{
|
|
p->mdepth = CSSTEP_PROFILE;
|
|
S_StartSound(NULL, sfx_s3k5b);
|
|
}
|
|
M_SetMenuDelay(num);
|
|
}
|
|
|
|
if (num == 0 && setup_numplayers == 1 && setup_maxpage && !forceskin) // ONLY one player.
|
|
{
|
|
if (M_MenuButtonPressed(num, MBT_L))
|
|
{
|
|
if (setup_page == setup_maxpage)
|
|
setup_page = 0;
|
|
else
|
|
setup_page++;
|
|
|
|
S_StartSound(NULL, sfx_s3k63);
|
|
M_SetMenuDelay(num);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void M_HandleCharRotate(setup_player_t *p, UINT8 num)
|
|
{
|
|
UINT8 numclones = setup_chargrid[p->gridx][p->gridy].numskins;
|
|
|
|
if (cv_splitdevice.value)
|
|
num = 0;
|
|
|
|
if (menucmd[num].dpad_lr > 0)
|
|
{
|
|
p->clonenum++;
|
|
if (p->clonenum >= numclones)
|
|
p->clonenum = 0;
|
|
p->rotate = CSROTATETICS;
|
|
p->delay = CSROTATETICS;
|
|
S_StartSound(NULL, sfx_s3kc3s);
|
|
}
|
|
else if (menucmd[num].dpad_lr < 0)
|
|
{
|
|
p->clonenum--;
|
|
if (p->clonenum < 0)
|
|
p->clonenum = numclones-1;
|
|
p->rotate = -CSROTATETICS;
|
|
p->delay = CSROTATETICS;
|
|
S_StartSound(NULL, sfx_s3kc3s);
|
|
}
|
|
|
|
if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/)
|
|
{
|
|
p->mdepth = CSSTEP_COLORS;
|
|
S_StartSound(NULL, sfx_s3k63);
|
|
M_SetMenuDelay(num);
|
|
}
|
|
else if (M_MenuBackPressed(num))
|
|
{
|
|
p->mdepth = CSSTEP_CHARS;
|
|
S_StartSound(NULL, sfx_s3k5b);
|
|
M_SetMenuDelay(num);
|
|
}
|
|
else if (M_MenuExtraPressed(num))
|
|
{
|
|
p->clonenum = 0;
|
|
p->rotate = CSROTATETICS;
|
|
p->hitlag = true;
|
|
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
|
|
M_SetMenuDelay(num);
|
|
}
|
|
}
|
|
|
|
static void M_HandleColorRotate(setup_player_t *p, UINT8 num)
|
|
{
|
|
boolean forceskin = (Playing() && K_CanChangeRules(true) == true) && (cv_forceskin.value != -1);
|
|
|
|
if (cv_splitdevice.value)
|
|
num = 0;
|
|
|
|
if (menucmd[num].dpad_lr > 0)
|
|
{
|
|
p->color = M_GetColorAfter(p->color, 1, false);
|
|
p->rotate = CSROTATETICS;
|
|
M_SetMenuDelay(num); //CSROTATETICS
|
|
S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s
|
|
}
|
|
else if (menucmd[num].dpad_lr < 0)
|
|
{
|
|
p->color = M_GetColorBefore(p->color, 1, false);
|
|
p->rotate = -CSROTATETICS;
|
|
M_SetMenuDelay(num); //CSROTATETICS
|
|
S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s
|
|
}
|
|
|
|
if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/)
|
|
{
|
|
p->mdepth = CSSTEP_FOLLOWERCATEGORY;
|
|
S_StartSound(NULL, sfx_s3k63);
|
|
M_SetMenuDelay(num);
|
|
}
|
|
else if (M_MenuBackPressed(num))
|
|
{
|
|
if (forceskin
|
|
|| setup_chargrid[p->gridx][p->gridy].numskins == 1)
|
|
{
|
|
p->mdepth = CSSTEP_CHARS; // Skip clones menu
|
|
}
|
|
else
|
|
{
|
|
p->mdepth = CSSTEP_ALTS;
|
|
}
|
|
S_StartSound(NULL, sfx_s3k5b);
|
|
M_SetMenuDelay(num);
|
|
}
|
|
else if (M_MenuExtraPressed(num))
|
|
{
|
|
if (p->skin >= 0)
|
|
{
|
|
p->color = skins[p->skin].prefcolor;
|
|
p->rotate = CSROTATETICS;
|
|
p->hitlag = true;
|
|
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
|
|
M_SetMenuDelay(num);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void M_AnimateFollower(setup_player_t *p)
|
|
{
|
|
if (--p->follower_tics <= 0)
|
|
{
|
|
|
|
// FF_ANIMATE; cycle through FRAMES and get back afterwards. This will be prominent amongst followers hence why it's being supported here.
|
|
if (p->follower_state->frame & FF_ANIMATE)
|
|
{
|
|
p->follower_frame++;
|
|
p->follower_tics = p->follower_state->var2;
|
|
if (p->follower_frame > (p->follower_state->frame & FF_FRAMEMASK) + p->follower_state->var1) // that's how it works, right?
|
|
p->follower_frame = p->follower_state->frame & FF_FRAMEMASK;
|
|
}
|
|
else
|
|
{
|
|
if (p->follower_state->nextstate != S_NULL)
|
|
p->follower_state = &states[p->follower_state->nextstate];
|
|
p->follower_tics = p->follower_state->tics;
|
|
/*if (p->follower_tics == -1)
|
|
p->follower_tics = 15; // er, what?*/
|
|
// get spritedef:
|
|
p->follower_frame = p->follower_state->frame & FF_FRAMEMASK;
|
|
}
|
|
}
|
|
|
|
p->follower_timer++;
|
|
}
|
|
|
|
static void M_HandleFollowerCategoryRotate(setup_player_t *p, UINT8 num)
|
|
{
|
|
if (cv_splitdevice.value)
|
|
num = 0;
|
|
|
|
if (menucmd[num].dpad_lr > 0)
|
|
{
|
|
p->followercategory++;
|
|
if (p->followercategory >= setup_numfollowercategories)
|
|
p->followercategory = -1;
|
|
|
|
p->rotate = CSROTATETICS;
|
|
p->delay = CSROTATETICS;
|
|
S_StartSound(NULL, sfx_s3kc3s);
|
|
}
|
|
else if (menucmd[num].dpad_lr < 0)
|
|
{
|
|
p->followercategory--;
|
|
if (p->followercategory < -1)
|
|
p->followercategory = setup_numfollowercategories-1;
|
|
|
|
p->rotate = -CSROTATETICS;
|
|
p->delay = CSROTATETICS;
|
|
S_StartSound(NULL, sfx_s3kc3s);
|
|
}
|
|
|
|
if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/)
|
|
{
|
|
if (p->followercategory < 0)
|
|
{
|
|
p->followern = -1;
|
|
p->mdepth = CSSTEP_READY;
|
|
p->delay = TICRATE;
|
|
M_SetupReadyExplosions(true, p->gridx, p->gridy, p->color);
|
|
S_StartSound(NULL, sfx_s3k4e);
|
|
}
|
|
else
|
|
{
|
|
if (p->followern < 0 || followers[p->followern].category != p->followercategory)
|
|
{
|
|
p->followern = 0;
|
|
while (p->followern < numfollowers
|
|
&& (followers[p->followern].category != setup_followercategories[p->followercategory][1]
|
|
|| !K_FollowerUsable(p->followern)))
|
|
p->followern++;
|
|
}
|
|
|
|
if (p->followern >= numfollowers)
|
|
{
|
|
p->followern = -1;
|
|
S_StartSound(NULL, sfx_s3kb2);
|
|
}
|
|
else
|
|
{
|
|
M_GetFollowerState(p);
|
|
p->mdepth = CSSTEP_FOLLOWER;
|
|
S_StartSound(NULL, sfx_s3k63);
|
|
}
|
|
}
|
|
|
|
M_SetMenuDelay(num);
|
|
}
|
|
else if (M_MenuBackPressed(num))
|
|
{
|
|
p->mdepth = CSSTEP_COLORS;
|
|
S_StartSound(NULL, sfx_s3k5b);
|
|
M_SetMenuDelay(num);
|
|
}
|
|
else if (M_MenuExtraPressed(num))
|
|
{
|
|
if (p->followercategory >= 0 || p->followern < 0 || p->followern >= numfollowers || followers[p->followern].category >= numfollowercategories)
|
|
p->followercategory = -1;
|
|
else
|
|
p->followercategory = followers[p->followern].category;
|
|
p->rotate = CSROTATETICS;
|
|
p->hitlag = true;
|
|
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
|
|
M_SetMenuDelay(num);
|
|
}
|
|
}
|
|
|
|
static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num)
|
|
{
|
|
INT16 startfollowern = p->followern;
|
|
|
|
if (cv_splitdevice.value)
|
|
num = 0;
|
|
|
|
if (menucmd[num].dpad_lr > 0)
|
|
{
|
|
do
|
|
{
|
|
p->followern++;
|
|
if (p->followern >= numfollowers)
|
|
p->followern = 0;
|
|
if (p->followern == startfollowern)
|
|
break;
|
|
}
|
|
while (followers[p->followern].category != setup_followercategories[p->followercategory][1] || !K_FollowerUsable(p->followern));
|
|
|
|
M_GetFollowerState(p);
|
|
|
|
p->rotate = CSROTATETICS;
|
|
p->delay = CSROTATETICS;
|
|
S_StartSound(NULL, sfx_s3kc3s);
|
|
}
|
|
else if (menucmd[num].dpad_lr < 0)
|
|
{
|
|
do
|
|
{
|
|
p->followern--;
|
|
if (p->followern < 0)
|
|
p->followern = numfollowers-1;
|
|
if (p->followern == startfollowern)
|
|
break;
|
|
}
|
|
while (followers[p->followern].category != setup_followercategories[p->followercategory][1] || !K_FollowerUsable(p->followern));
|
|
|
|
M_GetFollowerState(p);
|
|
|
|
p->rotate = -CSROTATETICS;
|
|
p->delay = CSROTATETICS;
|
|
S_StartSound(NULL, sfx_s3kc3s);
|
|
}
|
|
|
|
if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/)
|
|
{
|
|
if (p->followern > -1)
|
|
{
|
|
p->mdepth = CSSTEP_FOLLOWERCOLORS;
|
|
S_StartSound(NULL, sfx_s3k63);
|
|
}
|
|
else
|
|
{
|
|
p->mdepth = CSSTEP_READY;
|
|
p->delay = TICRATE;
|
|
M_SetupReadyExplosions(true, p->gridx, p->gridy, p->color);
|
|
S_StartSound(NULL, sfx_s3k4e);
|
|
}
|
|
|
|
M_SetMenuDelay(num);
|
|
}
|
|
else if (M_MenuBackPressed(num))
|
|
{
|
|
p->mdepth = CSSTEP_FOLLOWERCATEGORY;
|
|
S_StartSound(NULL, sfx_s3k5b);
|
|
M_SetMenuDelay(num);
|
|
}
|
|
else if (M_MenuExtraPressed(num))
|
|
{
|
|
p->mdepth = CSSTEP_FOLLOWERCATEGORY;
|
|
p->followercategory = -1;
|
|
p->rotate = CSROTATETICS;
|
|
p->hitlag = true;
|
|
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
|
|
M_SetMenuDelay(num);
|
|
}
|
|
}
|
|
|
|
static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num)
|
|
{
|
|
if (cv_splitdevice.value)
|
|
num = 0;
|
|
|
|
M_AnimateFollower(p);
|
|
|
|
if (menucmd[num].dpad_lr > 0)
|
|
{
|
|
p->followercolor = M_GetColorAfter(p->followercolor, 1, true);
|
|
p->rotate = CSROTATETICS;
|
|
M_SetMenuDelay(num); //CSROTATETICS
|
|
S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s
|
|
}
|
|
else if (menucmd[num].dpad_lr < 0)
|
|
{
|
|
p->followercolor = M_GetColorBefore(p->followercolor, 1, true);
|
|
p->rotate = -CSROTATETICS;
|
|
M_SetMenuDelay(num); //CSROTATETICS
|
|
S_StartSound(NULL, sfx_s3k5b); //sfx_s3kc3s
|
|
}
|
|
|
|
if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/)
|
|
{
|
|
p->mdepth = CSSTEP_READY;
|
|
p->delay = TICRATE;
|
|
M_SetupReadyExplosions(true, p->gridx, p->gridy, p->color);
|
|
S_StartSound(NULL, sfx_s3k4e);
|
|
M_SetMenuDelay(num);
|
|
}
|
|
else if (M_MenuBackPressed(num))
|
|
{
|
|
M_GetFollowerState(p);
|
|
p->mdepth = CSSTEP_FOLLOWER;
|
|
S_StartSound(NULL, sfx_s3k5b);
|
|
M_SetMenuDelay(num);
|
|
}
|
|
else if (M_MenuExtraPressed(num))
|
|
{
|
|
if (p->followercolor == FOLLOWERCOLOR_MATCH)
|
|
p->followercolor = FOLLOWERCOLOR_OPPOSITE;
|
|
else if (p->followercolor == followers[p->followern].defaultcolor)
|
|
p->followercolor = FOLLOWERCOLOR_MATCH;
|
|
else
|
|
p->followercolor = followers[p->followern].defaultcolor;
|
|
p->rotate = CSROTATETICS;
|
|
p->hitlag = true;
|
|
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
|
|
M_SetMenuDelay(num);
|
|
}
|
|
}
|
|
|
|
boolean M_CharacterSelectHandler(INT32 choice)
|
|
{
|
|
INT32 i;
|
|
boolean forceskin = (Playing() && K_CanChangeRules(true) == true) && (cv_forceskin.value != -1);
|
|
|
|
(void)choice;
|
|
|
|
for (i = MAXSPLITSCREENPLAYERS-1; i >= 0; i--)
|
|
{
|
|
setup_player_t *p = &setup_player[i];
|
|
boolean playersChanged = false;
|
|
|
|
if (p->delay == 0 && menucmd[i].delay == 0)
|
|
{
|
|
if (!optionsmenu.profile)
|
|
{
|
|
// If splitdevice is true, only do the last non-ready setups.
|
|
if (cv_splitdevice.value)
|
|
{
|
|
// Previous setup isn't ready, go there.
|
|
// In any case, do setup 0 first.
|
|
if (i > 0 && setup_player[i-1].mdepth < CSSTEP_READY)
|
|
continue;
|
|
}
|
|
|
|
if (M_MenuButtonPressed(i, MBT_R))
|
|
{
|
|
p->showextra ^= true;
|
|
}
|
|
}
|
|
|
|
switch (p->mdepth)
|
|
{
|
|
case CSSTEP_NONE: // Enter Game
|
|
if (gamestate == GS_MENU) // do NOT handle that outside of GS_MENU.
|
|
playersChanged = M_HandlePressStart(p, i);
|
|
break;
|
|
case CSSTEP_PROFILE:
|
|
playersChanged = M_HandleCSelectProfile(p, i);
|
|
break;
|
|
case CSSTEP_ASKCHANGES:
|
|
M_HandleCharAskChange(p, i);
|
|
break;
|
|
case CSSTEP_CHARS: // Character Select grid
|
|
M_HandleCharacterGrid(p, i);
|
|
break;
|
|
case CSSTEP_ALTS: // Select clone
|
|
M_HandleCharRotate(p, i);
|
|
break;
|
|
case CSSTEP_COLORS: // Select color
|
|
M_HandleColorRotate(p, i);
|
|
break;
|
|
case CSSTEP_FOLLOWERCATEGORY:
|
|
M_HandleFollowerCategoryRotate(p, i);
|
|
break;
|
|
case CSSTEP_FOLLOWER:
|
|
M_HandleFollowerRotate(p, i);
|
|
break;
|
|
case CSSTEP_FOLLOWERCOLORS:
|
|
M_HandleFollowerColorRotate(p, i);
|
|
break;
|
|
case CSSTEP_READY:
|
|
default: // Unready
|
|
if (M_MenuBackPressed(i))
|
|
{
|
|
p->mdepth = CSSTEP_COLORS;
|
|
S_StartSound(NULL, sfx_s3k5b);
|
|
M_SetMenuDelay(i);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Just makes it easier to access later
|
|
if (forceskin)
|
|
{
|
|
if (p->gridx != skins[cv_forceskin.value].kartspeed-1
|
|
|| p->gridy != skins[cv_forceskin.value].kartweight-1)
|
|
p->skin = -1;
|
|
else
|
|
p->skin = cv_forceskin.value;
|
|
}
|
|
else
|
|
{
|
|
p->skin = setup_chargrid[p->gridx][p->gridy].skinlist[p->clonenum];
|
|
}
|
|
|
|
// Keep profile colour.
|
|
/*if (p->mdepth < CSSTEP_COLORS)
|
|
{
|
|
p->color = skins[p->skin].prefcolor;
|
|
|
|
}*/
|
|
|
|
if (playersChanged == true)
|
|
{
|
|
setup_page = 0; // reset that.
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Setup new numplayers
|
|
setup_numplayers = 0;
|
|
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
|
{
|
|
if (setup_player[i].mdepth == CSSTEP_NONE)
|
|
break;
|
|
|
|
setup_numplayers = i+1;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Apply character skin and colour changes while ingame (we just call the skin / color commands.)
|
|
// ...Will this cause command buffer issues? -Lat'
|
|
static void M_MPConfirmCharacterSelection(void)
|
|
{
|
|
UINT8 i;
|
|
INT16 col;
|
|
|
|
for (i = 0; i < splitscreen +1; i++)
|
|
{
|
|
// colour
|
|
// (convert the number that's saved to a string we can use)
|
|
col = setup_player[i].color;
|
|
CV_StealthSetValue(&cv_playercolor[i], col);
|
|
|
|
// follower
|
|
if (setup_player[i].followern < 0)
|
|
CV_StealthSet(&cv_follower[i], "None");
|
|
else
|
|
CV_StealthSet(&cv_follower[i], followers[setup_player[i].followern].name);
|
|
|
|
// finally, call the skin[x] console command.
|
|
// This will call SendNameAndColor which will synch everything we sent here and apply the changes!
|
|
|
|
CV_StealthSet(&cv_skin[i], skins[setup_player[i].skin].name);
|
|
|
|
// ...actually, let's do this last - Skin_OnChange has some return-early occasions
|
|
// follower color
|
|
CV_SetValue(&cv_followercolor[i], setup_player[i].followercolor);
|
|
|
|
}
|
|
M_ClearMenus(true);
|
|
}
|
|
|
|
void M_CharacterSelectTick(void)
|
|
{
|
|
UINT8 i;
|
|
boolean setupnext = true;
|
|
|
|
setup_animcounter++;
|
|
|
|
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
|
{
|
|
if (setup_player[i].delay)
|
|
setup_player[i].delay--;
|
|
|
|
if (setup_player[i].rotate > 0)
|
|
setup_player[i].rotate--;
|
|
else if (setup_player[i].rotate < 0)
|
|
setup_player[i].rotate++;
|
|
else
|
|
setup_player[i].hitlag = false;
|
|
|
|
if (i >= setup_numplayers)
|
|
continue;
|
|
|
|
if (setup_player[i].mdepth < CSSTEP_READY || setup_player[i].delay > 0)
|
|
{
|
|
// Someone's not ready yet.
|
|
setupnext = false;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < CSEXPLOSIONS; i++)
|
|
{
|
|
if (setup_explosions[i].tics > 0)
|
|
setup_explosions[i].tics--;
|
|
}
|
|
|
|
if (setupnext && setup_numplayers > 0)
|
|
{
|
|
// Selecting from the menu
|
|
if (gamestate == GS_MENU)
|
|
{
|
|
// in a profile; update the selected profile and then go back to the profile menu.
|
|
if (optionsmenu.profile)
|
|
{
|
|
// save player
|
|
strcpy(optionsmenu.profile->skinname, skins[setup_player[0].skin].name);
|
|
optionsmenu.profile->color = setup_player[0].color;
|
|
|
|
// save follower
|
|
strcpy(optionsmenu.profile->follower, followers[setup_player[0].followern].name);
|
|
optionsmenu.profile->followercolor = setup_player[0].followercolor;
|
|
|
|
// reset setup_player
|
|
memset(setup_player, 0, sizeof(setup_player));
|
|
setup_numplayers = 0;
|
|
|
|
M_GoBack(0);
|
|
return;
|
|
}
|
|
else // in a normal menu, stealthset the cvars and then go to the play menu.
|
|
{
|
|
for (i = 0; i < setup_numplayers; i++)
|
|
{
|
|
CV_StealthSet(&cv_skin[i], skins[setup_player[i].skin].name);
|
|
CV_StealthSetValue(&cv_playercolor[i], setup_player[i].color);
|
|
|
|
if (setup_player[i].followern < 0)
|
|
CV_StealthSet(&cv_follower[i], "None");
|
|
else
|
|
CV_StealthSet(&cv_follower[i], followers[setup_player[i].followern].name);
|
|
CV_StealthSetValue(&cv_followercolor[i], setup_player[i].followercolor);
|
|
|
|
G_SetPlayerGamepadIndicatorToPlayerColor(i);
|
|
}
|
|
|
|
CV_StealthSetValue(&cv_splitplayers, setup_numplayers);
|
|
|
|
#if defined (TESTERS)
|
|
M_MPOptSelectInit(0);
|
|
#else
|
|
M_SetupPlayMenu(0);
|
|
#endif
|
|
|
|
}
|
|
}
|
|
else // In a game
|
|
{
|
|
// 23/05/2022: Since there's already restrictskinchange, just allow this to happen regardless.
|
|
M_MPConfirmCharacterSelection();
|
|
}
|
|
}
|
|
}
|
|
|
|
boolean M_CharacterSelectQuit(void)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static void Splitplayers_OnChange(void)
|
|
{
|
|
#if 0
|
|
if (cv_splitplayers.value < setupm_pselect)
|
|
setupm_pselect = 1;
|
|
#endif
|
|
}
|