Merge branch 'master' of https://git.do.srb2.org/KartKrew/Kart into ironman

# Conflicts:
#	src/g_demo.c
This commit is contained in:
toaster 2022-11-21 15:49:29 +00:00
commit e65d17cd4d
54 changed files with 2017 additions and 1057 deletions

View file

@ -1080,9 +1080,10 @@ static void COM_Toggle_f(void)
for (i = 2; i < COM_Argc() - 1; ++i)
{
const char *str = COM_Argv(i);
INT32 val;
INT32 val = 0;
if (CV_CompleteValue(cvar, &str, &val))
if (!cvar->PossibleValue ||
CV_CompleteValue(cvar, &str, &val))
{
if (str ? !stricmp(cvar->string, str)
: cvar->value == val)

View file

@ -531,8 +531,10 @@ typedef enum
CL_SEARCHING,
CL_CHECKFILES,
CL_DOWNLOADFILES,
CL_DOWNLOADFAILED,
CL_ASKJOIN,
CL_LOADFILES,
CL_SETUPFILES,
CL_WAITJOINRESPONSE,
CL_DOWNLOADSAVEGAME,
CL_CONNECTED,
@ -615,8 +617,12 @@ static inline void CL_DrawConnectionStatus(void)
break;
case CL_ASKFULLFILELIST:
case CL_CONFIRMCONNECT:
case CL_DOWNLOADFAILED:
cltext = "";
break;
case CL_SETUPFILES:
cltext = M_GetText("Configuring addons...");
break;
case CL_ASKJOIN:
case CL_WAITJOINRESPONSE:
if (serverisfull)
@ -655,8 +661,8 @@ static inline void CL_DrawConnectionStatus(void)
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, "Checking server addons...");
totalfileslength = (INT32)((checkednum/(double)(fileneedednum)) * 256);
M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 175);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, totalfileslength, 8, 160);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 111);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, totalfileslength, 8, 96);
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
va(" %2u/%2u Files",checkednum,fileneedednum));
}
@ -677,8 +683,8 @@ static inline void CL_DrawConnectionStatus(void)
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, "Loading server addons...");
totalfileslength = (INT32)((loadcompletednum/(double)(fileneedednum)) * 256);
M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 175);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, totalfileslength, 8, 160);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 111);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, totalfileslength, 8, 96);
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
va(" %2u/%2u Files",loadcompletednum,fileneedednum));
}
@ -719,8 +725,10 @@ static inline void CL_DrawConnectionStatus(void)
strncpy(tempname, filename, sizeof(tempname)-1);
}
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-30, 0,
va(M_GetText("%s downloading"), ((cl_mode == CL_DOWNLOADHTTPFILES) ? "\x82""HTTP" : "\x85""Direct")));
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-22, V_YELLOWMAP,
va(M_GetText("Downloading \"%s\""), tempname));
va(M_GetText("\"%s\""), tempname));
V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-58, V_20TRANS|V_MONOSPACE,
va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,file->totalsize>>10));
V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-58, V_20TRANS|V_MONOSPACE,
@ -736,8 +744,8 @@ static inline void CL_DrawConnectionStatus(void)
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-14, V_YELLOWMAP, "Overall Download Progress");
totalfileslength = (INT32)((totaldldsize/(double)totalfilesrequestedsize) * 256);
M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 175);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, totalfileslength, 8, 160);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 111);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, totalfileslength, 8, 96);
if (totalfilesrequestedsize>>20 >= 10) //display in MB if over 10MB
V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
@ -1497,6 +1505,10 @@ static void M_ConfirmConnect(void)
{
cl_mode = CL_DOWNLOADFILES;
}
else
{
cl_mode = CL_DOWNLOADFAILED;
}
}
#ifdef HAVE_CURL
else
@ -1645,6 +1657,10 @@ static boolean CL_FinishedFileList(void)
{
cl_mode = CL_DOWNLOADFILES;
}
else
{
cl_mode = CL_DOWNLOADFAILED;
}
}
#endif
}
@ -1850,8 +1866,28 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
cl_mode = CL_LOADFILES;
break;
case CL_DOWNLOADFAILED:
{
CONS_Printf(M_GetText("Legacy downloader request packet failed.\n"));
CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
D_QuitNetGame();
CL_Reset();
D_StartTitle();
M_StartMessage(M_GetText(
"The direct download encountered an error.\n"
"See the logfile for more info.\n"
"\n"
"Press (B)\n"
), NULL, MM_NOTHING);
return false;
}
case CL_LOADFILES:
if (CL_LoadServerFiles())
if (CL_LoadServerFiles())
cl_mode = CL_SETUPFILES;
break;
case CL_SETUPFILES:
if (P_PartialAddGetStage() < 0 || P_MultiSetupWadFiles(false))
{
*asksent = 0; //This ensure the first join ask is right away
firstconnectattempttime = I_GetTime();
@ -2079,7 +2115,12 @@ static void CL_ConnectToServer(void)
{
// If the connection was aborted for some reason, leave
if (!CL_ServerConnectionTicker(tmpsave, &oldtic, &asksent))
{
if (P_PartialAddGetStage() >= 0)
P_MultiSetupWadFiles(true); // in case any partial adds were done
return;
}
if (server)
{
@ -2487,6 +2528,11 @@ void CL_ClearPlayer(INT32 playernum)
{
int i;
if (players[playernum].follower)
{
K_RemoveFollower(&players[playernum]);
}
if (players[playernum].mo)
{
P_RemoveMobj(players[playernum].mo);
@ -3927,6 +3973,11 @@ static void HandleConnect(SINT8 node)
// If a server filled out, then it'd overwrite the host and turn everyone into weird husks.....
// It's too much effort to legimately fix right now. Just prevent it from reaching that state.
UINT8 maxplayers = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxconnections.value);
UINT8 connectedplayers = 0;
for (i = dedicated ? 1 : 0; i < MAXPLAYERS; i++)
if (playernode[i] != UINT8_MAX) // We use this to count players because it is affected by SV_AddWaitingPlayers when more than one client joins on the same tic, unlike playeringame and D_NumPlayers. UINT8_MAX denotes no node for that player
connectedplayers++;
if (bannednode && bannednode[node].banid != SIZE_MAX)
{
@ -3984,7 +4035,7 @@ static void HandleConnect(SINT8 node)
{
SV_SendRefuse(node, M_GetText("The server is not accepting\njoins for the moment."));
}
else if (D_NumPlayers() >= maxplayers)
else if (connectedplayers >= maxplayers)
{
SV_SendRefuse(node, va(M_GetText("Maximum players reached: %d"), maxplayers));
}
@ -3992,7 +4043,7 @@ static void HandleConnect(SINT8 node)
{
SV_SendRefuse(node, M_GetText("Too many players from\nthis node."));
}
else if (netgame && D_NumPlayers() + netbuffer->u.clientcfg.localplayers > maxplayers)
else if (netgame && connectedplayers + netbuffer->u.clientcfg.localplayers > maxplayers)
{
SV_SendRefuse(node, va(M_GetText("Number of local players\nwould exceed maximum: %d"), maxplayers));
}
@ -5475,14 +5526,6 @@ boolean TryRunTics(tic_t realtics)
if (ticking)
{
if (advancedemo)
{
if (timedemo_quit)
COM_ImmedExecute("quit");
else
D_StartTitle();
}
else
{
// run the count * tics
while (neededtic > gametic)

View file

@ -131,7 +131,6 @@ INT32 postimgparam[MAXSPLITSCREENPLAYERS];
boolean sound_disabled = false;
boolean digital_disabled = false;
boolean advancedemo;
#ifdef DEBUGFILE
INT32 debugload = 0;
#endif
@ -913,15 +912,6 @@ void D_SRB2Loop(void)
}
}
//
// D_AdvanceDemo
// Called after each demo or intro demosequence finishes
//
void D_AdvanceDemo(void)
{
advancedemo = true;
}
// =========================================================================
// D_SRB2Main
// =========================================================================
@ -997,13 +987,13 @@ void D_StartTitle(void)
//demosequence = -1;
G_SetGametype(GT_RACE); // SRB2kart
paused = false;
advancedemo = false;
// clear cmd building stuff
memset(gamekeydown, 0, sizeof (gamekeydown));
memset(deviceResponding, false, sizeof (deviceResponding));
F_StartTitleScreen();
M_ClearMenus(false);
// Reset the palette
if (rendermode != render_none)
@ -1214,8 +1204,6 @@ void D_SRB2Main(void)
{
INT32 i, p;
INT32 numbasemapheaders;
INT32 pstartmap = 1;
boolean autostart = false;
@ -1472,9 +1460,7 @@ void D_SRB2Main(void)
//
// search for mainwad maps
//
P_InitMapData(0);
numbasemapheaders = nummapheaders;
P_InitMapData(false);
CON_SetLoadingProgress(LOADED_IWAD);
@ -1485,7 +1471,7 @@ void D_SRB2Main(void)
//
// search for pwad maps
//
P_InitMapData(numbasemapheaders);
P_InitMapData(true);
CON_SetLoadingProgress(LOADED_PWAD);

View file

@ -18,8 +18,6 @@
#include "d_event.h"
#include "w_wad.h" // for MAX_WADFILES
extern boolean advancedemo;
// make sure not to write back the config until it's been correctly loaded
extern tic_t rendergametic;
@ -52,7 +50,6 @@ const char *D_Home(void);
//
// BASE LEVEL
//
void D_AdvanceDemo(void);
void D_StartTitle(void);
#endif //__D_MAIN__

View file

@ -295,10 +295,10 @@ consvar_t cv_follower[MAXSPLITSCREENPLAYERS] = {
// player's follower colors... Also saved...
consvar_t cv_followercolor[MAXSPLITSCREENPLAYERS] = {
CVAR_INIT ("followercolor", "1", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor_OnChange),
CVAR_INIT ("followercolor2", "1", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor2_OnChange),
CVAR_INIT ("followercolor3", "1", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor3_OnChange),
CVAR_INIT ("followercolor4", "1", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor4_OnChange)
CVAR_INIT ("followercolor", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor_OnChange),
CVAR_INIT ("followercolor2", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor2_OnChange),
CVAR_INIT ("followercolor3", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor3_OnChange),
CVAR_INIT ("followercolor4", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor4_OnChange)
};
// last selected profile, unaccessible cvar only set internally but is saved.
@ -386,7 +386,6 @@ consvar_t cv_gardentop = CVAR_INIT ("gardentop", "On", CV_NETVAR, CV_OnOff,
consvar_t cv_dualsneaker = CVAR_INIT ("dualsneaker", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_triplesneaker = CVAR_INIT ("triplesneaker", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_triplebanana = CVAR_INIT ("triplebanana", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_decabanana = CVAR_INIT ("decabanana", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_tripleorbinaut = CVAR_INIT ("tripleorbinaut", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_quadorbinaut = CVAR_INIT ("quadorbinaut", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_dualjawz = CVAR_INIT ("dualjawz", "On", CV_NETVAR, CV_OnOff, NULL);
@ -831,27 +830,6 @@ void D_RegisterClientCommands(void)
{
INT32 i;
for (i = 0; i < MAXSKINCOLORS; i++)
{
Color_cons_t[i].value = i;
Color_cons_t[i].strvalue = skincolors[i].name;
}
for (i = 2; i < MAXSKINCOLORS; i++)
{
Followercolor_cons_t[i].value = i-2;
Followercolor_cons_t[i].strvalue = skincolors[i-2].name;
}
Followercolor_cons_t[1].value = FOLLOWERCOLOR_MATCH;
Followercolor_cons_t[1].strvalue = "Match"; // Add "Match" option, which will make the follower color match the player's
Followercolor_cons_t[0].value = FOLLOWERCOLOR_OPPOSITE;
Followercolor_cons_t[0].strvalue = "Opposite"; // Add "Opposite" option, ...which is like "Match", but for coloropposite.
Color_cons_t[MAXSKINCOLORS].value = Followercolor_cons_t[MAXSKINCOLORS+2].value = 0;
Color_cons_t[MAXSKINCOLORS].strvalue = Followercolor_cons_t[MAXSKINCOLORS+2].strvalue = NULL;
// Set default player names
// Monster Iestyn (12/08/19): not sure where else I could have actually put this, but oh well
for (i = 0; i < MAXPLAYERS; i++)
@ -986,6 +964,9 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_bsaturation);
CV_RegisterVar(&cv_msaturation);
CV_RegisterVar(&cv_palette);
CV_RegisterVar(&cv_palettenum);
// k_menu.c
//CV_RegisterVar(&cv_compactscoreboard);
CV_RegisterVar(&cv_chatheight);
@ -1089,6 +1070,7 @@ void D_RegisterClientCommands(void)
COM_AddCommand("rteleport", Command_RTeleport_f);
COM_AddCommand("skynum", Command_Skynum_f);
COM_AddCommand("weather", Command_Weather_f);
COM_AddCommand("grayscale", Command_Grayscale_f);
#ifdef _DEBUG
COM_AddCommand("causecfail", Command_CauseCfail_f);
#endif
@ -1489,7 +1471,7 @@ static void SendNameAndColor(UINT8 n)
const INT32 playernum = g_localplayers[n];
player_t *player = &players[playernum];
char buf[MAXPLAYERNAME+9];
char buf[MAXPLAYERNAME+12];
char *p;
if (splitscreen < n)
@ -1520,14 +1502,11 @@ static void SendNameAndColor(UINT8 n)
if (!cv_followercolor[n].value)
CV_StealthSet(&cv_followercolor[n], "Match"); // set it to "Match". I don't care about your stupidity!
// so like, this is sent before we even use anything like cvars or w/e so it's possible that follower is set to a pretty yikes value, so let's fix that before we send garbage that could crash the game:
if (cv_follower[n].value >= numfollowers || cv_follower[n].value < -1)
CV_StealthSet(&cv_follower[n], "-1");
if (!strcmp(cv_playername[n].string, player_names[playernum])
&& cv_playercolor[n].value == player->skincolor
&& !strcmp(cv_skin[n].string, skins[player->skin].name)
&& cv_follower[n].value == player->followerskin
&& !stricmp(cv_skin[n].string, skins[player->skin].name)
&& !stricmp(cv_follower[n].string,
(player->followerskin < 0 ? "None" : followers[player->followerskin].name))
&& cv_followercolor[n].value == player->followercolor)
return;
@ -1549,31 +1528,28 @@ static void SendNameAndColor(UINT8 n)
K_KartResetPlayerColor(player);
// Update follower for local games:
if (cv_follower[n].value >= -1 && cv_follower[n].value != player->followerskin)
K_SetFollowerByNum(playernum, cv_follower[n].value);
player->followercolor = cv_followercolor[n].value;
if (metalrecording && n == 0)
{ // Starring Metal Sonic as themselves, obviously.
SetPlayerSkinByNum(playernum, 5);
CV_StealthSet(&cv_skin[n], skins[5].name);
}
else if ((foundskin = R_SkinAvailable(cv_skin[n].string)) != -1 && R_SkinUsable(playernum, foundskin))
if ((foundskin = R_SkinAvailable(cv_skin[n].string)) != -1 && R_SkinUsable(playernum, foundskin))
{
cv_skin[n].value = foundskin;
SetPlayerSkin(playernum, cv_skin[n].string);
CV_StealthSet(&cv_skin[n], skins[cv_skin[n].value].name);
CV_StealthSet(&cv_skin[n], skins[foundskin].name);
cv_skin[n].value = foundskin;
}
else
{
cv_skin[n].value = players[playernum].skin;
CV_StealthSet(&cv_skin[n], skins[player->skin].name);
cv_skin[n].value = player->skin;
// will always be same as current
SetPlayerSkin(playernum, cv_skin[n].string);
}
player->followercolor = cv_followercolor[n].value;
// Update follower for local games:
foundskin = K_FollowerAvailable(cv_follower[n].string);
CV_StealthSet(&cv_follower[n], (foundskin == -1) ? "None" : followers[foundskin].name);
cv_follower[n].value = foundskin;
K_SetFollowerByNum(playernum, foundskin);
return;
}
@ -1603,12 +1579,20 @@ static void SendNameAndColor(UINT8 n)
cv_skin[n].value = 0;
}
cv_follower[n].value = K_FollowerAvailable(cv_follower[n].string);
if (cv_follower[n].value < 0)
{
CV_StealthSet(&cv_follower[n], "None");
cv_follower[n].value = -1;
}
// Finally write out the complete packet and send it off.
WRITESTRINGN(p, cv_playername[n].zstring, MAXPLAYERNAME);
WRITEUINT32(p, (UINT32)player->availabilities);
WRITEUINT16(p, (UINT16)cv_playercolor[n].value);
WRITEUINT8(p, (UINT8)cv_skin[n].value);
WRITESINT8(p, (SINT8)cv_follower[n].value);
WRITEINT16(p, (INT16)cv_follower[n].value);
//CONS_Printf("Sending follower id %d\n", (INT16)cv_follower[n].value);
WRITEUINT16(p, (UINT16)cv_followercolor[n].value);
SendNetXCmdForPlayer(n, XD_NAMEANDCOLOR, buf, p - buf);
@ -1620,7 +1604,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
char name[MAXPLAYERNAME+1];
UINT16 color, followercolor;
UINT8 skin;
SINT8 follower;
INT16 follower;
SINT8 localplayer = -1;
UINT8 i;
@ -1649,7 +1633,8 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
p->availabilities = READUINT32(*cp);
color = READUINT16(*cp);
skin = READUINT8(*cp);
follower = READSINT8(*cp);
follower = READINT16(*cp);
//CONS_Printf("Recieved follower id %d\n", follower);
followercolor = READUINT16(*cp);
// set name
@ -6075,6 +6060,7 @@ static void Name_OnChange(void)
{
CONS_Alert(CONS_NOTICE, M_GetText("You may not change your name when chat is muted.\n"));
CV_StealthSet(&cv_playername[0], player_names[consoleplayer]);
return;
}
else
SendNameAndColor(0);
@ -6115,207 +6101,56 @@ static void Name4_OnChange(void)
}
// sends the follower change for players
static void Follower_OnChange(void)
static void FollowerAny_OnChange(UINT8 pnum)
{
char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1];
INT32 num;
// there is a slight chance that we will actually use a string instead so...
// let's investigate the string...
strcpy(str, cv_follower[0].string);
strcpy(cpy, cv_follower[0].string);
strlwr(str);
if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright...
{
if (stricmp(cpy, "None") == 0)
{
CV_StealthSet(&cv_follower[0], "-1");
if (!Playing())
return; // don't send anything there.
SendNameAndColor(0);
return;
}
num = K_FollowerAvailable(str);
if (num == -1) // that's an error.
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
CV_StealthSet(&cv_follower[0], str);
cv_follower[0].value = num;
}
if (!Playing())
return; // don't send anything there.
SendNameAndColor(0);
SendNameAndColor(pnum);
}
// sends the follower change for players
static void Follower_OnChange(void)
{
FollowerAny_OnChange(0);
}
// About the same as Color_OnChange but for followers.
static void Followercolor_OnChange(void)
{
if (!Playing())
return; // do whatever you want if you aren't in the game or don't have a follower.
if (!P_PlayerMoving(consoleplayer))
{
// Color change menu scrolling fix is no longer necessary
SendNameAndColor(0);
}
FollowerAny_OnChange(0);
}
// repeat for the 3 other players
static void Follower2_OnChange(void)
{
char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1];
INT32 num;
// there is a slight chance that we will actually use a string instead so...
// let's investigate the string...
strcpy(str, cv_follower[1].string);
strcpy(cpy, cv_follower[1].string);
strlwr(str);
if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright...
{
if (stricmp(cpy, "None") == 0)
{
CV_StealthSet(&cv_follower[1], "-1");
if (!Playing())
return; // don't send anything there.
SendNameAndColor(1);
return;
}
num = K_FollowerAvailable(str);
if (num == -1) // that's an error.
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
CV_StealthSet(&cv_follower[1], str);
cv_follower[1].value = num;
}
if (!Playing())
return; // don't send anything there.
SendNameAndColor(1);
FollowerAny_OnChange(1);
}
static void Followercolor2_OnChange(void)
{
if (!Playing())
return; // do whatever you want if you aren't in the game or don't have a follower.
if (!P_PlayerMoving(g_localplayers[1]))
{
// Color change menu scrolling fix is no longer necessary
SendNameAndColor(1);
}
FollowerAny_OnChange(1);
}
static void Follower3_OnChange(void)
{
char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1];
INT32 num;
// there is a slight chance that we will actually use a string instead so...
// let's investigate the string...
strcpy(str, cv_follower[2].string);
strcpy(cpy, cv_follower[2].string);
strlwr(str);
if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright...
{
if (stricmp(cpy, "None") == 0)
{
CV_StealthSet(&cv_follower[2], "-1");
if (!Playing())
return; // don't send anything there.
SendNameAndColor(2);
return;
}
num = K_FollowerAvailable(str);
if (num == -1) // that's an error.
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
CV_StealthSet(&cv_follower[2], str);
cv_follower[2].value = num;
}
if (!Playing())
return; // don't send anything there.
SendNameAndColor(2);
FollowerAny_OnChange(2);
}
static void Followercolor3_OnChange(void)
{
if (!Playing())
return; // do whatever you want if you aren't in the game or don't have a follower.
if (!P_PlayerMoving(g_localplayers[2]))
{
// Color change menu scrolling fix is no longer necessary
SendNameAndColor(2);
}
FollowerAny_OnChange(2);
}
static void Follower4_OnChange(void)
{
char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1];
INT32 num;
// there is a slight chance that we will actually use a string instead so...
// let's investigate the string...
strcpy(str, cv_follower[3].string);
strcpy(cpy, cv_follower[3].string);
strlwr(str);
if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright...
{
if (stricmp(cpy, "None") == 0)
{
CV_StealthSet(&cv_follower[3], "-1");
if (!Playing())
return; // don't send anything there.
SendNameAndColor(3);
return;
}
num = K_FollowerAvailable(str);
if (num == -1) // that's an error.
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
CV_StealthSet(&cv_follower[3], str);
cv_follower[3].value = num;
}
if (!Playing())
return; // don't send anything there.
SendNameAndColor(3);
FollowerAny_OnChange(3);
}
static void Followercolor4_OnChange(void)
{
if (!Playing())
return; // do whatever you want if you aren't in the game or don't have a follower.
if (!P_PlayerMoving(g_localplayers[3]))
{
// Color change menu scrolling fix is no longer necessary
SendNameAndColor(3);
}
FollowerAny_OnChange(3);
}
/** Sends a skin change for the console player, unless that player is moving. Also forces them to spectate if the change is done during gameplay
@ -6335,7 +6170,9 @@ static void Skin_OnChange(void)
}
if (CanChangeSkinWhilePlaying(consoleplayer))
{
SendNameAndColor(0);
}
else
{
CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n"));

View file

@ -100,7 +100,6 @@ extern consvar_t
cv_dualsneaker,
cv_triplesneaker,
cv_triplebanana,
cv_decabanana,
cv_tripleorbinaut,
cv_quadorbinaut,
cv_dualjawz;

View file

@ -354,6 +354,9 @@ void CL_AbortDownloadResume(void)
pauseddownload = NULL;
}
// The following was written and, against all odds, works.
#define MORELEGACYDOWNLOADER
/** Sends requests for files in the ::fileneeded table with a status of
* ::FS_NOTFOUND.
*
@ -366,42 +369,135 @@ boolean CL_SendFileRequest(void)
char *p;
INT32 i;
INT64 totalfreespaceneeded = 0, availablefreespace;
INT32 skippedafile = -1;
#ifdef MORELEGACYDOWNLOADER
boolean firstloop = true;
#endif
#ifdef PARANOIA
if (M_CheckParm("-nodownload"))
I_Error("Attempted to download files in -nodownload mode");
{
CONS_Printf("Direct download - Attempted to download files in -nodownload mode");
return false;
}
#endif
for (i = 0; i < fileneedednum; i++)
{
#ifdef PARANOIA
if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN
&& (fileneeded[i].willsend == 0 || fileneeded[i].willsend == 2))
{
I_Error("Attempted to download files that were not sendable");
CONS_Printf("Direct download - attempted to download files that were not sendable\n");
return false;
}
#endif
if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK))
{
// Error check for the first time around.
totalfreespaceneeded += fileneeded[i].totalsize;
}
}
I_GetDiskFreeSpace(&availablefreespace);
if (totalfreespaceneeded > availablefreespace)
{
CONS_Printf("Direct download -\n"
" To play on this server you must download %s KB,\n"
" but you have only %s KB free space on this drive\n",
sizeu1((size_t)(totalfreespaceneeded>>10)), sizeu2((size_t)(availablefreespace>>10)));
return false;
}
#ifdef MORELEGACYDOWNLOADER
tryagain:
skippedafile = -1;
#endif
#ifdef VERBOSEREQUESTFILE
CONS_Printf("Preparing packet\n");
#endif
netbuffer->packettype = PT_REQUESTFILE;
p = (char *)netbuffer->u.textcmd;
for (i = 0; i < fileneedednum; i++)
{
if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK))
{
totalfreespaceneeded += fileneeded[i].totalsize;
// Pre-prepare.
size_t checklen;
nameonly(fileneeded[i].filename);
// Figure out if we'd overrun our buffer.
checklen = strlen(fileneeded[i].filename)+2; // plus the fileid (and terminator, in case this is last)
if ((UINT8 *)(p + checklen) >= netbuffer->u.textcmd + MAXTEXTCMD)
{
skippedafile = i;
// we might have a shorter file that can fit in the remaining space, and file ID permits out-of-order data
continue;
}
// Now write.
WRITEUINT8(p, i); // fileid
WRITESTRINGN(p, fileneeded[i].filename, MAX_WADPATH);
#ifdef VERBOSEREQUESTFILE
CONS_Printf(" file \"%s\" (id %d)\n", i, fileneeded[i].filename);
#endif
// put it in download dir
strcatbf(fileneeded[i].filename, downloaddir, "/");
fileneeded[i].status = FS_REQUESTED;
}
WRITEUINT8(p, 0xFF);
I_GetDiskFreeSpace(&availablefreespace);
if (totalfreespaceneeded > availablefreespace)
I_Error("To play on this server you must download %s KB,\n"
"but you have only %s KB free space on this drive\n",
sizeu1((size_t)(totalfreespaceneeded>>10)), sizeu2((size_t)(availablefreespace>>10)));
}
// prepare to download
I_mkdir(downloaddir, 0755);
return HSendPacket(servernode, true, 0, p - (char *)netbuffer->u.textcmd);
#ifdef MORELEGACYDOWNLOADER
if (firstloop)
#else
// If we're not trying extralong legacy download requests, gotta bail.
if (skippedafile != -1)
{
CONS_Printf("Direct download - missing files are as follows:\n");
for (i = 0; i < fileneedednum; i++)
{
if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK || fileneeded[i].status == FS_REQUESTED)) // FS_REQUESTED added
CONS_Printf(" %s\n", fileneeded[i].filename);
}
return false;
}
#endif
I_mkdir(downloaddir, 0755);
#ifdef PARANOIA
// Couldn't fit a single one in?
if (p == (char *)netbuffer->u.textcmd)
{
CONS_Printf("Direct download - fileneeded name for %s (fileneeded[%d]) too long??\n", (skippedafile != -1 ? fileneeded[skippedafile].filename : NULL), skippedafile);
return false;
}
#endif
WRITEUINT8(p, 0xFF); // terminator
if (!HSendPacket(servernode, true, 0, p - (char *)netbuffer->u.textcmd))
{
CONS_Printf("Direct download - unable to send packet.\n");
return false;
}
#ifdef MORELEGACYDOWNLOADER
if (skippedafile != -1)
{
firstloop = false;
goto tryagain;
}
#endif
#ifdef VERBOSEREQUESTFILE
CONS_Printf("Returning true\n");
#endif
return true;
}
// get request filepak and put it on the send queue
@ -411,16 +507,18 @@ boolean PT_RequestFile(INT32 node)
char wad[MAX_WADPATH+1];
UINT8 *p = netbuffer->u.textcmd;
UINT8 id;
while (p < netbuffer->u.textcmd + MAXTEXTCMD-1) // Don't allow hacked client to overflow
while (p < netbuffer->u.textcmd + MAXTEXTCMD) // Don't allow hacked client to overflow
{
id = READUINT8(p);
if (id == 0xFF)
break;
READSTRINGN(p, wad, MAX_WADPATH);
if (!AddFileToSendQueue(node, wad, id))
if (p >= netbuffer->u.textcmd + MAXTEXTCMD || !AddFileToSendQueue(node, wad, id))
{
if (cv_noticedownload.value)
CONS_Printf("Bad PT_REQUESTFILE from node %d!\n", node);
SV_AbortSendFiles(node);
return false; // don't read the rest of the files
return false; // don't read any more
}
}
return true; // no problems with any files
@ -552,7 +650,7 @@ boolean CL_LoadServerFiles(void)
continue; // Already loaded
else if (fileneeded[i].status == FS_FOUND)
{
P_AddWadFile(fileneeded[i].filename);
P_PartialAddWadFile(fileneeded[i].filename);
G_SetGameModified(true, false);
fileneeded[i].status = FS_OPEN;
return false;
@ -798,7 +896,7 @@ static boolean AddFileToSendQueue(INT32 node, const char *filename, UINT8 fileid
char wadfilename[MAX_WADPATH];
if (cv_noticedownload.value)
CONS_Printf("Sending file \"%s\" to node %d (%s)\n", filename, node, I_GetNodeAddress(node));
CONS_Printf("Sending file \"%s\" (id %d) to node %d (%s)\n", filename, fileid, node, I_GetNodeAddress(node));
// Find the last file in the list and set a pointer to its "next" field
q = &transfer[node].txlist;
@ -972,7 +1070,7 @@ static void SV_EndFileSend(INT32 node)
{
case SF_FILE: // It's a file, close it and free its filename
if (cv_noticedownload.value)
CONS_Printf("Ending file transfer for node %d\n", node);
CONS_Printf("Ending file transfer (id %d) for node %d\n", p->fileid, node);
if (transfer[node].currentfile)
fclose(transfer[node].currentfile);
free(p->id.filename);
@ -1750,6 +1848,7 @@ void CURLGetFile(void)
int msgs_left; /* how many messages are left */
const char *easy_handle_error;
long response_code = 0;
static char *filename;
if (curl_runninghandles)
{
@ -1774,6 +1873,8 @@ void CURLGetFile(void)
{
e = m->easy_handle;
easyres = m->data.result;
filename = Z_StrDup(curl_realname);
nameonly(filename);
if (easyres != CURLE_OK)
{
if (easyres == CURLE_HTTP_RETURNED_ERROR)
@ -1786,21 +1887,30 @@ void CURLGetFile(void)
curl_failedwebdownload = true;
fclose(curl_curfile->file);
remove(curl_curfile->filename);
curl_curfile->file = NULL;
//nameonly(curl_curfile->filename);
nameonly(curl_realname);
CONS_Printf(M_GetText("Failed to download %s (%s)\n"), curl_realname, easy_handle_error);
CONS_Printf(M_GetText("Failed to download %s (%s)\n"), filename, easy_handle_error);
}
else
{
nameonly(curl_realname);
CONS_Printf(M_GetText("Finished downloading %s\n"), curl_realname);
downloadcompletednum++;
downloadcompletedsize += curl_curfile->totalsize;
curl_curfile->status = FS_FOUND;
fclose(curl_curfile->file);
if (checkfilemd5(curl_curfile->filename, curl_curfile->md5sum) == FS_MD5SUMBAD)
{
CONS_Alert(CONS_ERROR, M_GetText("HTTP Download of %s finished but is corrupt or has been modified\n"), filename);
curl_curfile->status = FS_FALLBACK;
curl_failedwebdownload = true;
}
else
{
CONS_Printf(M_GetText("Finished HTTP download of %s\n"), filename);
downloadcompletednum++;
downloadcompletedsize += curl_curfile->totalsize;
curl_curfile->status = FS_FOUND;
}
}
Z_Free(filename);
curl_curfile->file = NULL;
curl_running = false;
curl_transfers--;
curl_multi_remove_handle(multi_handle, e);

View file

@ -168,7 +168,6 @@ typedef enum
KRITEM_DUALSNEAKER = NUMKARTITEMS,
KRITEM_TRIPLESNEAKER,
KRITEM_TRIPLEBANANA,
KRITEM_TENFOLDBANANA,
KRITEM_TRIPLEORBINAUT,
KRITEM_QUADORBINAUT,
KRITEM_DUALJAWZ,
@ -415,7 +414,7 @@ typedef struct player_s
// Basic gameplay things
UINT8 position; // Used for Kart positions, mostly for deterministic stuff
UINT8 oldposition; // Used for taunting when you pass someone
UINT8 positiondelay; // Used for position number, so it can grow when passing/being passed
UINT8 positiondelay; // Used for position number, so it can grow when passing
UINT32 distancetofinish;
waypoint_t *nextwaypoint;
respawnvars_t respawn; // Respawn info

View file

@ -224,7 +224,10 @@ void readfreeslots(MYFILE *f)
// TODO: Out-of-slots warnings/errors.
// TODO: Name too long (truncated) warnings.
if (fastcmp(type, "SFX"))
{
CONS_Printf("Sound sfx_%s allocated.\n",word);
S_AddSoundFx(word, false, 0, false);
}
else if (fastcmp(type, "SPR"))
{
for (i = SPR_FIRSTFREESLOT; i <= SPR_LASTFREESLOT; i++)
@ -238,39 +241,54 @@ void readfreeslots(MYFILE *f)
// Found a free slot!
strncpy(sprnames[i],word,4);
//sprnames[i][4] = 0;
CONS_Printf("Sprite SPR_%s allocated.\n",word);
used_spr[(i-SPR_FIRSTFREESLOT)/8] |= 1<<(i%8); // Okay, this sprite slot has been named now.
break;
}
if (i > SPR_LASTFREESLOT)
I_Error("Out of Sprite Freeslots while allocating \"%s\"\nLoad less addons to fix this.", word);
}
else if (fastcmp(type, "S"))
{
for (i = 0; i < NUMSTATEFREESLOTS; i++)
if (!FREE_STATES[i]) {
CONS_Printf("State S_%s allocated.\n",word);
FREE_STATES[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL);
strcpy(FREE_STATES[i],word);
freeslotusage[0][0]++;
break;
}
if (i == NUMSTATEFREESLOTS)
I_Error("Out of State Freeslots while allocating \"%s\"\nLoad less addons to fix this.", word);
}
else if (fastcmp(type, "MT"))
{
for (i = 0; i < NUMMOBJFREESLOTS; i++)
if (!FREE_MOBJS[i]) {
CONS_Printf("MobjType MT_%s allocated.\n",word);
FREE_MOBJS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL);
strcpy(FREE_MOBJS[i],word);
freeslotusage[1][0]++;
break;
}
if (i == NUMMOBJFREESLOTS)
I_Error("Out of Mobj Freeslots while allocating \"%s\"\nLoad less addons to fix this.", word);
}
else if (fastcmp(type, "SKINCOLOR"))
{
for (i = 0; i < NUMCOLORFREESLOTS; i++)
if (!FREE_SKINCOLORS[i]) {
CONS_Printf("Skincolor SKINCOLOR_%s allocated.\n",word);
FREE_SKINCOLORS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL);
strcpy(FREE_SKINCOLORS[i],word);
M_AddMenuColor(numskincolors++);
break;
}
if (i == NUMCOLORFREESLOTS)
I_Error("Out of Skincolor Freeslots while allocating \"%s\"\nLoad less addons to fix this.", word);
}
else if (fastcmp(type, "SPR2"))
{
@ -287,7 +305,7 @@ void readfreeslots(MYFILE *f)
spr2defaults[free_spr2] = 0;
spr2names[free_spr2++][4] = 0;
} else
deh_warning("Ran out of free SPR2 slots!\n");
I_Error("Out of SPR2 Freeslots while allocating \"%s\"\nLoad less addons to fix this.", word);
}
else if (fastcmp(type, "TOL"))
{
@ -302,7 +320,7 @@ void readfreeslots(MYFILE *f)
// We don't, so freeslot it.
if (lastcustomtol == (UINT32)MAXTOL) // Unless you have way too many, since they're flags.
deh_warning("Ran out of free typeoflevel slots!\n");
I_Error("Out of Typeoflevel Freeslots while allocating \"%s\"\nLoad less addons to fix this.", word);
else
{
G_AddTOL(lastcustomtol, word);
@ -326,7 +344,7 @@ void readfreeslots(MYFILE *f)
precipprops[i].name = Z_StrDup(word);
precip_freeslot++;
} else
deh_warning("Ran out of free PRECIP slots!\n");
I_Error("Out of Precipitation Freeslots while allocating \"%s\"\nLoad less addons to fix this.", word);
}
else
deh_warning("Freeslots: unknown enum class '%s' for '%s_%s'", type, type, word);
@ -3112,7 +3130,7 @@ void readcupheader(MYFILE *f, cupheader_t *cup)
void readfollower(MYFILE *f)
{
char *s;
char *word, *word2, dname[SKINNAMESIZE+1];
char *word, *word2;
char *tmp;
char testname[SKINNAMESIZE+1];
@ -3121,10 +3139,9 @@ void readfollower(MYFILE *f)
INT32 res;
INT32 i;
if (numfollowers > MAXSKINS)
if (numfollowers >= MAXSKINS)
{
deh_warning("Error: Too many followers, cannot add anymore.\n");
return;
I_Error("Out of Followers\nLoad less addons to fix this.");
}
s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
@ -3143,8 +3160,9 @@ void readfollower(MYFILE *f)
followers[numfollowers].bobspeed = TICRATE*2;
followers[numfollowers].bobamp = 4*FRACUNIT;
followers[numfollowers].hitconfirmtime = TICRATE;
followers[numfollowers].defaultcolor = SKINCOLOR_GREEN;
strcpy(followers[numfollowers].icon, "M_NORANK");
followers[numfollowers].defaultcolor = FOLLOWERCOLOR_MATCH;
followers[numfollowers].category = UINT8_MAX;
strcpy(followers[numfollowers].icon, "MISSING");
do
{
@ -3183,6 +3201,23 @@ void readfollower(MYFILE *f)
strcpy(followers[numfollowers].icon, word2);
nameset = true;
}
else if (fastcmp(word, "CATEGORY"))
{
INT32 j;
for (j = 0; j < numfollowercategories; j++)
{
if (!stricmp(followercategories[j].name, word2))
{
followers[numfollowers].category = j;
break;
}
}
if (j == numfollowercategories)
{
deh_warning("Follower %d: unknown follower category '%s'", numfollowers, word2);
}
}
else if (fastcmp(word, "MODE"))
{
if (word2)
@ -3197,7 +3232,20 @@ void readfollower(MYFILE *f)
}
else if (fastcmp(word, "DEFAULTCOLOR"))
{
followers[numfollowers].defaultcolor = get_number(word2);
INT32 j;
for (j = 0; j < numskincolors +2; j++) // +2 because of Match and Opposite
{
if (!stricmp(Followercolor_cons_t[j].strvalue, word2))
{
followers[numfollowers].defaultcolor = Followercolor_cons_t[j].value;
break;
}
}
if (j == numskincolors+2)
{
deh_warning("Follower %d: unknown follower color '%s'", numfollowers, word2);
}
}
else if (fastcmp(word, "SCALE"))
{
@ -3300,10 +3348,6 @@ void readfollower(MYFILE *f)
// set skin name (this is just the follower's name in lowercases):
// but before we do, let's... actually check if another follower isn't doing the same shit...
strcpy(testname, followers[numfollowers].name);
// lower testname for skin checks...
strlwr(testname);
res = K_FollowerAvailable(testname);
if (res > -1) // yikes, someone else has stolen our name already
{
@ -3315,8 +3359,7 @@ void readfollower(MYFILE *f)
// in that case, we'll be very lazy and copy numfollowers to the end of our skin name.
}
strcpy(followers[numfollowers].skinname, testname);
strcpy(dname, followers[numfollowers].skinname); // display name, just used for printing succesful stuff or errors later down the line.
strcpy(testname, followers[numfollowers].name);
// now that the skin name is ready, post process the actual name to turn the underscores into spaces!
for (i = 0; followers[numfollowers].name[i]; i++)
@ -3331,14 +3374,14 @@ void readfollower(MYFILE *f)
if (followers[numfollowers].mode < FOLLOWERMODE_FLOAT || followers[numfollowers].mode >= FOLLOWERMODE__MAX)
{
followers[numfollowers].mode = FOLLOWERMODE_FLOAT;
deh_warning("Follower '%s': Value for 'mode' should be between %d and %d.", dname, FOLLOWERMODE_FLOAT, FOLLOWERMODE__MAX-1);
deh_warning("Follower '%s': Value for 'mode' should be between %d and %d.", testname, FOLLOWERMODE_FLOAT, FOLLOWERMODE__MAX-1);
}
#define FALLBACK(field, field2, threshold, set) \
if ((signed)followers[numfollowers].field < threshold) \
{ \
followers[numfollowers].field = set; \
deh_warning("Follower '%s': Value for '%s' is too low! Minimum should be %d. Value was overwritten to %d.", dname, field2, threshold, set); \
deh_warning("Follower '%s': Value for '%s' is too low! Minimum should be %d. Value was overwritten to %d.", testname, field2, threshold, set); \
} \
FALLBACK(dist, "DIST", 0, 0);
@ -3355,13 +3398,6 @@ if ((signed)followers[numfollowers].field < threshold) \
#undef FALLBACK
// Special case for color I suppose
if (followers[numfollowers].defaultcolor > (unsigned)(numskincolors-1))
{
followers[numfollowers].defaultcolor = SKINCOLOR_GREEN;
deh_warning("Follower \'%s\': Value for 'color' should be between 1 and %d.\n", dname, numskincolors-1);
}
// also check if we forgot states. If we did, we will set any missing state to the follower's idlestate.
// Print a warning in case we don't have a fallback and set the state to S_INVISIBLE (rather than S_NULL) if unavailable.
@ -3370,7 +3406,7 @@ if (!followers[numfollowers].field) \
{ \
followers[numfollowers].field = fallbackstate ? fallbackstate : S_INVISIBLE; \
if (!fallbackstate) \
deh_warning("Follower '%s' is missing state definition for '%s', no idlestate fallback was found", dname, field2); \
deh_warning("Follower '%s' is missing state definition for '%s', no idlestate fallback was found", testname, field2); \
} \
NOSTATE(idlestate, "IDLESTATE");
@ -3381,11 +3417,83 @@ if (!followers[numfollowers].field) \
NOSTATE(hitconfirmstate, "HITCONFIRMSTATE");
#undef NOSTATE
CONS_Printf("Added follower '%s'\n", dname);
CONS_Printf("Added follower '%s'\n", testname);
if (followers[numfollowers].category < numfollowercategories)
followercategories[followers[numfollowers].category].numincategory++;
numfollowers++; // add 1 follower
Z_Free(s);
}
void readfollowercategory(MYFILE *f)
{
char *s;
char *word, *word2;
char *tmp;
boolean nameset;
if (numfollowercategories == MAXFOLLOWERCATEGORIES)
{
I_Error("Out of Follower categories\nLoad less addons to fix this.");
}
s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
// Ready the default variables for followers. We will overwrite them as we go! We won't set the name or states RIGHT HERE as this is handled down instead.
strcpy(followercategories[numfollowercategories].icon, "MISSING");
followercategories[numfollowercategories].numincategory = 0;
do
{
if (myfgets(s, MAXLINELEN, f))
{
if (s[0] == '\n')
break;
tmp = strchr(s, '#');
if (tmp)
*tmp = '\0';
if (s == tmp)
continue; // Skip comment lines, but don't break.
word = strtok(s, " ");
if (word)
strupr(word);
else
break;
word2 = strtok(NULL, " = ");
if (!word2)
break;
if (word2[strlen(word2)-1] == '\n')
word2[strlen(word2)-1] = '\0';
if (fastcmp(word, "NAME"))
{
strcpy(followercategories[numfollowercategories].name, word2);
nameset = true;
}
else if (fastcmp(word, "ICON"))
{
strcpy(followercategories[numfollowercategories].icon, word2);
nameset = true;
}
}
} while (!myfeof(f)); // finish when the line is empty
if (!nameset)
{
// well this is problematic.
strcpy(followercategories[numfollowercategories].name, va("Followercategory%d", numfollowercategories)); // this is lazy, so what
}
CONS_Printf("Added follower category '%s'\n", followercategories[numfollowercategories].name);
numfollowercategories++; // add 1 follower
Z_Free(s);
}
void readweather(MYFILE *f, INT32 num)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);

View file

@ -82,6 +82,7 @@ void clear_conditionsets(void);
void readcupheader(MYFILE *f, cupheader_t *cup);
void readfollower(MYFILE *f);
void readfollowercategory(MYFILE *f);
preciptype_t get_precip(const char *word);
void readweather(MYFILE *f, INT32 num);

View file

@ -3271,6 +3271,7 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_ITEM_DEBRIS_CLOUD_SPAWNER2",
"S_ITEMICON",
"S_ITEMBACKDROP",
// Item capsules
"S_ITEMCAPSULE",
@ -3491,6 +3492,11 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_BANANA",
"S_BANANA_DEAD",
"S_BANANA_SPARK",
"S_BANANA_SPARK2",
"S_BANANA_SPARK3",
"S_BANANA_SPARK4",
//{ Orbinaut
"S_ORBINAUT1",
"S_ORBINAUT2",
@ -5315,6 +5321,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_BANANA", // Banana Stuff
"MT_BANANA_SHIELD",
"MT_BANANA_SPARK",
"MT_ORBINAUT", // Orbinaut stuff
"MT_ORBINAUT_SHIELD",
@ -5632,7 +5639,7 @@ const char *const MOBJFLAG_LIST[] = {
"SLIDEME",
"NOCLIP",
"FLOAT",
"BOXICON",
"SLOPE",
"MISSILE",
"SPRING",
"MONITOR",
@ -6761,14 +6768,13 @@ struct int_const_s const INT_CONST[] = {
// SRB2Kart
// kartitems_t
#define FOREACH( name, n ) { #name, KITEM_ ## name }
#define FOREACH( name, n ) { TOSTR (KITEM_ ## name), KITEM_ ## name }
KART_ITEM_ITERATOR, // Actual items (can be set for k_itemtype)
#undef FOREACH
{"NUMKARTITEMS",NUMKARTITEMS},
{"KRITEM_DUALSNEAKER",KRITEM_DUALSNEAKER}, // Additional roulette IDs (not usable for much in Lua besides K_GetItemPatch)
{"KRITEM_TRIPLESNEAKER",KRITEM_TRIPLESNEAKER},
{"KRITEM_TRIPLEBANANA",KRITEM_TRIPLEBANANA},
{"KRITEM_TENFOLDBANANA",KRITEM_TENFOLDBANANA},
{"KRITEM_TRIPLEORBINAUT",KRITEM_TRIPLEORBINAUT},
{"KRITEM_QUADORBINAUT",KRITEM_QUADORBINAUT},
{"KRITEM_DUALJAWZ",KRITEM_DUALJAWZ},

View file

@ -235,6 +235,12 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
readfollower(f);
continue;
}
else if (fastcmp(word, "FOLLOWERCATEGORY"))
{
// This is not a major mod.
readfollowercategory(f);
continue;
}
word2 = strtok(NULL, " ");
if (word2) {

View file

@ -398,6 +398,19 @@ typedef enum
SKINCOLOR_CHAOSEMERALD7,
SKINCOLOR_INVINCFLASH,
SKINCOLOR_POSNUM,
SKINCOLOR_POSNUM_WIN1,
SKINCOLOR_POSNUM_WIN2,
SKINCOLOR_POSNUM_WIN3,
SKINCOLOR_POSNUM_LOSE1,
SKINCOLOR_POSNUM_LOSE2,
SKINCOLOR_POSNUM_LOSE3,
SKINCOLOR_POSNUM_BEST1,
SKINCOLOR_POSNUM_BEST2,
SKINCOLOR_POSNUM_BEST3,
SKINCOLOR_POSNUM_BEST4,
SKINCOLOR_POSNUM_BEST5,
SKINCOLOR_POSNUM_BEST6,
SKINCOLOR_FIRSTFREESLOT,
SKINCOLOR_LASTFREESLOT = SKINCOLOR_FIRSTFREESLOT + NUMCOLORFREESLOTS - 1,

View file

@ -316,11 +316,11 @@ void G_ReadDemoExtraData(void)
demo_p += 16;
for (i = 0; i < numskincolors +2; i++) // +2 because of Match and Opposite
{
if (!stricmp(Followercolor_cons_t[i].strvalue, name))
{
players[p].followercolor = Followercolor_cons_t[i].value;
break;
}
if (!stricmp(Followercolor_cons_t[i].strvalue, name))
{
players[p].followercolor = Followercolor_cons_t[i].value;
break;
}
}
}
if (extradata & DXD_PLAYSTATE)
@ -410,7 +410,7 @@ void G_ReadDemoExtraData(void)
void G_WriteDemoExtraData(void)
{
INT32 i;
INT32 i, j;
char name[16];
for (i = 0; i < MAXPLAYERS; i++)
@ -456,13 +456,18 @@ void G_WriteDemoExtraData(void)
if (players[i].followerskin == -1)
strncpy(name, "None", 16);
else
strncpy(name, followers[players[i].followerskin].skinname, 16);
strncpy(name, followers[players[i].followerskin].name, 16);
M_Memcpy(demo_p, name, 16);
demo_p += 16;
// write follower color
memset(name, 0, 16);
strncpy(name, Followercolor_cons_t[(UINT16)(players[i].followercolor+2)].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match"
for (j = (numskincolors+2)-1; j > 0; j--)
{
if (Followercolor_cons_t[j].value == players[i].followercolor)
break;
}
strncpy(name, Followercolor_cons_t[j].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match"
M_Memcpy(demo_p,name,16);
demo_p += 16;
@ -1954,7 +1959,7 @@ void G_RecordMetal(void)
void G_BeginRecording(void)
{
UINT8 i, p;
UINT8 i, j, p;
char name[MAXCOLORNAME+1];
player_t *player = &players[consoleplayer];
@ -2009,7 +2014,7 @@ void G_BeginRecording(void)
if (wadfiles[i]->important)
{
nameonly(( filename = va("%s", wadfiles[i]->filename) ));
WRITESTRINGN(demo_p, filename, 64);
WRITESTRINGL(demo_p, filename, MAX_WADPATH);
WRITEMEM(demo_p, wadfiles[i]->md5sum, 16);
totalfiles++;
@ -2091,7 +2096,7 @@ void G_BeginRecording(void)
memset(name, 0, 16);
if (player->follower)
strncpy(name, followers[player->followerskin].skinname, 16);
strncpy(name, followers[player->followerskin].name, 16);
else
strncpy(name, "None", 16); // Say we don't have one, then.
@ -2100,7 +2105,12 @@ void G_BeginRecording(void)
// Save follower's colour
memset(name, 0, 16);
strncpy(name, Followercolor_cons_t[(UINT16)(player->followercolor+2)].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match"
for (j = (numskincolors+2)-1; j > 0; j--)
{
if (Followercolor_cons_t[j].value == players[i].followercolor)
break;
}
strncpy(name, Followercolor_cons_t[j].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match"
M_Memcpy(demo_p, name, 16);
demo_p += 16;
@ -2299,10 +2309,13 @@ static void G_LoadDemoExtraFiles(UINT8 **pp)
}
else
{
P_AddWadFile(filename);
P_PartialAddWadFile(filename);
}
}
}
if (P_PartialAddGetStage() >= 0)
P_MultiSetupWadFiles(true); // in case any partial adds were done
}
static void G_SkipDemoExtraFiles(UINT8 **pp)
@ -3076,11 +3089,11 @@ void G_DoPlayDemo(char *defdemoname)
demo_p += 16;
for (i = 0; i < numskincolors +2; i++) // +2 because of Match and Opposite
{
if (!stricmp(Followercolor_cons_t[i].strvalue, color))
{
players[p].followercolor = i;
break;
}
if (!stricmp(Followercolor_cons_t[i].strvalue, color))
{
players[p].followercolor = Followercolor_cons_t[i].value;
break;
}
}
// Score, since Kart uses this to determine where you start on the map
@ -3739,7 +3752,11 @@ static void G_StopTimingDemo(void)
if (restorecv_vidwait != cv_vidwait.value)
CV_SetValue(&cv_vidwait, restorecv_vidwait);
D_AdvanceDemo();
if (timedemo_quit)
COM_ImmedExecute("quit");
else
D_StartTitle();
}
// reset engine variable set for the demos
@ -3798,10 +3815,12 @@ boolean G_CheckDemoStatus(void)
{
G_StopDemo();
if (modeattacking)
if (timedemo_quit)
COM_ImmedExecute("quit");
else if (modeattacking)
M_EndModeAttackRun();
else
D_AdvanceDemo();
D_StartTitle();
}
return true;

View file

@ -4280,8 +4280,26 @@ void G_EndGame(void)
// Sets a tad of default info we need.
void G_LoadGameSettings(void)
{
INT32 i;
// initialize free sfx slots for skin sounds
S_InitRuntimeSounds();
// Prepare skincolor material.
for (i = 0; i < MAXSKINCOLORS; i++)
{
Color_cons_t[i].value = Followercolor_cons_t[i+2].value = i;
Color_cons_t[i].strvalue = Followercolor_cons_t[i+2].strvalue = skincolors[i].name;
}
Followercolor_cons_t[1].value = FOLLOWERCOLOR_MATCH;
Followercolor_cons_t[1].strvalue = "Match"; // Add "Match" option, which will make the follower color match the player's
Followercolor_cons_t[0].value = FOLLOWERCOLOR_OPPOSITE;
Followercolor_cons_t[0].strvalue = "Opposite"; // Add "Opposite" option, ...which is like "Match", but for coloropposite.
Color_cons_t[MAXSKINCOLORS].value = Followercolor_cons_t[MAXSKINCOLORS+2].value = 0;
Color_cons_t[MAXSKINCOLORS].strvalue = Followercolor_cons_t[MAXSKINCOLORS+2].strvalue = NULL;
}
#define GD_VERSIONCHECK 0xBA5ED444 // Change every major version, as usual

View file

@ -326,22 +326,28 @@ patch_t *HU_UpdateOrBlankPatch(patch_t **user, boolean required, const char *for
va_list ap;
char buffer[9];
lumpnum_t lump;
lumpnum_t lump = INT16_MAX;
patch_t *patch;
va_start (ap, format);
vsnprintf(buffer, sizeof buffer, format, ap);
va_end (ap);
if (user && p_adding_file != INT16_MAX)
if (user && partadd_earliestfile != UINT16_MAX)
{
lump = W_CheckNumForNamePwad(buffer, p_adding_file, 0);
UINT16 fileref = numwadfiles;
lump = INT16_MAX;
while ((lump == INT16_MAX) && ((--fileref) >= partadd_earliestfile))
{
lump = W_CheckNumForNamePwad(buffer, fileref, 0);
}
/* no update in this wad */
if (lump == INT16_MAX)
if (fileref < partadd_earliestfile)
return *user;
lump |= (p_adding_file << 16);
lump |= (fileref << 16);
}
else
{
@ -1251,7 +1257,7 @@ boolean HU_Responder(event_t *ev)
if (chatlen+pastelen > HU_MAXMSGLEN)
return true; // we can't paste this!!
memmove(&w_chat[c_input + pastelen], &w_chat[c_input], pastelen);
memmove(&w_chat[c_input + pastelen], &w_chat[c_input], (chatlen - c_input) + 1); // +1 for '\0'
memcpy(&w_chat[c_input], paste, pastelen); // copy all of that.
c_input += pastelen;
return true;

View file

@ -531,6 +531,7 @@ char sprnames[NUMSPRITES + 1][5] =
"SBOX", // Sphere Box (for Battle)
"RPOP", // Random Item Box Pop
"ITRI", // Item Box Debris
"ITPA", // Paper item backdrop
"SGNS", // Signpost sparkle
"FAST", // Speed boost trail
"DSHR", // Speed boost dust release
@ -559,6 +560,7 @@ char sprnames[NUMSPRITES + 1][5] =
"RSHE", // Rocket sneaker
"FITM", // Eggman Monitor
"BANA", // Banana Peel
"BAND", // Banana Peel death particles
"ORBN", // Orbinaut
"JAWZ", // Jawz
"SSMN", // SS Mine
@ -3882,6 +3884,7 @@ state_t states[NUMSTATES] =
{SPR_NULL, 0, 7, {A_SpawnItemDebrisCloud}, 20, 0, S_ITEM_DEBRIS_CLOUD_SPAWNER1}, // S_ITEM_DEBRIS_CLOUD_SPAWNER2
{SPR_NULL, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_ITEMICON
{SPR_ITPA, FF_FULLBRIGHT, -1, {NULL}, 1, 0, S_NULL}, // S_ITEMBACKDROP
{SPR_ICAP, FF_ADD|0, -1, {NULL}, 0, 0, S_NULL}, // S_ITEMCAPSULE
{SPR_ICAP, FF_PAPERSPRITE|1, -1, {NULL}, 0, 0, S_NULL}, // S_ITEMCAPSULE_TOP_SIDE
@ -4082,7 +4085,12 @@ state_t states[NUMSTATES] =
{SPR_FITM, 24|FF_FULLBRIGHT, 175, {NULL}, 0, 0, S_NULL}, // S_EGGMANITEM_DEAD
{SPR_BANA, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BANANA
{SPR_BANA, 0, 175, {NULL}, 0, 0, S_NULL}, // S_BANANA_DEAD
{SPR_BANA, 1, 175, {NULL}, 0, 0, S_NULL}, // S_BANANA_DEAD
{SPR_BAND, 0, -1, {NULL}, 0, 0, S_BANANA_SPARK2}, // S_BANANA_SPARK
{SPR_BAND, 1|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_BANANA_SPARK3}, // S_BANANA_SPARK2
{SPR_BAND, 2|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_BANANA_SPARK4}, // S_BANANA_SPARK3
{SPR_BAND, 3|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_BANANA_SPARK4
{SPR_ORBN, 0, 1, {NULL}, 0, 0, S_ORBINAUT2}, // S_ORBINAUT1
{SPR_ORBN, 1, 1, {NULL}, 0, 0, S_ORBINAUT3}, // S_ORBINAUT2
@ -5294,7 +5302,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
1000, // mass
MT_THOK, // damage
sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SOLID|MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
(statenum_t)MT_THOK // raisestate
},
@ -5321,7 +5329,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
1000, // mass
0, // damage
sfx_None, // activesound
MF_SOLID|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SOLID|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -9991,7 +9999,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10018,7 +10026,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10045,7 +10053,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10072,7 +10080,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10099,7 +10107,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10126,7 +10134,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10153,7 +10161,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10180,7 +10188,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10207,7 +10215,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10234,7 +10242,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10261,7 +10269,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10288,7 +10296,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10315,7 +10323,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10342,7 +10350,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10369,7 +10377,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10396,7 +10404,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10423,7 +10431,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10450,7 +10458,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -10477,7 +10485,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
62*FRACUNIT, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -23122,7 +23130,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_peel, // activesound
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -23149,7 +23157,34 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_None, // activesound
MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
{ // MT_BANANA_SPARK
-1, // doomednum
S_BANANA_SPARK, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_BANANA_SPARK2,// deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
4*FRACUNIT, // radius
8*FRACUNIT, // height
0, // display offset
100, // mass
1, // damage
sfx_None, // activesound
MF_DONTENCOREMAP|MF_NOCLIPTHING|MF_NOSQUISH, // flags
S_NULL // raisestate
},
@ -23176,7 +23211,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_s3k96, // activesound
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -23203,7 +23238,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_None, // activesound
MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -23230,7 +23265,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_s3kc0s, // activesound
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -23257,7 +23292,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_None, // activesound
MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -23311,7 +23346,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_s3k5c, // activesound
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -23338,7 +23373,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_None, // activesound
MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -23446,7 +23481,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // mass
0, // damage
sfx_s3k5c, // activesound
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -23473,7 +23508,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_s3k96, // activesound
MF_SPECIAL|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SPECIAL|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -23500,7 +23535,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_None, // activesound
MF_SPECIAL|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SPECIAL|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -24121,7 +24156,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_None, // activesound
MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
S_NULL // raisestate
},
@ -29158,7 +29193,21 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
{"Chaos Emerald 6", { 0, 208, 50, 32, 34, 37, 40, 44, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_CHAOSEMERALD6
{"Chaos Emerald 7", { 0, 120, 121, 140, 133, 135, 149, 156, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_CHAOSEMERALD7
{"Invinc Flash", { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, SKINCOLOR_NONE, 0, 0, false} // SKINCOLOR_INVINCFLASH
{"Invinc Flash", { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_INVINCFLASH
{"Position", { 8, 9, 11, 12, 14, 15, 17, 18, 20, 21, 23, 24, 26, 27, 29, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM
{"Position Win 1", {152, 152, 153, 153, 154, 154, 155, 155, 156, 156, 157, 158, 159, 253, 254, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_WIN1
{"Position Win 2", {134, 134, 135, 135, 135, 136, 136, 136, 137, 137, 138, 138, 139, 139, 254, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_WIN2
{"Position Win 3", {255, 255, 122, 122, 123, 123, 141, 141, 142, 142, 143, 143, 138, 139, 254, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_WIN3
{"Position Lose 1", { 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 71, 46, 47, 29, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_LOSE1
{"Position Lose 2", { 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 63, 44, 45, 46, 47, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_LOSE2
{"Position Lose 3", { 73, 74, 75, 76, 76, 77, 77, 78, 78, 79, 79, 236, 237, 238, 239, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_LOSE3
{"Position Best 1", { 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 71, 46, 47, 29, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_BEST1
{"Position Best 2", { 73, 74, 75, 76, 76, 77, 77, 78, 78, 79, 79, 236, 237, 238, 239, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_BEST2
{"Position Best 3", {112, 112, 113, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 110, 111, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_BEST3
{"Position Best 4", {255, 255, 122, 122, 123, 123, 141, 141, 142, 142, 143, 143, 138, 139, 254, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_BEST4
{"Position Best 5", {152, 152, 153, 153, 154, 154, 155, 155, 156, 156, 157, 158, 159, 253, 254, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_BEST5
{"Position Best 6", {181, 181, 182, 182, 183, 183, 184, 184, 185, 185, 186, 186, 187, 187, 29, 30}, SKINCOLOR_NONE, 0, 0, false} // SKINCOLOR_POSNUM_BEST6
};
/** Patches the mobjinfo, state, and skincolor tables.

View file

@ -1077,6 +1077,7 @@ typedef enum sprite
SPR_SBOX, // Sphere Box (for Battle)
SPR_RPOP, // Random Item Box Pop
SPR_ITRI, // Item Box Debris
SPR_ITPA, // Paper item backdrop
SPR_SGNS, // Signpost sparkle
SPR_FAST, // Speed boost trail
SPR_DSHR, // Speed boost dust release
@ -1105,6 +1106,7 @@ typedef enum sprite
SPR_RSHE, // Rocket sneaker
SPR_FITM, // Eggman Monitor
SPR_BANA, // Banana Peel
SPR_BAND, // Banana Peel death particles
SPR_ORBN, // Orbinaut
SPR_JAWZ, // Jawz
SPR_SSMN, // SS Mine
@ -4285,6 +4287,7 @@ typedef enum state
S_ITEM_DEBRIS_CLOUD_SPAWNER2,
S_ITEMICON,
S_ITEMBACKDROP,
// Item capsules
S_ITEMCAPSULE,
@ -4505,6 +4508,11 @@ typedef enum state
S_BANANA,
S_BANANA_DEAD,
S_BANANA_SPARK,
S_BANANA_SPARK2,
S_BANANA_SPARK3,
S_BANANA_SPARK4,
//{ Orbinaut
S_ORBINAUT1,
S_ORBINAUT2,
@ -6365,6 +6373,7 @@ typedef enum mobj_type
MT_BANANA, // Banana Stuff
MT_BANANA_SHIELD,
MT_BANANA_SPARK,
MT_ORBINAUT, // Orbinaut stuff
MT_ORBINAUT_SHIELD,

View file

@ -94,7 +94,7 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2)
S_StartSound(t2, t2->info->deathsound);
P_KillMobj(t2, t1, t1, DMG_NORMAL);
P_SetObjectMomZ(t2, 8*FRACUNIT, false);
P_SetObjectMomZ(t2, 24*FRACUNIT, false);
P_InstaThrust(t2, bounceangle, 16*FRACUNIT);
P_SpawnMobj(t2->x/2 + t1->x/2, t2->y/2 + t1->y/2, t2->z/2 + t1->z/2, MT_ITEMCLASH);
@ -122,7 +122,7 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2)
S_StartSound(t1, t1->info->deathsound);
P_KillMobj(t1, t2, t2, DMG_NORMAL);
P_SetObjectMomZ(t1, 8*FRACUNIT, false);
P_SetObjectMomZ(t1, 24*FRACUNIT, false);
P_InstaThrust(t1, bounceangle, 16*FRACUNIT);
}
@ -351,7 +351,7 @@ boolean K_MineCollide(mobj_t *t1, mobj_t *t2)
S_StartSound(t2, t2->info->deathsound);
P_KillMobj(t2, t1, t1, DMG_NORMAL);
P_SetObjectMomZ(t2, 8*FRACUNIT, false);
P_SetObjectMomZ(t2, 24*FRACUNIT, false);
P_InstaThrust(t2, bounceangle, 16*FRACUNIT);
}
else if (t2->flags & MF_SHOOTABLE)
@ -414,7 +414,7 @@ boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2)
S_StartSound(t2, t2->info->deathsound);
P_KillMobj(t2, t1, t1, DMG_NORMAL);
P_SetObjectMomZ(t2, 8*FRACUNIT, false);
P_SetObjectMomZ(t2, 24*FRACUNIT, false);
P_InstaThrust(t2, bounceangle, 16*FRACUNIT);
P_SpawnMobj(t2->x/2 + t1->x/2, t2->y/2 + t1->y/2, t2->z/2 + t1->z/2, MT_ITEMCLASH);

View file

@ -15,6 +15,9 @@
INT32 numfollowers = 0;
follower_t followers[MAXSKINS];
INT32 numfollowercategories;
followercategory_t followercategories[MAXFOLLOWERCATEGORIES];
CV_PossibleValue_t Followercolor_cons_t[MAXSKINCOLORS+3]; // +3 to account for "Match", "Opposite" & NULL
/*--------------------------------------------------
@ -28,7 +31,7 @@ INT32 K_FollowerAvailable(const char *name)
for (i = 0; i < numfollowers; i++)
{
if (stricmp(followers[i].skinname, name) == 0)
if (stricmp(followers[i].name, name) == 0)
return i;
}
@ -54,7 +57,7 @@ boolean K_SetFollowerByName(INT32 playernum, const char *skinname)
for (i = 0; i < numfollowers; i++)
{
// search in the skin list
if (stricmp(followers[i].skinname, skinname) == 0)
if (stricmp(followers[i].name, skinname) == 0)
{
K_SetFollowerByNum(playernum, i);
return true;
@ -74,6 +77,31 @@ boolean K_SetFollowerByName(INT32 playernum, const char *skinname)
return false;
}
/*--------------------------------------------------
void K_RemoveFollower(player_t *player)
See header file for description.
--------------------------------------------------*/
void K_RemoveFollower(player_t *player)
{
mobj_t *bub, *tmp;
if (player->follower && !P_MobjWasRemoved(player->follower)) // this is also called when we change colour so don't respawn the follower unless we changed skins
{
// Remove follower's possible hnext list (bubble)
bub = player->follower->hnext;
while (bub && !P_MobjWasRemoved(bub))
{
tmp = bub->hnext;
P_RemoveMobj(bub);
bub = tmp;
}
P_RemoveMobj(player->follower);
P_SetTarget(&player->follower, NULL);
}
}
/*--------------------------------------------------
void K_SetFollowerByNum(INT32 playernum, INT32 skinnum)
@ -82,8 +110,6 @@ boolean K_SetFollowerByName(INT32 playernum, const char *skinname)
void K_SetFollowerByNum(INT32 playernum, INT32 skinnum)
{
player_t *player = &players[playernum];
mobj_t *bub;
mobj_t *tmp;
player->followerready = true; // we are ready to perform follower related actions in the player thinker, now.
@ -94,21 +120,8 @@ void K_SetFollowerByNum(INT32 playernum, INT32 skinnum)
However, we will despawn it right here if there's any to make it easy for the player thinker to replace it or delete it.
*/
if (player->follower && skinnum != player->followerskin) // this is also called when we change colour so don't respawn the follower unless we changed skins
{
// Remove follower's possible hnext list (bubble)
bub = player->follower->hnext;
while (bub && !P_MobjWasRemoved(bub))
{
tmp = bub->hnext;
P_RemoveMobj(bub);
bub = tmp;
}
P_RemoveMobj(player->follower);
P_SetTarget(&player->follower, NULL);
}
if (skinnum != player->followerskin)
K_RemoveFollower(player);
player->followerskin = skinnum;
@ -253,18 +266,16 @@ void K_HandleFollower(player_t *player)
{
//CONS_Printf("Follower skin invlaid. Setting to -1.\n");
player->followerskin = -1;
return;
}
// don't do anything if we can't have a follower to begin with.
// (It gets removed under those conditions)
if (player->spectator)
{
return;
}
if (player->followerskin < 0)
if (player->spectator || player->followerskin < 0)
{
if (player->follower)
{
K_RemoveFollower(player);
}
return;
}

View file

@ -45,10 +45,11 @@ typedef enum
//
typedef struct follower_s
{
char skinname[SKINNAMESIZE+1]; // Skin Name. This is what to refer to when asking the commands anything.
char name[SKINNAMESIZE+1]; // Name. This is used for the menus. We'll just follow the same rules as skins for this.
char name[SKINNAMESIZE+1]; // Skin Name. This is what to refer to when asking the commands anything..
char icon[8+1]; // Lump names are only 8 characters. (+1 for \0)
UINT8 category; // Category
skincolornum_t defaultcolor; // default color for menus.
followermode_t mode; // Follower behavior modifier.
@ -85,6 +86,18 @@ typedef struct follower_s
extern INT32 numfollowers;
extern follower_t followers[MAXSKINS];
#define MAXFOLLOWERCATEGORIES 32
typedef struct followercategory_s
{
char name[SKINNAMESIZE+1]; // Name. This is used for the menus. We'll just follow the same rules as skins for this.
char icon[8+1]; // Lump names are only 8 characters. (+1 for \0)
UINT8 numincategory;
} followercategory_t;
extern INT32 numfollowercategories;
extern followercategory_t followercategories[MAXFOLLOWERCATEGORIES];
/*--------------------------------------------------
INT32 K_FollowerAvailable(const char *name)
@ -168,5 +181,19 @@ UINT16 K_GetEffectiveFollowerColor(UINT16 followercolor, UINT16 playercolor);
void K_HandleFollower(player_t *player);
/*--------------------------------------------------
void K_RemoveFollower(player_t *player)
Removes Follower object
Input Arguments:-
player - The player who we want to remove the follower of.
Return:-
None
--------------------------------------------------*/
void K_RemoveFollower(player_t *player);
#endif // __K_FOLLOWER__

View file

@ -676,7 +676,7 @@ void K_PlayerLoseLife(player_t *player)
return;
}
if (player->spectator || player->exiting || player->bot || (player->pflags & PF_LOSTLIFE))
if (player->spectator || player->exiting || player->bot || player->lives <= 0 || (player->pflags & PF_LOSTLIFE))
{
return;
}

View file

@ -37,10 +37,6 @@
#include "r_fps.h"
#include "m_random.h"
#define NUMPOSNUMS 10
#define NUMPOSFRAMES 7 // White, three blues, three reds
#define NUMWINFRAMES 6 // Red, yellow, green, cyan, blue, purple
//{ Patch Definitions
static patch_t *kp_nodraw;
@ -70,8 +66,7 @@ static patch_t *kp_startcountdown[20];
static patch_t *kp_racefault[6];
static patch_t *kp_racefinish[6];
static patch_t *kp_positionnum[NUMPOSNUMS][NUMPOSFRAMES];
static patch_t *kp_winnernum[NUMPOSFRAMES];
static patch_t *kp_positionnum[10][2][2]; // number, overlay or underlay, splitscreen
static patch_t *kp_facenum[MAXPLAYERS+1];
static patch_t *kp_facehighlight[8];
@ -179,7 +174,7 @@ static patch_t *kp_trickcool[2];
void K_LoadKartHUDGraphics(void)
{
INT32 i, j;
INT32 i, j, k;
char buffer[9];
// Null Stuff
@ -280,23 +275,29 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_racefinish[5], "K_2PFINB");
// Position numbers
sprintf(buffer, "K_POSNxx");
for (i = 0; i < NUMPOSNUMS; i++)
sprintf(buffer, "KRNKxyz");
for (i = 0; i < 10; i++)
{
buffer[6] = '0'+i;
for (j = 0; j < NUMPOSFRAMES; j++)
{
//sprintf(buffer, "K_POSN%d%d", i, j);
buffer[7] = '0'+j;
HU_UpdatePatch(&kp_positionnum[i][j], "%s", buffer);
}
}
sprintf(buffer, "K_POSNWx");
for (i = 0; i < NUMWINFRAMES; i++)
{
buffer[7] = '0'+i;
HU_UpdatePatch(&kp_winnernum[i], "%s", buffer);
for (j = 0; j < 2; j++)
{
buffer[5] = 'A'+j;
for (k = 0; k < 2; k++)
{
if (k > 0)
{
buffer[4] = 'S';
}
else
{
buffer[4] = 'B';
}
HU_UpdatePatch(&kp_positionnum[i][j][k], "%s", buffer);
}
}
}
sprintf(buffer, "OPPRNKxx");
@ -659,7 +660,6 @@ const char *K_GetItemPatch(UINT8 item, boolean tiny)
return (tiny ? "K_ISINV1" : "K_ITINV1");
case KITEM_BANANA:
case KRITEM_TRIPLEBANANA:
case KRITEM_TENFOLDBANANA:
return (tiny ? "K_ISBANA" : "K_ITBANA");
case KITEM_EGGMAN:
return (tiny ? "K_ISEGGM" : "K_ITEGGM");
@ -950,6 +950,11 @@ void K_ObjectTracking(trackingResult_t *result, vector3_t *point, boolean revers
h = R_PointToDist2(point->x, point->y, viewx, viewy);
da = AngleDeltaSigned(viewpointAngle, R_PointToAngle2(point->x, point->y, viewx, viewy));
if (reverse)
{
da = -(da);
}
// Set results relative to top left!
result->x = FixedMul(NEWTAN(da), fg);
result->y = FixedMul((NEWTAN(viewpointAiming) - FixedDiv((viewz - point->z), 1 + FixedMul(NEWCOS(da), h))), fg);
@ -1545,146 +1550,187 @@ bademblem:
}
}
static void K_DrawKartPositionNum(INT32 num)
static fixed_t K_DrawKartPositionNumPatch(UINT8 num, UINT8 *color, fixed_t x, fixed_t y, fixed_t scale, INT32 flags)
{
// POSI_X = BASEVIDWIDTH - 51; // 269
// POSI_Y = BASEVIDHEIGHT- 64; // 136
UINT8 splitIndex = (r_splitscreen > 0) ? 1 : 0;
fixed_t w = FRACUNIT;
fixed_t h = FRACUNIT;
INT32 overlayFlags[2];
INT32 i;
boolean win = (stplyr->exiting && num == 1);
//INT32 X = POSI_X;
INT32 W = SHORT(kp_positionnum[0][0]->width);
fixed_t scale = FRACUNIT;
patch_t *localpatch = kp_positionnum[0][0];
INT32 fx = 0, fy = 0, fflags = 0;
INT32 addOrSub = V_ADD;
boolean flipdraw = false; // flip the order we draw it in for MORE splitscreen bs. fun.
boolean flipvdraw = false; // used only for 2p splitscreen so overtaking doesn't make 1P's position fly off the screen.
boolean overtake = false;
if (num >= 10)
{
return x; // invalid input
}
if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SUBTRACTNUM) == LF_SUBTRACTNUM)
{
addOrSub = V_SUBTRACT;
overlayFlags[0] = V_SUBTRACT;
overlayFlags[1] = V_ADD;
}
else
{
overlayFlags[0] = V_ADD;
overlayFlags[1] = V_SUBTRACT;
}
w = kp_positionnum[num][0][splitIndex]->width * scale;
h = kp_positionnum[num][0][splitIndex]->height * scale;
if (flags & V_SNAPTORIGHT)
{
x -= w;
}
if (flags & V_SNAPTOBOTTOM)
{
y -= h;
}
for (i = 1; i >= 0; i--)
{
V_DrawFixedPatch(
x, y, scale,
flags | overlayFlags[i],
kp_positionnum[num][i][splitIndex],
color
);
}
return (x - w);
}
static void K_DrawKartPositionNum(INT32 num)
{
const tic_t counter = (leveltime / 3); // Alternate colors every three frames
fixed_t scale = FRACUNIT;
fixed_t fx = 0, fy = 0;
transnum_t trans = 0;
INT32 fflags = 0;
UINT8 *color = NULL;
if (leveltime < (starttime + NUMTRANSMAPS))
{
trans = (starttime + NUMTRANSMAPS) - leveltime;
}
if (trans >= NUMTRANSMAPS)
{
return;
}
if (stplyr->positiondelay || stplyr->exiting)
{
scale *= 2;
overtake = true; // this is used for splitscreen stuff in conjunction with flipdraw.
const UINT8 delay = (stplyr->exiting) ? POS_DELAY_TIME : stplyr->positiondelay;
const fixed_t add = (scale * 3) >> ((r_splitscreen == 1) ? 1 : 2);
scale += min((add * (delay * delay)) / (POS_DELAY_TIME * POS_DELAY_TIME), add);
}
if (r_splitscreen)
{
scale /= 2;
}
W = FixedMul(W<<FRACBITS, scale)>>FRACBITS;
// pain and suffering defined below
if (!r_splitscreen)
{
fx = POSI_X;
fy = BASEVIDHEIGHT - 8;
fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN;
fx = BASEVIDWIDTH << FRACBITS;
fy = BASEVIDHEIGHT << FRACBITS;
fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT;
}
else if (r_splitscreen == 1) // for this splitscreen, we'll use case by case because it's a bit different.
{
fx = POSI_X;
if (stplyr == &players[displayplayers[0]]) // for player 1: display this at the top right, above the minimap.
fx = BASEVIDWIDTH << FRACBITS;
if (stplyr == &players[displayplayers[0]])
{
fy = 30;
fflags = V_SNAPTOTOP|V_SNAPTORIGHT|V_SPLITSCREEN;
if (overtake)
flipvdraw = true; // make sure overtaking doesn't explode us
// for player 1: display this at the top right, above the minimap.
fy = 0;
fflags = V_SNAPTOTOP|V_SNAPTORIGHT;
}
else // if we're not p1, that means we're p2. display this at the bottom right, below the minimap.
else
{
fy = (BASEVIDHEIGHT/2) - 8;
fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN;
// if we're not p1, that means we're p2. display this at the bottom right, below the minimap.
fy = BASEVIDHEIGHT << FRACBITS;
fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT;
}
fy >>= 1;
}
else
{
fy = BASEVIDHEIGHT << FRACBITS;
if (stplyr == &players[displayplayers[0]]
|| stplyr == &players[displayplayers[2]])
{
// If we are P1 or P3...
fx = 0;
fflags = V_SNAPTOLEFT|V_SNAPTOBOTTOM;
}
else
{
// else, that means we're P2 or P4.
fx = BASEVIDWIDTH << FRACBITS;
fflags = V_SNAPTORIGHT|V_SNAPTOBOTTOM;
}
fx >>= 1;
fy >>= 1;
}
if (trans > 0)
{
fflags |= (trans << V_ALPHASHIFT);
}
if (stplyr->exiting && num == 1)
{
// 1st place winner? You get rainbows!!
color = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_POSNUM_BEST1 + (counter % 6), GTC_CACHE);
}
else if (stplyr->laps >= numlaps || stplyr->exiting)
{
// On the final lap, or already won.
boolean useRedNums = K_IsPlayerLosing(stplyr);
if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SUBTRACTNUM) == LF_SUBTRACTNUM)
{
// Subtracting RED will look BLUE, and vice versa.
useRedNums = !useRedNums;
}
if (useRedNums == true)
{
color = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_POSNUM_LOSE1 + (counter % 3), GTC_CACHE);
}
else
{
color = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_POSNUM_WIN1 + (counter % 3), GTC_CACHE);
}
}
else
{
if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3...
{
fx = POSI_X;
fy = POSI_Y;
fflags = V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
flipdraw = true;
if (num && num >= 10)
fx += W; // this seems dumb, but we need to do this in order for positions above 10 going off screen.
}
else // else, that means we're P2 or P4.
{
fx = POSI2_X;
fy = POSI2_Y;
fflags = V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
}
color = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_POSNUM, GTC_CACHE);
}
// Special case for 0
if (num <= 0)
{
V_DrawFixedPatch(fx<<FRACBITS, fy<<FRACBITS, scale, addOrSub|V_SLIDEIN|fflags, kp_positionnum[0][0], NULL);
K_DrawKartPositionNumPatch(
0, color,
fx, fy, scale, V_SLIDEIN|V_SPLITSCREEN|fflags
);
return;
}
// Draw the number
while (num)
{
if (win) // 1st place winner? You get rainbows!!
{
localpatch = kp_winnernum[(leveltime % (NUMWINFRAMES*3)) / 3];
}
else if (stplyr->laps >= numlaps || stplyr->exiting) // Check for the final lap, or won
{
boolean useRedNums = K_IsPlayerLosing(stplyr);
/*
*/
if (addOrSub == V_SUBTRACT)
{
// Subtracting RED will look BLUE, and vice versa.
useRedNums = !useRedNums;
}
// Alternate frame every three frames
switch ((leveltime % 9) / 3)
{
case 0:
if (useRedNums == true)
localpatch = kp_positionnum[num % 10][4];
else
localpatch = kp_positionnum[num % 10][1];
break;
case 1:
if (useRedNums == true)
localpatch = kp_positionnum[num % 10][5];
else
localpatch = kp_positionnum[num % 10][2];
break;
case 2:
if (useRedNums == true)
localpatch = kp_positionnum[num % 10][6];
else
localpatch = kp_positionnum[num % 10][3];
break;
default:
localpatch = kp_positionnum[num % 10][0];
break;
}
}
else
{
localpatch = kp_positionnum[num % 10][0];
}
V_DrawFixedPatch(
(fx<<FRACBITS) + ((overtake && flipdraw) ? (SHORT(localpatch->width)*scale/2) : 0),
(fy<<FRACBITS) + ((overtake && flipvdraw) ? (SHORT(localpatch->height)*scale/2) : 0),
scale, addOrSub|V_SLIDEIN|fflags, localpatch, NULL
fx = K_DrawKartPositionNumPatch(
(num % 10), color,
fx, fy, scale, V_SLIDEIN|V_SPLITSCREEN|fflags
);
// ^ if we overtake as p1 or p3 in splitscren, we shift it so that it doesn't go off screen.
// ^ if we overtake as p1 in 2p splits, shift vertically so that this doesn't happen either.
fx -= W;
num /= 10;
}
}
@ -4514,7 +4560,6 @@ static void K_drawDistributionDebugger(void)
kp_sneaker[1],
kp_sneaker[1],
kp_banana[1],
kp_banana[1],
kp_orbinaut[4],
kp_orbinaut[4],
kp_jawz[1]

View file

@ -19,6 +19,8 @@
#define RINGANIM_NUMFRAMES 10
#define RINGANIM_DELAYMAX 5
#define POS_DELAY_TIME 10
void K_AdjustXYWithSnap(INT32 *x, INT32 *y, UINT32 options, INT32 dupx, INT32 dupy);
typedef struct trackingResult_s

View file

@ -305,7 +305,6 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_dualsneaker);
CV_RegisterVar(&cv_triplesneaker);
CV_RegisterVar(&cv_triplebanana);
CV_RegisterVar(&cv_decabanana);
CV_RegisterVar(&cv_tripleorbinaut);
CV_RegisterVar(&cv_quadorbinaut);
CV_RegisterVar(&cv_dualjawz);
@ -409,7 +408,6 @@ consvar_t *KartItemCVars[NUMKARTRESULTS-1] =
&cv_dualsneaker,
&cv_triplesneaker,
&cv_triplebanana,
&cv_decabanana,
&cv_tripleorbinaut,
&cv_quadorbinaut,
&cv_dualjawz
@ -428,7 +426,7 @@ static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] =
{ 1, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor
{ 5, 5, 2, 2, 0, 0, 0, 0 }, // Orbinaut
{ 0, 4, 2, 1, 0, 0, 0, 0 }, // Jawz
{ 0, 3, 3, 1, 0, 0, 0, 0 }, // Mine
{ 0, 3, 3, 2, 0, 0, 0, 0 }, // Mine
{ 3, 0, 0, 0, 0, 0, 0, 0 }, // Land Mine
{ 0, 0, 2, 2, 0, 0, 0, 0 }, // Ballhog
{ 0, 0, 0, 0, 0, 2, 4, 0 }, // Self-Propelled Bomb
@ -446,7 +444,6 @@ static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] =
{ 0, 0, 2, 2, 2, 0, 0, 0 }, // Sneaker x2
{ 0, 0, 0, 0, 4, 4, 4, 0 }, // Sneaker x3
{ 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3
{ 0, 0, 0, 1, 0, 0, 0, 0 }, // Banana x10
{ 0, 0, 1, 0, 0, 0, 0, 0 }, // Orbinaut x3
{ 0, 0, 0, 2, 0, 0, 0, 0 }, // Orbinaut x4
{ 0, 0, 1, 2, 1, 0, 0, 0 } // Jawz x2
@ -480,7 +477,6 @@ static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS][2] =
{ 0, 0 }, // Sneaker x2
{ 0, 1 }, // Sneaker x3
{ 0, 0 }, // Banana x3
{ 1, 1 }, // Banana x10
{ 2, 0 }, // Orbinaut x3
{ 1, 1 }, // Orbinaut x4
{ 5, 1 } // Jawz x2
@ -516,7 +512,6 @@ static UINT8 K_KartItemOddsSpecial[NUMKARTRESULTS-1][4] =
{ 0, 1, 1, 0 }, // Sneaker x2
{ 0, 0, 1, 1 }, // Sneaker x3
{ 0, 0, 0, 0 }, // Banana x3
{ 0, 0, 0, 0 }, // Banana x10
{ 0, 1, 1, 0 }, // Orbinaut x3
{ 0, 0, 1, 1 }, // Orbinaut x4
{ 0, 0, 1, 1 } // Jawz x2
@ -577,7 +572,6 @@ SINT8 K_ItemResultToType(SINT8 getitem)
return KITEM_SNEAKER;
case KRITEM_TRIPLEBANANA:
case KRITEM_TENFOLDBANANA:
return KITEM_BANANA;
case KRITEM_TRIPLEORBINAUT:
@ -615,9 +609,6 @@ UINT8 K_ItemResultToAmount(SINT8 getitem)
case KITEM_BALLHOG: // Not a special result, but has a special amount
return 5;
case KRITEM_TENFOLDBANANA:
return 10;
default:
return 1;
}
@ -891,7 +882,6 @@ INT32 K_KartGetItemOdds(
break;
case KRITEM_TRIPLEBANANA:
case KRITEM_TENFOLDBANANA:
powerItem = true;
notNearEnd = true;
break;
@ -3471,7 +3461,7 @@ boolean K_TripwirePass(player_t *player)
boolean K_MovingHorizontally(mobj_t *mobj)
{
return (P_AproxDistance(mobj->momx, mobj->momy) / 5 > abs(mobj->momz));
return (P_AproxDistance(mobj->momx, mobj->momy) / 4 > abs(mobj->momz));
}
boolean K_WaterRun(mobj_t *mobj)
@ -6174,6 +6164,7 @@ mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing,
{
// Shoot forward
mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, mapthing);
mo->angle = player->mo->angle;
// These are really weird so let's make it a very specific case to make SURE it works...
if (player->mo->eflags & MFE_VERTICALFLIP)
@ -6188,7 +6179,6 @@ mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing,
S_StartSound(player->mo, mo->info->seesound);
if (mo)
{
angle_t fa = player->mo->angle>>ANGLETOFINESHIFT;
fixed_t HEIGHT = ((20 + (dir*10)) * FRACUNIT) + (FixedDiv(player->mo->momz, mapobjectscale)*P_MobjFlip(player->mo)); // Also intentionally not player scale
@ -6196,14 +6186,20 @@ mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing,
P_SetObjectMomZ(mo, HEIGHT, false);
mo->momx = player->mo->momx + FixedMul(FINECOSINE(fa), PROJSPEED*dir);
mo->momy = player->mo->momy + FixedMul(FINESINE(fa), PROJSPEED*dir);
}
mo->extravalue2 = dir;
mo->extravalue2 = dir;
if (mo->eflags & MFE_UNDERWATER)
mo->momz = (117 * mo->momz) / 200;
if (mo->eflags & MFE_UNDERWATER)
mo->momz = (117 * mo->momz) / 200;
P_SetScale(mo, finalscale);
mo->destscale = finalscale;
P_SetScale(mo, finalscale);
mo->destscale = finalscale;
if (mapthing == MT_BANANA)
{
mo->angle = FixedAngle(P_RandomRange(PR_DECORATION, -180, 180) << FRACBITS);
mo->rollangle = FixedAngle(P_RandomRange(PR_DECORATION, -180, 180) << FRACBITS);
}
// this is the small graphic effect that plops in you when you throw an item:
@ -6288,6 +6284,8 @@ mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing,
if (player->mo->eflags & MFE_VERTICALFLIP)
mo->eflags |= MFE_VERTICALFLIP;
mo->angle = newangle;
if (mapthing == MT_SSMINE)
mo->extravalue1 = 49; // Pads the start-up length from 21 frames to a full 2 seconds
else if (mapthing == MT_BUBBLESHIELDTRAP)
@ -6609,6 +6607,7 @@ void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound)
mo->player->tricktime = 0; // Reset post-hitlag timer
// Setup the boost for potential upwards trick, at worse, make it your regular max speed. (boost = curr speed*1.25)
mo->player->trickboostpower = max(FixedDiv(mo->player->speed, K_GetKartSpeed(mo->player, false, false)) - FRACUNIT, 0)*125/100;
mo->player->trickboostpower = FixedDiv(mo->player->trickboostpower, K_GrowShrinkSpeedMul(mo->player));
//CONS_Printf("Got boost: %d%\n", mo->player->trickboostpower*100 / FRACUNIT);
}
@ -6703,7 +6702,7 @@ killnext:
S_StartSound(banana, banana->info->deathsound);
P_KillMobj(banana, inflictor, source, DMG_NORMAL);
P_SetObjectMomZ(banana, 8*FRACUNIT, false);
P_SetObjectMomZ(banana, 24*FRACUNIT, false);
if (inflictor)
P_InstaThrust(banana, R_PointToAngle2(inflictor->x, inflictor->y, banana->x, banana->y)+ANGLE_90, 16*FRACUNIT);
}
@ -6965,9 +6964,14 @@ void K_DropHnextList(player_t *player, boolean keepshields)
mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT8 amount)
{
mobj_t *drop = P_SpawnMobj(x, y, z, MT_FLOATINGITEM);
mobj_t *backdrop = P_SpawnMobjFromMobj(drop, 0, 0, 0, MT_OVERLAY);
P_SetTarget(&backdrop->target, drop);
P_SetMobjState(backdrop, S_ITEMBACKDROP);
P_SetScale(drop, drop->scale>>4);
drop->destscale = (3*drop->destscale)/2;
drop->angle = angle;
P_Thrust(drop,
FixedAngle(P_RandomFixed(PR_ITEM_ROULETTE) * 180) + angle,
@ -7043,6 +7047,9 @@ mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8
}
drop->flags |= MF_NOCLIPTHING;
backdrop->dispoffset = 1;
P_SetTarget(&backdrop->tracer, drop);
backdrop->flags2 |= MF2_LINKDRAW;
return drop;
}
@ -7096,7 +7103,7 @@ void K_DropRocketSneaker(player_t *player)
flingangle = ANG60;
S_StartSound(shoe, shoe->info->deathsound);
P_SetObjectMomZ(shoe, 8*FRACUNIT, false);
P_SetObjectMomZ(shoe, 24*FRACUNIT, false);
P_InstaThrust(shoe, R_PointToAngle2(shoe->target->x, shoe->target->y, shoe->x, shoe->y)+flingangle, 16*FRACUNIT);
shoe->momx += shoe->target->momx;
shoe->momy += shoe->target->momy;
@ -9898,8 +9905,16 @@ void K_KartUpdatePosition(player_t *player)
if (leveltime < starttime || oldposition == 0)
oldposition = position;
if (oldposition != position) // Changed places?
player->positiondelay = 10; // Position number growth
if (position != oldposition) // Changed places?
{
if (position < oldposition && P_IsDisplayPlayer(player) == true)
{
// Play sound when getting closer to 1st.
S_StartSound(player->mo, sfx_mbs41);
}
player->positiondelay = POS_DELAY_TIME + 4; // Position number growth
}
/* except in FREE PLAY */
if (player->curshield == KSHIELD_TOP &&
@ -11394,9 +11409,10 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
{
const angle_t lr = ANGLE_45;
fixed_t momz = FixedDiv(player->mo->momz, mapobjectscale); // bring momz back to scale...
fixed_t invertscale = FixedDiv(FRACUNIT, K_GrowShrinkSpeedMul(player));
fixed_t speedmult = max(0, FRACUNIT - abs(momz)/TRICKMOMZRAMP); // TRICKMOMZRAMP momz is minimum speed (Should be 20)
fixed_t basespeed = K_GetKartSpeed(player, false, false); // at WORSE, keep your normal speed when tricking.
fixed_t speed = FixedMul(speedmult, P_AproxDistance(player->mo->momx, player->mo->momy));
fixed_t basespeed = FixedMul(invertscale, K_GetKartSpeed(player, false, false)); // at WORSE, keep your normal speed when tricking.
fixed_t speed = FixedMul(invertscale, FixedMul(speedmult, P_AproxDistance(player->mo->momx, player->mo->momy)));
K_trickPanelTimingVisual(player, momz);
@ -11483,14 +11499,12 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
}
else if (cmd->throwdir < 0)
{
boolean relative = true;
player->mo->momx /= 3;
player->mo->momy /= 3;
if (player->mo->momz * P_MobjFlip(player->mo) <= 0)
{
relative = false;
player->mo->momz = 0; // relative = false;
}
// Calculate speed boost decay:
@ -11499,7 +11513,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
player->trickboostdecay = min(TICRATE*3/4, abs(momz/FRACUNIT));
//CONS_Printf("decay: %d\n", player->trickboostdecay);
P_SetObjectMomZ(player->mo, 48*FRACUNIT, relative);
player->mo->momz += P_MobjFlip(player->mo)*48*mapobjectscale;
player->trickpanel = 4;
}
}

View file

@ -410,6 +410,8 @@ typedef enum
{
mpause_addons = 0,
mpause_switchmap,
mpause_restartmap,
mpause_tryagain,
#ifdef HAVE_DISCORDRPC
mpause_discordrequests,
#endif
@ -599,6 +601,7 @@ typedef enum
CSSTEP_CHARS,
CSSTEP_ALTS,
CSSTEP_COLORS,
CSSTEP_FOLLOWERCATEGORY,
CSSTEP_FOLLOWER,
CSSTEP_FOLLOWERCOLORS,
CSSTEP_READY
@ -614,6 +617,7 @@ typedef struct setup_player_s
UINT8 delay;
UINT16 color;
UINT8 mdepth;
boolean hitlag;
// Hack, save player 1's original device even if they init charsel with keyboard.
// If they play ALONE, allow them to retain that original device, otherwise, ignore this.
@ -622,7 +626,8 @@ typedef struct setup_player_s
UINT8 changeselect;
INT32 followern;
INT16 followercategory;
INT16 followern;
UINT16 followercolor;
tic_t follower_tics;
tic_t follower_timer;
@ -975,6 +980,8 @@ extern consvar_t cv_dummymenuplayer;
extern consvar_t cv_dummyspectator;
// Bunch of funny functions for the pause menu...~
void M_RestartMap(INT32 choice); // Restart level (MP)
void M_TryAgain(INT32 choice); // Try again (SP)
void M_ConfirmSpectate(INT32 choice); // Spectate confirm when you're alone
void M_ConfirmEnterGame(INT32 choice); // Enter game confirm when you're alone
void M_ConfirmSpectateChange(INT32 choice); // Splitscreen spectate/play menu func

View file

@ -1071,7 +1071,6 @@ menuitem_t OPTIONS_GameplayItems[] =
{IT_KEYHANDLER | IT_NOTHING, NULL, "Bananas", NULL, {.routine = M_HandleItemToggles}, KITEM_BANANA, 0},
{IT_KEYHANDLER | IT_NOTHING, NULL, "Bananas x3", NULL, {.routine = M_HandleItemToggles}, KRITEM_TRIPLEBANANA, 0},
{IT_KEYHANDLER | IT_NOTHING, NULL, "Bananas x10", NULL, {.routine = M_HandleItemToggles}, KRITEM_TENFOLDBANANA, 0},
{IT_KEYHANDLER | IT_NOTHING, NULL, "Proximity Mines", NULL, {.routine = M_HandleItemToggles}, KITEM_MINE, 0},
{IT_KEYHANDLER | IT_NOTHING, NULL, "Orbinauts", NULL, {.routine = M_HandleItemToggles}, KITEM_ORBINAUT, 0},
@ -1594,6 +1593,12 @@ menuitem_t PAUSE_Main[] =
{IT_STRING | IT_SUBMENU, "CHANGE MAP", "M_ICOMAP",
NULL, {.submenu = &PAUSE_GamemodesDef}, 0, 0},
{IT_STRING | IT_CALL, "RESTART MAP", "M_ICORE",
NULL, {.routine = M_RestartMap}, 0, 0},
{IT_STRING | IT_CALL, "TRY AGAIN", "M_ICORE",
NULL, {.routine = M_TryAgain}, 0, 0},
#ifdef HAVE_DISCORDRPC
{IT_STRING | IT_CALL, "DISCORD REQUESTS", "M_ICODIS",
NULL, {NULL}, 0, 0},

View file

@ -969,7 +969,8 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
{
angle_t angamt = ANGLE_MAX;
UINT16 i, numoptions = 0;
UINT16 l = 0, r = 0;
INT16 l = 0, r = 0;
INT16 subtractcheck;
switch (p->mdepth)
{
@ -979,8 +980,11 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
case CSSTEP_COLORS:
numoptions = nummenucolors;
break;
case CSSTEP_FOLLOWERCATEGORY:
numoptions = numfollowercategories+1;
break;
case CSSTEP_FOLLOWER:
numoptions = numfollowers+1;
numoptions = followercategories[p->followercategory].numincategory;
break;
case CSSTEP_FOLLOWERCOLORS:
numoptions = nummenucolors+2;
@ -994,17 +998,19 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
return;
}
subtractcheck = 1 ^ (numoptions & 1);
angamt /= numoptions;
for (i = 0; i < numoptions; i++)
{
fixed_t cx = x << FRACBITS, cy = y << FRACBITS;
boolean subtract = (i & 1);
boolean subtract = (i & 1) == subtractcheck;
angle_t ang = ((i+1)/2) * angamt;
patch_t *patch = NULL;
UINT8 *colormap = NULL;
fixed_t radius = 28<<FRACBITS;
UINT16 n = 0;
INT16 n = 0;
switch (p->mdepth)
{
@ -1017,7 +1023,7 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
n -= ((i+1)/2);
else
n += ((i+1)/2);
n %= numoptions;
n = (n + numoptions) % numoptions;
skin = setup_chargrid[p->gridx][p->gridy].skinlist[n];
patch = faceprefix[skin][FACE_RANK];
@ -1061,16 +1067,16 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
break;
}
case CSSTEP_FOLLOWER:
case CSSTEP_FOLLOWERCATEGORY:
{
follower_t *fl = NULL;
followercategory_t *fc = NULL;
n = (p->followern + 1) + numoptions/2;
n = (p->followercategory + 1) + numoptions/2;
if (subtract)
n -= ((i+1)/2);
else
n += ((i+1)/2);
n %= numoptions;
n = (n + numoptions) % numoptions;
if (n == 0)
{
@ -1078,11 +1084,71 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
}
else
{
fl = &followers[n - 1];
fc = &followercategories[n - 1];
patch = W_CachePatchName(fc->icon, PU_CACHE);
}
radius = 24<<FRACBITS;
cx -= (SHORT(patch->width) << FRACBITS) >> 1;
cy -= (SHORT(patch->height) << FRACBITS) >> 1;
break;
}
case CSSTEP_FOLLOWER:
{
follower_t *fl = NULL;
INT16 startfollowern = p->followern;
if (i == 0)
{
n = p->followern;
r = (numoptions+1)/2;
while (r)
{
n--;
if (n < 0)
n = numfollowers-1;
if (n == startfollowern)
break;
if (followers[n].category == p->followercategory)
r--;
}
l = r = n;
}
else if (subtract)
{
do
{
l--;
if (l < 0)
l = numfollowers-1;
if (l == startfollowern)
break;
}
while (followers[l].category != p->followercategory);
n = l;
}
else
{
do
{
r++;
if (r >= numfollowers)
r = 0;
if (r == startfollowern)
break;
}
while (followers[r].category != p->followercategory);
n = r;
}
{
fl = &followers[n];
patch = W_CachePatchName(fl->icon, PU_CACHE);
colormap = R_GetTranslationColormap(TC_DEFAULT,
K_GetEffectiveFollowerColor(p->followercolor, p->color),
K_GetEffectiveFollowerColor(fl->defaultcolor, p->color),
GTC_MENUCACHE
);
}
@ -1142,7 +1208,12 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
ang = (signed)(ang - (angamt/2));
if (p->rotate)
ang = (signed)(ang + ((angamt / CSROTATETICS) * p->rotate));
{
SINT8 rotate = p->rotate;
if ((p->hitlag == true) && (setup_animcounter & 1))
rotate = -rotate;
ang = (signed)(ang + ((angamt / CSROTATETICS) * rotate));
}
cx += FixedMul(radius, FINECOSINE(ang >> ANGLETOFINESHIFT));
cy -= FixedMul(radius, FINESINE(ang >> ANGLETOFINESHIFT)) / 3;
@ -1154,41 +1225,40 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
}
// returns false if the character couldn't be rendered
static boolean M_DrawCharacterSprite(INT16 x, INT16 y, INT16 skin, INT32 addflags, UINT8 *colormap)
static boolean M_DrawCharacterSprite(INT16 x, INT16 y, INT16 skin, boolean charflip, boolean animate, INT32 addflags, UINT8 *colormap)
{
UINT8 spr;
spritedef_t *sprdef;
spriteframe_t *sprframe;
patch_t *sprpatch;
UINT8 rotation = (charflip ? 1 : 7);
UINT32 frame = animate ? setup_animcounter : 0;
UINT32 flags = 0;
UINT32 frame;
spr = P_GetSkinSprite2(&skins[skin], SPR2_FSTN, NULL);
spr = P_GetSkinSprite2(&skins[skin], SPR2_STIN, NULL);
sprdef = &skins[skin].sprites[spr];
if (!sprdef->numframes) // No frames ??
return false; // Can't render!
frame = states[S_KART_FAST].frame & FF_FRAMEMASK;
if (frame >= sprdef->numframes) // Walking animation missing
frame = 0; // Try to use standing frame
frame %= sprdef->numframes;
sprframe = &sprdef->spriteframes[frame];
sprpatch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE);
sprpatch = W_CachePatchNum(sprframe->lumppat[rotation], PU_CACHE);
if (sprframe->flip & 1) // Only for first sprite
flags |= V_FLIP; // This sprite is left/right flipped!
if (sprframe->flip & (1<<rotation)) // Only for first sprite
{
addflags ^= V_FLIP; // This sprite is left/right flipped!
}
if (skins[skin].highresscale != FRACUNIT)
{
V_DrawFixedPatch(x<<FRACBITS,
y<<FRACBITS,
skins[skin].highresscale,
flags, sprpatch, colormap);
addflags, sprpatch, colormap);
}
else
V_DrawMappedPatch(x, y, addflags|flags, sprpatch, colormap);
V_DrawMappedPatch(x, y, addflags, sprpatch, colormap);
return true;
}
@ -1196,7 +1266,7 @@ static boolean M_DrawCharacterSprite(INT16 x, INT16 y, INT16 skin, INT32 addflag
// Returns false is the follower shouldn't be rendered.
// 'num' can be used to directly specify the follower number, but doing this will not animate it.
// if a setup_player_t is specified instead, its data will be used to animate the follower sprite.
static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, INT32 addflags, UINT16 color, setup_player_t *p)
static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, boolean charflip, INT32 addflags, UINT16 color, setup_player_t *p)
{
spritedef_t *sprdef;
@ -1207,6 +1277,7 @@ static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, INT32 addflags,
UINT32 useframe;
follower_t fl;
UINT8 *colormap = NULL;
UINT8 rotation = (charflip ? 1 : 7);
if (p != NULL)
followernum = p->followern;
@ -1238,14 +1309,11 @@ static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, INT32 addflags,
useframe = 0; // frame doesn't exist, we went beyond it... what?
sprframe = &sprdef->spriteframes[useframe];
patch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE);
patch = W_CachePatchNum(sprframe->lumppat[rotation], PU_CACHE);
if (sprframe->flip & 2) // Only for first sprite
if (sprframe->flip & (1<<rotation)) // Only for first sprite
{
if (addflags & V_FLIP)
addflags &= ~V_FLIP;
else
addflags |= V_FLIP; // This sprite is left/right flipped!
addflags ^= V_FLIP; // This sprite is left/right flipped!
}
fixed_t sine = 0;
@ -1253,35 +1321,33 @@ static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, INT32 addflags,
if (p != NULL)
{
sine = FixedMul(fl.bobamp, FINESINE(((FixedMul(4 * M_TAU_FIXED, fl.bobspeed) * p->follower_timer)>>ANGLETOFINESHIFT) & FINEMASK));
color = K_GetEffectiveFollowerColor(p->followercolor, p->color);
color = K_GetEffectiveFollowerColor(
(p->mdepth < CSSTEP_FOLLOWERCOLORS) ? fl.defaultcolor : p->followercolor,
p->color);
}
colormap = R_GetTranslationColormap(TC_DEFAULT, color, GTC_MENUCACHE);
V_DrawFixedPatch((x)*FRACUNIT, ((y-12)*FRACUNIT) + sine - fl.zoffs, fl.scale, addflags, patch, colormap);
V_DrawFixedPatch((x*FRACUNIT), ((y-12)*FRACUNIT) + sine, fl.scale, addflags, patch, colormap);
return true;
}
static void M_DrawCharSelectSprite(UINT8 num, INT16 x, INT16 y)
static void M_DrawCharSelectSprite(UINT8 num, INT16 x, INT16 y, boolean charflip)
{
setup_player_t *p = &setup_player[num];
UINT8 cnum = p->clonenum;
UINT8 color;
UINT8 *colormap;
// for p1 alone don't try to preview things on pages that don't exist lol.
if (p->mdepth == CSSTEP_CHARS && setup_numplayers == 1)
cnum = setup_page;
if (p->skin < 0)
return;
INT16 skin = setup_chargrid[p->gridx][p->gridy].skinlist[cnum];
UINT8 color = p->color;
UINT8 *colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
INT32 flags = 0;
if (p->mdepth < CSSTEP_COLORS)
color = skins[p->skin].prefcolor;
else
color = p->color;
colormap = R_GetTranslationColormap(p->skin, color, GTC_MENUCACHE);
// Flip for left-side players
if (!(num & 1))
flags ^= V_FLIP;
if (skin >= 0)
M_DrawCharacterSprite(x, y, skin, flags, colormap);
M_DrawCharacterSprite(x, y, p->skin, charflip, (p->mdepth == CSSTEP_READY), 0, colormap);
}
static void M_DrawCharSelectPreview(UINT8 num)
@ -1289,6 +1355,7 @@ static void M_DrawCharSelectPreview(UINT8 num)
INT16 x = 11, y = 5;
char letter = 'A' + num;
setup_player_t *p = &setup_player[num];
boolean charflip = !!(num & 1);
if (num & 1)
x += 233;
@ -1300,28 +1367,10 @@ static void M_DrawCharSelectPreview(UINT8 num)
if (p->mdepth >= CSSTEP_CHARS)
{
M_DrawCharSelectSprite(num, x+32, y+75);
if (p->mdepth >= CSSTEP_FOLLOWER)
{
M_DrawFollowerSprite(x+16, y+75, -1, !(num & 1) ? V_FLIP : 0, 0, p);
}
M_DrawCharSelectSprite(num, x+32, y+75, charflip);
M_DrawCharSelectCircle(p, x+32, y+64);
}
if ((setup_animcounter/10) & 1 && gamestate == GS_MENU) // Not drawn outside of GS_MENU.
{
if (p->mdepth == CSSTEP_NONE && num == setup_numplayers)
{
V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PSTART", PU_CACHE));
}
else if (p->mdepth >= CSSTEP_READY)
{
V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PREADY", PU_CACHE));
}
}
V_DrawScaledPatch(x+9, y+2, 0, W_CachePatchName("FILEBACK", PU_CACHE));
V_DrawScaledPatch(x, y+2, 0, W_CachePatchName(va("CHARSEL%c", letter), PU_CACHE));
if (p->mdepth > CSSTEP_PROFILE)
@ -1334,6 +1383,23 @@ static void M_DrawCharSelectPreview(UINT8 num)
V_DrawFileString(x+16, y+2, 0, "PLAYER");
}
if (p->mdepth >= CSSTEP_FOLLOWER)
{
M_DrawFollowerSprite(x+32+((charflip ? 1 : -1)*16), y+75, -1, charflip, 0, 0, p);
}
if ((setup_animcounter/10) & 1 && gamestate == GS_MENU) // Not drawn outside of GS_MENU.
{
if (p->mdepth == CSSTEP_NONE && num == setup_numplayers)
{
V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PSTART", PU_CACHE));
}
else if (p->mdepth >= CSSTEP_READY)
{
V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PREADY", PU_CACHE));
}
}
// Profile selection
if (p->mdepth == CSSTEP_PROFILE)
{
@ -1549,39 +1615,38 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p)
// check what setup_player is doing in priority.
if (sp->mdepth >= CSSTEP_CHARS)
{
skinnum = setup_chargrid[sp->gridx][sp->gridy].skinlist[sp->clonenum];
if (skinnum >= 0)
{
if (M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap))
if (M_DrawCharacterSprite(x-22, y+119, skinnum, false, false, 0, colormap))
V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap);
if (sp->mdepth >= CSSTEP_FOLLOWER)
{
if (M_DrawFollowerSprite(x-44 +12, y+119, 0, V_FLIP, 0, sp))
{
UINT16 col = K_GetEffectiveFollowerColor(sp->followercolor, sp->color);;
patch_t *ico = W_CachePatchName(followers[sp->followern].icon, PU_CACHE);
UINT8 *fcolormap;
fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE);
V_DrawMappedPatch(x+14+18, y+66, 0, ico, fcolormap);
}
}
}
M_DrawCharSelectCircle(sp, x-22, y+104);
if (sp->mdepth >= CSSTEP_FOLLOWER)
{
if (M_DrawFollowerSprite(x-22 - 16, y+119, 0, false, 0, 0, sp))
{
UINT16 col = K_GetEffectiveFollowerColor(sp->followercolor, sp->color);;
patch_t *ico = W_CachePatchName(followers[sp->followern].icon, PU_CACHE);
UINT8 *fcolormap;
fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE);
V_DrawMappedPatch(x+14+18, y+66, 0, ico, fcolormap);
}
}
}
else if (skinnum > -1) // otherwise, read from profile.
{
UINT16 col = K_GetEffectiveFollowerColor(p->followercolor, p->color);;
UINT8 fln = K_FollowerAvailable(p->follower);
if (M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap))
if (M_DrawCharacterSprite(x-22, y+119, skinnum, false, false, 0, colormap))
V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap);
if (M_DrawFollowerSprite(x-44 +12, y+119, fln, V_FLIP, col, NULL))
if (M_DrawFollowerSprite(x-22 - 16, y+119, fln, false, 0, col, NULL))
{
patch_t *ico = W_CachePatchName(followers[fln].icon, PU_CACHE);
UINT8 *fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE);
@ -3594,19 +3659,29 @@ void M_DrawPause(void)
{
case IT_STRING:
{
char iconame[9]; // 8 chars + \0
patch_t *pp;
if (i == itemOn)
{
strcpy(iconame, currentMenu->menuitems[i].tooltip);
iconame[7] = '2'; // Yes this is a stupid hack. Replace the last character with a 2 when we're selecting this graphic.
if (i == mpause_restartmap || i == mpause_tryagain)
{
pp = W_CachePatchName(
va("M_ICOR2%c", ('A'+(pausemenu.ticker & 1))),
PU_CACHE);
}
else
{
char iconame[9]; // 8 chars + \0
strcpy(iconame, currentMenu->menuitems[i].tooltip);
iconame[7] = '2'; // Yes this is a stupid hack. Replace the last character with a 2 when we're selecting this graphic.
pp = W_CachePatchName(iconame, PU_CACHE);
pp = W_CachePatchName(iconame, PU_CACHE);
}
}
else
{
pp = W_CachePatchName(currentMenu->menuitems[i].tooltip, PU_CACHE);
}
// 294 - 261 = 33
// We need to move 33 px in 50 tics which means we move 33/50 = 0.66 px every tic = 2/3 of the offset.
@ -3898,6 +3973,18 @@ static void M_DrawReplayHutReplayInfo(menudemo_t *demoref)
if (demoref->gametype == GT_RACE)
{
V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "TIME");
}
else
{
V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "SCORE");
}
if (demoref->standings[0].timeorscore == (UINT32_MAX-1))
{
V_DrawThinString(x+32, y+39, V_SNAPTOTOP, "NO CONTEST");
}
else if (demoref->gametype == GT_RACE)
{
V_DrawRightAlignedString(x+84, y+40, V_SNAPTOTOP, va("%d'%02d\"%02d",
G_TicsToMinutes(demoref->standings[0].timeorscore, true),
G_TicsToSeconds(demoref->standings[0].timeorscore),
@ -3906,7 +3993,6 @@ static void M_DrawReplayHutReplayInfo(menudemo_t *demoref)
}
else
{
V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "SCORE");
V_DrawString(x+32, y+40, V_SNAPTOTOP, va("%d", demoref->standings[0].timeorscore));
}
@ -3915,7 +4001,7 @@ static void M_DrawReplayHutReplayInfo(menudemo_t *demoref)
// Lat: 08/06/2020: For some reason missing skins have their value set to 255 (don't even ask me why I didn't write this)
// and for an even STRANGER reason this passes the first check below, so we're going to make sure that the skin here ISN'T 255 before we do anything stupid.
if (demoref->standings[0].skin != 0xFF)
if (demoref->standings[0].skin < numskins)
{
patch = faceprefix[demoref->standings[0].skin][FACE_WANTED];
colormap = R_GetTranslationColormap(
@ -4116,7 +4202,7 @@ void M_DrawReplayStartMenu(void)
// Lat: 08/06/2020: For some reason missing skins have their value set to 255 (don't even ask me why I didn't write this)
// and for an even STRANGER reason this passes the first check below, so we're going to make sure that the skin here ISN'T 255 before we do anything stupid.
if (demoref->standings[i].skin != 0xFF)
if (demoref->standings[i].skin < numskins)
{
patch = faceprefix[demoref->standings[i].skin][FACE_RANK];
colormap = R_GetTranslationColormap(

View file

@ -357,7 +357,7 @@ static void M_EraseDataResponse(INT32 ch)
void M_EraseData(INT32 choice)
{
const char *eschoice, *esstr = M_GetText("Are you sure you want to erase\n%s?\n\n(Press A to confirm)\n");
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;
@ -1517,7 +1517,7 @@ static void M_HandleMenuInput(void)
{
if (((currentMenu->menuitems[itemOn].status & IT_CALLTYPE) & IT_CALL_NOTMODIFIED) && majormods)
{
M_StartMessage(M_GetText("This cannot be done with complex addons\nor in a cheated game.\n\n(Press a key)\n"), NULL, MM_NOTHING);
M_StartMessage(M_GetText("This cannot be done with complex addons\nor in a cheated game.\n\nPress (B)\n"), NULL, MM_NOTHING);
return;
}
}
@ -1583,6 +1583,12 @@ void M_Ticker(void)
{
INT32 i;
if (!menuactive)
{
noFurtherInput = false;
return;
}
if (menutransition.tics != 0 || menutransition.dest != 0)
{
noFurtherInput = true;
@ -2035,7 +2041,7 @@ void M_QuitSRB2(INT32 choice)
// We pick index 0 which is language sensitive, or one at random,
// between 1 and maximum number.
(void)choice;
M_StartMessage("Are you sure you want to quit playing?\n\n(Press A to exit)", FUNCPTRCAST(M_QuitResponse), MM_YESNO);
M_StartMessage("Are you sure you want to quit playing?\n\nPress (A) to confirm or (B) to cancel", FUNCPTRCAST(M_QuitResponse), MM_YESNO);
}
// =========
@ -2063,6 +2069,12 @@ static void M_SetupProfileGridPos(setup_player_t *p)
// 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)
p->followercategory = -1;
else
p->followercategory = followers[p->followern].category;
p->followercolor = pr->followercolor;
// Now position the grid for skin
@ -2150,6 +2162,7 @@ void M_CharacterSelectInit(void)
{
// 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:
@ -2486,6 +2499,16 @@ static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num)
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;
@ -2578,6 +2601,15 @@ static boolean M_HandleCharacterGrid(setup_player_t *p, UINT8 num)
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;
@ -2692,6 +2724,14 @@ static void M_HandleCharRotate(setup_player_t *p, UINT8 num)
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)
@ -2716,8 +2756,7 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num)
if (M_MenuConfirmPressed(num) /*|| M_MenuButtonPressed(num, MBT_START)*/)
{
p->mdepth = CSSTEP_FOLLOWER;
M_GetFollowerState(p);
p->mdepth = CSSTEP_FOLLOWERCATEGORY;
S_StartSound(NULL, sfx_s3k63);
M_SetMenuDelay(num);
}
@ -2734,6 +2773,17 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num)
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)
@ -2764,16 +2814,103 @@ static void M_AnimateFollower(setup_player_t *p)
p->follower_timer++;
}
static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num)
static void M_HandleFollowerCategoryRotate(setup_player_t *p, UINT8 num)
{
if (cv_splitdevice.value)
num = 0;
if (menucmd[num].dpad_lr > 0)
{
p->followern++;
if (p->followern >= numfollowers)
p->followercategory++;
if (p->followercategory >= 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 = 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(p);
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 != p->followercategory)
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 != p->followercategory);
M_GetFollowerState(p);
@ -2783,9 +2920,17 @@ static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num)
}
else if (menucmd[num].dpad_lr < 0)
{
p->followern--;
if (p->followern < -1)
p->followern = numfollowers-1;
do
{
p->followern--;
if (p->followern < 0)
p->followern = numfollowers-1;
if (p->followern == startfollowern)
break;
}
while (followers[p->followern].category != p->followercategory);
M_GetFollowerState(p);
p->rotate = -CSROTATETICS;
p->delay = CSROTATETICS;
@ -2811,10 +2956,19 @@ static void M_HandleFollowerRotate(setup_player_t *p, UINT8 num)
}
else if (M_MenuBackPressed(num))
{
p->mdepth = CSSTEP_COLORS;
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)
@ -2849,10 +3003,24 @@ static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 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)
@ -2901,6 +3069,9 @@ boolean M_CharacterSelectHandler(INT32 choice)
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;
@ -2956,33 +3127,27 @@ static void M_MPConfirmCharacterSelection(void)
UINT8 i;
INT16 col;
char commandnames[][MAXSTRINGLENGTH] = { "skin ", "skin2 ", "skin3 ", "skin4 "};
// ^ laziness 100 (we append a space directly so that we don't have to do it later too!!!!)
for (i = 0; i < splitscreen +1; i++)
{
char cmd[MAXSTRINGLENGTH];
// 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
CV_StealthSetValue(&cv_follower[i], setup_player[i].followern);
// follower color
CV_StealthSetValue(&cv_followercolor[i], setup_player[i].followercolor);
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!
// This is a hack to make sure we call Skin[x]_OnChange afterwards
CV_StealthSetValue(&cv_skin[i], -1);
CV_StealthSet(&cv_skin[i], skins[setup_player[i].skin].name);
strcpy(cmd, commandnames[i]);
strcat(cmd, skins[setup_player[i].skin].name);
COM_ImmedExecute(cmd);
// ...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);
@ -3004,6 +3169,8 @@ void M_CharacterSelectTick(void)
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;
@ -3034,7 +3201,7 @@ void M_CharacterSelectTick(void)
optionsmenu.profile->color = setup_player[0].color;
// save follower
strcpy(optionsmenu.profile->follower, followers[setup_player[0].followern].skinname);
strcpy(optionsmenu.profile->follower, followers[setup_player[0].followern].name);
optionsmenu.profile->followercolor = setup_player[0].followercolor;
// reset setup_player
@ -3051,7 +3218,10 @@ void M_CharacterSelectTick(void)
CV_StealthSet(&cv_skin[i], skins[setup_player[i].skin].name);
CV_StealthSetValue(&cv_playercolor[i], setup_player[i].color);
CV_StealthSetValue(&cv_follower[i], setup_player[i].followern);
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);
}
@ -3269,7 +3439,15 @@ static void M_LevelListFromGametype(INT16 gt)
while (cup)
{
if (cup->unlockrequired == -1 || unlockables[cup->unlockrequired].unlocked)
{
highestid = cup->id;
if (Playing() && mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->cup == cup)
{
cupgrid.x = cup->id % CUPMENU_COLUMNS;
cupgrid.y = (cup->id / CUPMENU_COLUMNS) % CUPMENU_ROWS;
cupgrid.pageno = cup->id / (CUPMENU_COLUMNS * CUPMENU_ROWS);
}
}
cup = cup->next;
}
@ -4775,7 +4953,7 @@ void M_HandleProfileSelect(INT32 ch)
#if 0
if (optionsmenu.profilen == 0)
{
M_StartMessage(M_GetText("Are you sure you wish\nto use the Guest Profile?\nThis profile cannot be customised.\nIt is recommended to create\na new Profile instead.\n\n(Press A to confirm)"), FUNCPTRCAST(M_FirstPickProfile), MM_YESNO);
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
@ -4917,7 +5095,7 @@ void M_ConfirmProfile(INT32 choice)
}
else
{
M_StartMessage(M_GetText("Are you sure you wish to\nselect this profile?\n\n(Press A to confirm)"), FUNCPTRCAST(M_FirstPickProfile), MM_YESNO);
M_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);
}
}
@ -5123,7 +5301,7 @@ void M_ProfileControlsConfirm(INT32 choice)
{
(void)choice;
//M_StartMessage(M_GetText("Exiting will save the control changes\nfor this Profile.\nIs this okay?\n\n(Press A to confirm)"), FUNCPTRCAST(M_ProfileControlSaveResponse), MM_YESNO);
//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);
@ -5500,7 +5678,7 @@ void M_CheckProfileData(INT32 choice)
if (np < 2)
{
S_StartSound(NULL, sfx_s3k7b);
M_StartMessage("There are no custom profiles.\n\n(Press any button)", NULL, MM_NOTHING);
M_StartMessage("There are no custom profiles.\n\nPress (B)", NULL, MM_NOTHING);
return;
}
@ -5564,9 +5742,9 @@ void M_HandleProfileErase(INT32 choice)
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\n(Press A to confirm)", FUNCPTRCAST(M_EraseProfileResponse), MM_YESNO);
M_StartMessage("Your ""\x85""current profile""\x80"" will be erased.\nAre you sure you want to proceed?\nDeleting this profile will also\nreturn you to the title screen.\n\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\n(Press A to confirm)", FUNCPTRCAST(M_EraseProfileResponse), MM_YESNO);
M_StartMessage("This profile will be erased.\nAre you sure you want to proceed?\n\nPress (A) to confirm or (B) to cancel", FUNCPTRCAST(M_EraseProfileResponse), MM_YESNO);
M_SetMenuDelay(pid);
}
@ -5700,6 +5878,8 @@ struct pausemenu_s pausemenu;
// Pause menu!
void M_OpenPauseMenu(void)
{
INT32 i = 0;
currentMenu = &PAUSE_MainDef;
// Ready the variables
@ -5716,6 +5896,8 @@ void M_OpenPauseMenu(void)
PAUSE_Main[mpause_addons].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
@ -5735,9 +5917,36 @@ void M_OpenPauseMenu(void)
if (server || IsPlayerAdmin(consoleplayer))
{
PAUSE_Main[mpause_switchmap].status = IT_STRING | IT_SUBMENU;
for (i = 0; i < PAUSE_GamemodesDef.numitems; i++)
{
if (PAUSE_GamemodesMenu[i].mvar2 != gametype)
continue;
PAUSE_GamemodesDef.lastOn = i;
break;
}
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())
{
@ -5766,6 +5975,7 @@ void M_QuitPauseMenu(INT32 choice)
void M_PauseTick(void)
{
pausemenu.offset /= 2;
pausemenu.ticker++;
if (pausemenu.closing)
{
@ -5814,6 +6024,37 @@ boolean M_PauseInputs(INT32 ch)
return false;
}
// 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)
{
@ -5828,7 +6069,7 @@ void M_ConfirmEnterGame(INT32 choice)
(void)choice;
if (!cv_allowteamchange.value)
{
M_StartMessage(M_GetText("The server is not allowing\nteam changes at this time.\nPress a key.\n"), NULL, MM_NOTHING);
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);
@ -5860,7 +6101,7 @@ void M_EndGame(INT32 choice)
if (!Playing())
return;
M_StartMessage(M_GetText("Are you sure you want to return\nto the title screen?\n(Press A to confirm)\n"), FUNCPTRCAST(M_ExitGameResponse), MM_YESNO);
M_StartMessage(M_GetText("Are you sure you want to return\nto the title screen?\nPress (A) to confirm or (B) to cancel\n"), FUNCPTRCAST(M_ExitGameResponse), MM_YESNO);
}
@ -6042,7 +6283,7 @@ void M_ReplayHut(INT32 choice)
}
if (!preparefilemenu(false, true))
{
M_StartMessage("No replays found.\n\n(Press a key)\n", NULL, MM_NOTHING);
M_StartMessage("No replays found.\n\nPress (B)\n", NULL, MM_NOTHING);
return;
}
else if (!demo.inreplayhut)
@ -6118,7 +6359,7 @@ void M_HandleReplayHutList(INT32 choice)
if (!preparefilemenu(false, true))
{
S_StartSound(NULL, sfx_s224);
M_StartMessage(va("%c%s\x80\nThis folder is empty.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING);
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))
@ -6137,7 +6378,7 @@ void M_HandleReplayHutList(INT32 choice)
else
{
S_StartSound(NULL, sfx_s26d);
M_StartMessage(va("%c%s\x80\nThis folder is too deep to navigate to!\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING);
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;
@ -6264,7 +6505,7 @@ void M_Addons(INT32 choice)
if (!preparefilemenu(false, false))
{
M_StartMessage(va("No files/folders found.\n\n%s\n\n(Press a key)\n", LOCATIONSTRING1),NULL,MM_NOTHING);
M_StartMessage(va("No files/folders found.\n\n%s\n\nPress (B)\n", LOCATIONSTRING1),NULL,MM_NOTHING);
return;
}
else
@ -6297,7 +6538,7 @@ char *M_AddonsHeaderPath(void)
#define UNEXIST S_StartSound(NULL, sfx_s26d);\
M_SetupNextMenu(MISC_AddonsDef.prevMenu, false);\
M_StartMessage(va("\x82%s\x80\nThis folder no longer exists!\nAborting to main menu.\n\n(Press a key)\n", M_AddonsHeaderPath()),NULL,MM_NOTHING)
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
@ -6341,19 +6582,19 @@ void M_AddonsRefresh(void)
{
S_StartSound(NULL, sfx_s26d);
if (refreshdirmenu & REFRESHDIR_MAX)
message = va("%c%s\x80\nMaximum number of addons reached.\nA file could not be loaded.\nIf you wish to play with this addon, restart the game to clear existing ones.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
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\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
message = va("%c%s\x80\nA file was not loaded.\nCheck the console log for more info.\n\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\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname, ((refreshdirmenu & REFRESHDIR_ERROR) ? "errors" : "warnings"));
message = va("%c%s\x80\nA file was loaded with %s.\nCheck the console log for more info.\n\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\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
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;
}
@ -6472,7 +6713,7 @@ void M_HandleAddons(INT32 choice)
if (!preparefilemenu(false, false))
{
S_StartSound(NULL, sfx_s224);
M_StartMessage(va("%c%s\x80\nThis folder is empty.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING);
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))
@ -6491,7 +6732,7 @@ void M_HandleAddons(INT32 choice)
else
{
S_StartSound(NULL, sfx_s26d);
M_StartMessage(va("%c%s\x80\nThis folder is too deep to navigate to!\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING);
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;
@ -6507,11 +6748,11 @@ void M_HandleAddons(INT32 choice)
break;
case EXT_TXT:
M_StartMessage(va("%c%s\x80\nThis file may not be a console script.\nAttempt to run anyways? \n\n(Press A to confirm)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), dirmenu[dir_on[menudepthleft]]+DIR_STRING),FUNCPTRCAST(M_AddonExec),MM_YESNO);
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\n(Press A to confirm)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), dirmenu[dir_on[menudepthleft]]+DIR_STRING),FUNCPTRCAST(M_AddonExec),MM_YESNO);
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:

View file

@ -402,10 +402,7 @@ static int lib_pRandomByte(lua_State *L)
static int lib_pRandomKey(lua_State *L)
{
INT32 a = (INT32)luaL_checkinteger(L, 1);
NOHUD
if (a > 65536)
LUA_UsageWarning(L, "P_RandomKey: range > 65536 is undefined behavior");
lua_pushinteger(L, P_RandomKey(PR_UNDEFINED, a));
demo_writerng = 2;
return 1;
@ -415,15 +412,13 @@ static int lib_pRandomRange(lua_State *L)
{
INT32 a = (INT32)luaL_checkinteger(L, 1);
INT32 b = (INT32)luaL_checkinteger(L, 2);
NOHUD
if (b < a) {
if (b < a)
{
INT32 c = a;
a = b;
b = c;
}
if ((b-a+1) > 65536)
LUA_UsageWarning(L, "P_RandomRange: range > 65536 is undefined behavior");
lua_pushinteger(L, P_RandomRange(PR_UNDEFINED, a, b));
demo_writerng = 2;
return 1;

View file

@ -762,6 +762,13 @@ void Command_Setlives_f(void)
D_Cheat(consoleplayer, CHEAT_LIVES, atoi(COM_Argv(1)));
}
void Command_Grayscale_f(void)
{
REQUIRE_CHEATS;
COM_ImmedExecute("toggle palette \"\" GRAYPAL");
}
//
// OBJECTPLACE (and related variables)
//

View file

@ -75,6 +75,7 @@ void Command_Teleport_f(void);
void Command_RTeleport_f(void);
void Command_Skynum_f(void);
void Command_Weather_f(void);
void Command_Grayscale_f(void);
#ifdef _DEBUG
void Command_CauseCfail_f(void);
#endif

View file

@ -25,20 +25,56 @@
// RNG functions (not synched)
// ---------------------------
ATTRINLINE static UINT32 FUNCINLINE __external_prng__(void)
{
UINT32 rnd = rand();
#if RAND_MAX < 65535
// Compensate for especially bad randomness.
UINT32 rndv = (rand() & 1) << 15;
rnd ^= rndv;
#endif
// Shuffle like we do for our own PRNG, since RAND_MAX
// tends to be [0, INT32_MAX] instead of [0, UINT32_MAX].
rnd ^= rnd >> 13;
rnd ^= rnd >> 11;
rnd ^= rnd << 21;
return (rnd * 36548569);
}
ATTRINLINE static UINT32 FUNCINLINE __external_prng_bound__(UINT32 bound)
{
// Do rejection sampling to remove the modulo bias.
UINT32 threshold = -bound % bound;
for (;;)
{
UINT32 r = __external_prng__();
if (r >= threshold)
{
return r % bound;
}
}
}
/** Provides a random 32-bit number. Distribution is uniform.
* As with all M_Random functions, not synched in netgames.
*
* \return A random 32-bit number.
*/
UINT32 M_Random(void)
{
return __external_prng__();
}
/** Provides a random fixed point number. Distribution is uniform.
* As with all M_Random functions, not synched in netgames.
*
* \return A random fixed point number from [0,1).
* \return A random fixed point number from [0,1].
*/
fixed_t M_RandomFixed(void)
{
#if RAND_MAX < 65535
// Compensate for insufficient randomness.
fixed_t rndv = (rand()&1)<<15;
return rand()^rndv;
#else
return (rand() & 0xFFFF);
#endif
return (fixed_t)(__external_prng_bound__(FRACUNIT));
}
/** Provides a random byte. Distribution is uniform.
@ -48,7 +84,7 @@ fixed_t M_RandomFixed(void)
*/
UINT8 M_RandomByte(void)
{
return (rand() & 0xFF);
return (UINT8)(__external_prng_bound__(UINT8_MAX));
}
/** Provides a random integer for picking random elements from an array.
@ -58,9 +94,9 @@ UINT8 M_RandomByte(void)
* \param a Number of items in array.
* \return A random integer from [0,a).
*/
INT32 M_RandomKey(INT32 a)
UINT32 M_RandomKey(UINT32 a)
{
return (INT32)((rand()/((unsigned)RAND_MAX+1.0f))*a);
return __external_prng_bound__(a);
}
/** Provides a random integer in a given range.
@ -73,7 +109,7 @@ INT32 M_RandomKey(INT32 a)
*/
INT32 M_RandomRange(INT32 a, INT32 b)
{
return (INT32)((rand()/((unsigned)RAND_MAX+1.0f))*(b-a+1))+a;
return (INT32)(__external_prng_bound__((b - a) + 1)) + a;
}
@ -92,23 +128,56 @@ typedef struct
static rng_t rng; // The entire PRNG state
/** Provides a random fixed point number.
/** Provides a random 32 bit integer.
* This is a variant of an xorshift PRNG; state fits in a 32 bit integer structure.
*
* \return A random fixed point number from [0,1).
* \return A random, uniformly distributed number from [0,UINT32_MAX].
*/
ATTRINLINE static fixed_t FUNCINLINE __internal_prng__(pr_class_t pr_class)
ATTRINLINE static UINT32 FUNCINLINE __internal_prng__(pr_class_t pr_class)
{
rng.seed[pr_class] ^= rng.seed[pr_class] >> 13;
rng.seed[pr_class] ^= rng.seed[pr_class] >> 11;
rng.seed[pr_class] ^= rng.seed[pr_class] << 21;
return ( (rng.seed[pr_class] * 36548569) >> 4) & (FRACUNIT-1);
return (rng.seed[pr_class] * 36548569);
}
/** Provides a random number within a specified range.
*
* \return A random, uniformly distributed number from [0,bound].
*/
ATTRINLINE static UINT32 FUNCINLINE __internal_prng_bound__(pr_class_t pr_class, UINT32 bound)
{
// Do rejection sampling to remove the modulo bias.
UINT32 threshold = -bound % bound;
for (;;)
{
UINT32 r = __internal_prng__(pr_class);
if (r >= threshold)
{
return r % bound;
}
}
}
/** Provides a random fixed point number. Distribution is uniform.
* Literally a wrapper for the internal PRNG function.
*
* \return A random fixed point number from [0,1).
* \return A random fixed point number from [0,UINT32_MAX].
*/
#ifndef DEBUGRANDOM
UINT32 P_Random(pr_class_t pr_class)
{
#else
UINT32 P_RandomD(const char *rfile, INT32 rline, pr_class_t pr_class)
{
CONS_Printf("P_Random(%u) at: %sp %d\n", pr_class, rfile, rline);
#endif
return __internal_prng__(pr_class);
}
/** Provides a random fixed point number. Distribution is uniform.
*
* \return A random fixed point number from [0,1].
*/
#ifndef DEBUGRANDOM
fixed_t P_RandomFixed(pr_class_t pr_class)
@ -118,14 +187,14 @@ fixed_t P_RandomFixedD(const char *rfile, INT32 rline, pr_class_t pr_class)
{
CONS_Printf("P_RandomFixed(%u) at: %sp %d\n", pr_class, rfile, rline);
#endif
return __internal_prng__(pr_class);
return (fixed_t)(__internal_prng_bound__(pr_class, FRACUNIT));
}
/** Provides a random byte. Distribution is uniform.
* If you're curious, (&0xFF00) >> 8 gives the same result
* as a fixed point multiplication by 256.
*
* \return Random integer from [0, 255].
* \return Random integer from [0,255].
* \sa __internal_prng__
*/
#ifndef DEBUGRANDOM
@ -136,7 +205,7 @@ UINT8 P_RandomByteD(const char *rfile, INT32 rline, pr_class_t pr_class)
{
CONS_Printf("P_RandomByte(%u) at: %sp %d\n", pr_class, rfile, rline);
#endif
return (UINT8)((__internal_prng__(pr_class) & 0xFF00) >> 8);
return (UINT8)(__internal_prng_bound__(pr_class, UINT8_MAX));
}
/** Provides a random integer for picking random elements from an array.
@ -144,23 +213,22 @@ UINT8 P_RandomByteD(const char *rfile, INT32 rline, pr_class_t pr_class)
* NOTE: Maximum range is 65536.
*
* \param a Number of items in array.
* \return A random integer from [0,a).
* \return A random integer from [0,a].
* \sa __internal_prng__
*/
#ifndef DEBUGRANDOM
INT32 P_RandomKey(pr_class_t pr_class, INT32 a)
UINT32 P_RandomKey(pr_class_t pr_class, UINT32 a)
{
#else
INT32 P_RandomKeyD(const char *rfile, INT32 rline, pr_class_t pr_class, INT32 a)
UINT32 P_RandomKeyD(const char *rfile, INT32 rline, pr_class_t pr_class, UINT32 a)
{
CONS_Printf("P_RandomKey(%u) at: %sp %d\n", pr_class, rfile, rline);
#endif
return (INT32)(((INT64)__internal_prng__(pr_class) * a) >> FRACBITS);
return __internal_prng_bound__(pr_class, a);
}
/** Provides a random integer in a given range.
* Distribution is uniform.
* NOTE: Maximum range is 65536.
*
* \param a Lower bound.
* \param b Upper bound.
@ -175,7 +243,7 @@ INT32 P_RandomRangeD(const char *rfile, INT32 rline, pr_class_t pr_class, INT32
{
CONS_Printf("P_RandomRange(%u) at: %sp %d\n", pr_class, rfile, rline);
#endif
return (INT32)(((INT64)__internal_prng__(pr_class) * (b - a + 1)) >> FRACBITS) + a;
return (INT32)(__internal_prng_bound__(pr_class, (b - a) + 1)) + a;
}
@ -187,13 +255,13 @@ INT32 P_RandomRangeD(const char *rfile, INT32 rline, pr_class_t pr_class, INT32
/** Peeks to see what the next result from the PRNG will be.
* Used for debugging.
*
* \return A 'random' fixed point number from [0,1).
* \return A 'random' number from [0,UINT32_MAX]
* \sa __internal_prng__
*/
fixed_t P_RandomPeek(pr_class_t pr_class)
UINT32 P_RandomPeek(pr_class_t pr_class)
{
UINT32 r = rng.seed[pr_class];
fixed_t ret = __internal_prng__(pr_class);
UINT32 ret = __internal_prng__(pr_class);
rng.seed[pr_class] = r;
return ret;
}

View file

@ -70,37 +70,41 @@ typedef enum
// P_Random functions pulls random bytes from a PRNG that is network synced.
// RNG functions
UINT32 M_Random(void);
fixed_t M_RandomFixed(void);
UINT8 M_RandomByte(void);
INT32 M_RandomKey(INT32 a);
UINT32 M_RandomKey(UINT32 a);
INT32 M_RandomRange(INT32 a, INT32 b);
// PRNG functions
#ifdef DEBUGRANDOM
#define P_Random(c) P_RandomD(__FILE__, __LINE__, c)
#define P_RandomFixed(c) P_RandomFixedD(__FILE__, __LINE__, c)
#define P_RandomByte(c) P_RandomByteD(__FILE__, __LINE__, c)
#define P_RandomKey(c, d) P_RandomKeyD(__FILE__, __LINE__, c, d)
#define P_RandomRange(c, d, e) P_RandomRangeD(__FILE__, __LINE__, c, d, e)
UINT32 P_RandomD(const char *rfile, INT32 rline, pr_class_t pr_class);
fixed_t P_RandomFixedD(const char *rfile, INT32 rline, pr_class_t pr_class);
UINT8 P_RandomByteD(const char *rfile, INT32 rline, pr_class_t pr_class);
INT32 P_RandomKeyD(const char *rfile, INT32 rline, pr_class_t pr_class, INT32 a);
UINT32 P_RandomKeyD(const char *rfile, INT32 rline, pr_class_t pr_class, UINT32 a);
INT32 P_RandomRangeD(const char *rfile, INT32 rline, pr_class_t pr_class, INT32 a, INT32 b);
#else
UINT32 P_Random(pr_class_t pr_class);
fixed_t P_RandomFixed(pr_class_t pr_class);
UINT8 P_RandomByte(pr_class_t pr_class);
INT32 P_RandomKey(pr_class_t pr_class, INT32 a);
UINT32 P_RandomKey(pr_class_t pr_class, UINT32 a);
INT32 P_RandomRange(pr_class_t pr_class, INT32 a, INT32 b);
#endif
// Macros for other functions
#define M_SignedRandom() ((INT32)M_RandomByte() - 128) // [-128, 127] signed byte, originally a
#define P_SignedRandom(pr) ((INT32)P_RandomByte(pr) - 128) // function of its own, moved to a macro
#define M_SignedRandom() ((INT32)M_RandomByte() + INT8_MIN) // [-128, 127] signed byte, originally a
#define P_SignedRandom(pr) ((INT32)P_RandomByte(pr) + INT8_MIN) // function of its own, moved to a macro
#define M_RandomChance(p) (M_RandomFixed() < p) // given fixed point probability, p, between 0 (0%)
#define P_RandomChance(pr, p) (P_RandomFixed(pr) < p) // and FRACUNIT (100%), returns true p% of the time
// Debugging
fixed_t P_RandomPeek(pr_class_t pr_class);
UINT32 P_RandomPeek(pr_class_t pr_class);
// Working with the seed for PRNG
#ifdef DEBUGRANDOM

View file

@ -217,7 +217,7 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
S_StartSound(t2, t2->info->deathsound);
P_KillMobj(t2, t1, t1, DMG_NORMAL);
P_SetObjectMomZ(t2, 8*FRACUNIT, false);
P_SetObjectMomZ(t2, 24*FRACUNIT, false);
P_InstaThrust(t2, bounceangle, 16*FRACUNIT);
P_SpawnMobj(t2->x/2 + t1->x/2, t2->y/2 + t1->y/2, t2->z/2 + t1->z/2, MT_ITEMCLASH);
@ -254,7 +254,7 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
S_StartSound(t1, t1->info->deathsound);
P_KillMobj(t1, t2, t2, DMG_NORMAL);
P_SetObjectMomZ(t1, 8*FRACUNIT, false);
P_SetObjectMomZ(t1, 24*FRACUNIT, false);
P_InstaThrust(t1, bounceangle, 16*FRACUNIT);
}

View file

@ -1585,6 +1585,32 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
target->fuse = 1;
break;
case MT_BANANA:
case MT_BANANA_SHIELD:
{
const UINT8 numParticles = 8;
const angle_t diff = ANGLE_MAX / numParticles;
UINT8 i;
for (i = 0; i < numParticles; i++)
{
mobj_t *spark = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_BANANA_SPARK);
spark->angle = (diff * i) - (diff / 2);
if (inflictor != NULL && P_MobjWasRemoved(inflictor) == false)
{
spark->angle += K_MomentumAngle(inflictor);
spark->momx += inflictor->momx / 2;
spark->momy += inflictor->momy / 2;
spark->momz += inflictor->momz / 2;
}
P_SetObjectMomZ(spark, (12 + P_RandomRange(PR_DECORATION, -4, 4)) * FRACUNIT, true);
P_Thrust(spark, spark->angle, (12 + P_RandomRange(PR_DECORATION, -4, 4)) * spark->scale);
}
break;
}
default:
break;
}
@ -1997,9 +2023,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
// Check if the player is allowed to be damaged!
// If not, then spawn the instashield effect instead.
// NB: "allowcombo", "hardhit" and related checks are here to disallow HITLAG COMBOS, not loss-of-control combos
// DMG_EXPLODE bypasses this check to prevent blocking eggbox/SPB with spinout flashtics
if (!force && (type != DMG_EXPLODE))
if (!force)
{
if (gametyperules & GTR_BUMPERS)
{
@ -2039,7 +2063,8 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
allowcombo = false;
}
if ((target->hitlag == 0 || allowcombo == false) && player->flashing > 0)
// DMG_EXPLODE excluded from flashtic checks to prevent dodging eggbox/SPB with weak spinout
if ((target->hitlag == 0 || allowcombo == false) && player->flashing > 0 && type != DMG_EXPLODE)
{
// Post-hit invincibility
K_DoInstashield(player);
@ -2257,6 +2282,9 @@ static void P_FlingBurst
mo->fuse = objFuse;
P_SetTarget(&mo->target, player->mo);
// We want everything from P_SpawnMobjFromMobj except scale.
objScale = FixedMul(objScale, FixedDiv(mapobjectscale, player->mo->scale));
if (objScale != FRACUNIT)
{
P_SetScale(mo, FixedMul(objScale, mo->scale));

View file

@ -2618,7 +2618,7 @@ increment_move
if (!(thing->flags & MF_NOCLIP))
{
//All things are affected by their scale.
fixed_t maxstep = P_GetThingStepUp(thing, x, y);
fixed_t maxstep = P_GetThingStepUp(thing, tryx, tryy);
if (tmceilingz - tmfloorz < thing->height)
{

View file

@ -1158,66 +1158,68 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
gravityadd = -gravityadd;
}
}
else //Otherwise, sort through the other exceptions.
// Sort through the other exceptions.
switch (mo->type)
{
switch (mo->type)
{
case MT_FLINGRING:
case MT_FLINGCOIN:
case MT_FLINGBLUESPHERE:
case MT_FLINGNIGHTSCHIP:
case MT_BOUNCERING:
case MT_RAILRING:
case MT_INFINITYRING:
case MT_AUTOMATICRING:
case MT_EXPLOSIONRING:
case MT_SCATTERRING:
case MT_GRENADERING:
case MT_BOUNCEPICKUP:
case MT_RAILPICKUP:
case MT_AUTOPICKUP:
case MT_EXPLODEPICKUP:
case MT_SCATTERPICKUP:
case MT_GRENADEPICKUP:
case MT_REDFLAG:
case MT_BLUEFLAG:
if (mo->target)
case MT_FLINGRING:
case MT_FLINGCOIN:
case MT_FLINGBLUESPHERE:
case MT_FLINGNIGHTSCHIP:
case MT_BOUNCERING:
case MT_RAILRING:
case MT_INFINITYRING:
case MT_AUTOMATICRING:
case MT_EXPLOSIONRING:
case MT_SCATTERRING:
case MT_GRENADERING:
case MT_BOUNCEPICKUP:
case MT_RAILPICKUP:
case MT_AUTOPICKUP:
case MT_EXPLODEPICKUP:
case MT_SCATTERPICKUP:
case MT_GRENADEPICKUP:
case MT_REDFLAG:
case MT_BLUEFLAG:
if (mo->target)
{
// Flung items copy the gravity of their tosser.
if ((mo->target->eflags & MFE_VERTICALFLIP) && !(mo->eflags & MFE_VERTICALFLIP))
{
// Flung items copy the gravity of their tosser.
if ((mo->target->eflags & MFE_VERTICALFLIP) && !(mo->eflags & MFE_VERTICALFLIP))
{
gravityadd = -gravityadd;
mo->eflags |= MFE_VERTICALFLIP;
}
gravityadd = -gravityadd;
mo->eflags |= MFE_VERTICALFLIP;
}
break;
case MT_WATERDROP:
case MT_BATTLEBUMPER:
gravityadd /= 2;
break;
case MT_BANANA:
case MT_EGGMANITEM:
case MT_SSMINE:
case MT_LANDMINE:
case MT_DROPTARGET:
case MT_SINK:
case MT_EMERALD:
}
break;
case MT_WATERDROP:
case MT_BATTLEBUMPER:
gravityadd /= 2;
break;
case MT_BANANA:
case MT_EGGMANITEM:
case MT_SSMINE:
case MT_LANDMINE:
case MT_DROPTARGET:
case MT_SINK:
case MT_EMERALD:
if (mo->health > 0)
{
if (mo->extravalue2 > 0)
{
gravityadd *= mo->extravalue2;
}
gravityadd = (5*gravityadd)/2;
break;
case MT_KARMAFIREWORK:
gravityadd /= 3;
break;
case MT_ITEM_DEBRIS:
gravityadd *= 6;
break;
default:
break;
}
}
break;
case MT_KARMAFIREWORK:
gravityadd /= 3;
break;
case MT_ITEM_DEBRIS:
gravityadd *= 6;
break;
default:
break;
}
}
@ -1261,6 +1263,11 @@ void P_CheckGravity(mobj_t *mo, boolean affect)
//
void P_SetPitchRollFromSlope(mobj_t *mo, pslope_t *slope)
{
if (!(mo->flags & MF_SLOPE))
{
return;
}
if (slope)
{
fixed_t tempz = slope->normal.z;
@ -1764,7 +1771,7 @@ void P_XYMovement(mobj_t *mo)
S_StartSound(mo, mo->info->deathsound);
P_KillMobj(mo, NULL, NULL, DMG_NORMAL);
P_SetObjectMomZ(mo, 8*FRACUNIT, false);
P_SetObjectMomZ(mo, 24*FRACUNIT, false);
P_InstaThrust(mo, R_PointToAngle2(mo->x, mo->y, mo->x + xmove, mo->y + ymove)+ANGLE_90, 16*FRACUNIT);
}
break;
@ -3119,16 +3126,14 @@ boolean P_CanRunOnWater(mobj_t *mobj, ffloor_t *rover)
fixed_t surfaceheight = INT32_MAX;
fixed_t surfDiff = INT32_MAX;
fixed_t floorheight = INT32_MAX;
fixed_t floorDiff = INT32_MAX;
fixed_t mobjbottom = INT32_MAX;
fixed_t maxStep = INT32_MAX;
boolean doifit = false;
pslope_t *waterSlope = NULL;
angle_t ourZAng = 0;
angle_t waterZAng = 0;
angle_t moveDir = 0;
fixed_t ourZAng = 0;
fixed_t waterZAng = 0;
if (rover == NULL)
{
@ -3162,20 +3167,36 @@ boolean P_CanRunOnWater(mobj_t *mobj, ffloor_t *rover)
return false;
}
if (mobj->standingslope != NULL)
moveDir = K_MomentumAngle(mobj);
if (mobj->standingslope != NULL && mobj->standingslope->zangle != 0)
{
ourZAng = mobj->standingslope->zangle;
angle_t dir = mobj->standingslope->xydirection;
angle_t workang = mobj->standingslope->zangle;
if (workang >= ANGLE_180)
{
workang = InvAngle(workang);
dir = InvAngle(dir);
}
ourZAng = P_ReturnThrustX(mobj, dir - moveDir, AngleFixed(workang));
}
waterSlope = (flip ? *rover->b_slope : *rover->t_slope);
if (waterSlope != NULL)
if (waterSlope != NULL && waterSlope->zangle != 0)
{
waterZAng = waterSlope->zangle;
angle_t dir = waterSlope->xydirection;
angle_t workang = waterSlope->zangle;
if (workang >= ANGLE_180)
{
workang = InvAngle(workang);
dir = InvAngle(dir);
}
waterZAng = P_ReturnThrustX(mobj, dir - moveDir, AngleFixed(workang));
}
if (ourZAng != waterZAng)
if (abs(ourZAng - waterZAng) > 11*FRACUNIT)
{
// The surface slopes are different.
// The surface slopes are too different.
return false;
}
@ -3193,16 +3214,20 @@ boolean P_CanRunOnWater(mobj_t *mobj, ffloor_t *rover)
maxStep = P_GetThingStepUp(mobj, mobj->x, mobj->y);
surfDiff = flip ? (surfaceheight - mobjbottom) : (mobjbottom - surfaceheight);
// We start water run IF we can step onto it!
if (surfDiff <= maxStep && surfDiff >= 0)
{
// We start water run IF we can step-down!
floorheight = flip ? P_GetSectorCeilingZAt(mobj->subsector->sector, mobj->x, mobj->y) : P_GetSectorFloorZAt(mobj->subsector->sector, mobj->x, mobj->y);
floorDiff = flip ? (floorheight - mobjbottom) : (mobjbottom - floorheight);
if (floorDiff <= maxStep && floorDiff >= 0)
if (ourZAng < 0)
{
// ... but NOT if real floor is in range.
// FIXME: Count solid FOFs in this check
return false;
fixed_t floorheight = flip ? P_GetSectorCeilingZAt(mobj->subsector->sector, mobj->x, mobj->y) : P_GetSectorFloorZAt(mobj->subsector->sector, mobj->x, mobj->y);
fixed_t floorDiff = flip ? (floorheight - mobjbottom) : (mobjbottom - floorheight);
if (floorDiff <= maxStep && floorDiff >= -maxStep)
{
// ... but NOT if going down and real floor is in range.
// FIXME: Count solid FOFs in this check
return false;
}
}
return true;
@ -5257,27 +5282,8 @@ void P_RunOverlays(void)
continue;
}
if (!r_splitscreen /*&& rendermode != render_soft*/)
{
angle_t viewingangle;
if (players[displayplayers[0]].awayviewtics)
viewingangle = R_PointToAngle2(mo->target->x, mo->target->y, players[displayplayers[0]].awayviewmobj->x, players[displayplayers[0]].awayviewmobj->y);
else if (!camera[0].chase && players[displayplayers[0]].mo)
viewingangle = R_PointToAngle2(mo->target->x, mo->target->y, players[displayplayers[0]].mo->x, players[displayplayers[0]].mo->y);
else
viewingangle = R_PointToAngle2(mo->target->x, mo->target->y, camera[0].x, camera[0].y);
if (!(mo->state->frame & FF_ANIMATE) && mo->state->var1)
viewingangle += ANGLE_180;
destx = mo->target->x + P_ReturnThrustX(mo->target, viewingangle, FixedMul(FRACUNIT/4, mo->scale));
desty = mo->target->y + P_ReturnThrustY(mo->target, viewingangle, FixedMul(FRACUNIT/4, mo->scale));
}
else
{
destx = mo->target->x;
desty = mo->target->y;
}
destx = mo->target->x;
desty = mo->target->y;
mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP) | (mo->target->eflags & MFE_VERTICALFLIP);
mo->scale = mo->destscale = FixedMul(mo->target->scale, mo->movefactor);
@ -5289,12 +5295,27 @@ void P_RunOverlays(void)
if ((mo->flags & MF_DONTENCOREMAP) != (mo->target->flags & MF_DONTENCOREMAP))
mo->flags ^= MF_DONTENCOREMAP;
mo->dispoffset = mo->target->dispoffset + mo->info->dispoffset;
if (!(mo->state->frame & FF_ANIMATE))
{
zoffs = FixedMul(((signed)mo->state->var2)*FRACUNIT, mo->scale);
// if you're using FF_ANIMATE on an overlay,
// then you're on your own.
if (mo->state->var1)
{
mo->dispoffset--;
}
else
{
mo->dispoffset++;
}
}
else
{
// if you're using FF_ANIMATE on an overlay,
// then you're on your own.
zoffs = 0;
}
P_UnsetThingPosition(mo);
mo->x = destx;
@ -6490,8 +6511,26 @@ static boolean P_MobjDeadThink(mobj_t *mobj)
P_SetObjectMomZ(mobj, -2*FRACUNIT/3, true);
}
break;
case MT_ORBINAUT:
case MT_BANANA:
{
angle_t spin = FixedMul(FixedDiv(abs(mobj->momz), 8 * mobj->scale), ANGLE_67h);
mobj->angle -= spin;
mobj->rollangle += spin;
if (P_IsObjectOnGround(mobj) && mobj->momz * P_MobjFlip(mobj) <= 0)
{
P_RemoveMobj(mobj);
return false;
}
}
break;
case MT_BANANA_SPARK:
{
angle_t spin = FixedMul(FixedDiv(abs(mobj->momz), 8 * mobj->scale), ANGLE_22h);
mobj->rollangle += spin;
}
break;
case MT_ORBINAUT:
case MT_EGGMANITEM:
case MT_LANDMINE:
//case MT_DROPTARGET:
@ -6786,6 +6825,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
break;
case MT_FLOATINGITEM:
{
mobj->pitch = mobj->roll = 0;
if (mobj->flags & MF_NOCLIPTHING)
{
if (P_CheckDeathPitCollide(mobj))
@ -6930,16 +6970,61 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
}
}
if (P_IsObjectOnGround(mobj) && mobj->health > 1)
if (P_IsObjectOnGround(mobj))
{
S_StartSound(mobj, mobj->info->activesound);
mobj->momx = mobj->momy = 0;
mobj->health = 1;
//mobj->rollangle = 0;
if (mobj->health > 1)
{
S_StartSound(mobj, mobj->info->activesound);
mobj->momx = mobj->momy = 0;
mobj->health = 1;
}
}
else
{
// tilt n tumble
angle_t spin = FixedMul(FixedDiv(abs(mobj->momz), 8 * mobj->scale), ANGLE_67h);
mobj->angle += spin;
mobj->rollangle -= spin;
}
if (mobj->threshold > 0)
mobj->threshold--;
break;
case MT_BANANA_SPARK:
{
if (leveltime & 1)
{
mobj->spritexscale = mobj->spriteyscale = FRACUNIT;
}
else
{
if ((leveltime / 2) & 1)
{
mobj->spriteyscale = 3*FRACUNIT/2;
}
else
{
mobj->spritexscale = 3*FRACUNIT/2;
}
}
if (P_IsObjectOnGround(mobj) == true && mobj->momz * P_MobjFlip(mobj) <= 0)
{
P_SetObjectMomZ(mobj, 8*FRACUNIT, false);
if (mobj->health > 0)
{
mobj->tics = 1;
mobj->destscale = 0;
mobj->spritexscale = mobj->spriteyscale = FRACUNIT;
mobj->health = 0;
}
}
break;
}
case MT_SPB:
{
Obj_SPBThink(mobj);
@ -9579,7 +9664,7 @@ void P_MobjThinker(mobj_t *mobj)
S_StartSound(mobj, mobj->info->deathsound);
P_KillMobj(mobj, NULL, NULL, DMG_NORMAL);
P_SetObjectMomZ(mobj, 8*FRACUNIT, false);
P_SetObjectMomZ(mobj, 24*FRACUNIT, false);
P_InstaThrust(mobj, R_PointToAngle2(0, 0, mobj->momx, mobj->momy) + ANGLE_90, 16*FRACUNIT);
}
@ -9806,24 +9891,6 @@ void P_PushableThinker(mobj_t *mobj)
// Quick, optimized function for scenery
void P_SceneryThinker(mobj_t *mobj)
{
if (mobj->flags & MF_BOXICON)
{
if (!(mobj->eflags & MFE_VERTICALFLIP))
{
if (mobj->z < mobj->floorz + FixedMul(mobj->info->damage, mobj->scale))
mobj->momz = FixedMul(mobj->info->speed, mobj->scale);
else
mobj->momz = 0;
}
else
{
if (mobj->z + FixedMul(mobj->info->height, mobj->scale) > mobj->ceilingz - FixedMul(mobj->info->damage, mobj->scale))
mobj->momz = -FixedMul(mobj->info->speed, mobj->scale);
else
mobj->momz = 0;
}
}
// momentum movement
if (mobj->momx || mobj->momy)
{
@ -10872,21 +10939,41 @@ void P_RemovePrecipMobj(precipmobj_t *mobj)
void P_RemoveSavegameMobj(mobj_t *mobj)
{
// unlink from sector and block lists
P_UnsetThingPosition(mobj);
// Remove touching_sectorlist from mobj.
if (sector_list)
if (((thinker_t *)mobj)->function.acp1 == (actionf_p1)P_NullPrecipThinker)
{
P_DelSeclist(sector_list);
sector_list = NULL;
P_UnsetPrecipThingPosition((precipmobj_t *)mobj);
if (precipsector_list)
{
P_DelPrecipSeclist(precipsector_list);
precipsector_list = NULL;
}
}
else
{
// unlink from sector and block lists
P_UnsetThingPosition(mobj);
// Remove touching_sectorlist from mobj.
if (sector_list)
{
P_DelSeclist(sector_list);
sector_list = NULL;
}
}
// stop any playing sound
S_StopSound(mobj);
R_RemoveMobjInterpolator(mobj);
// free block
P_RemoveThinker((thinker_t *)mobj);
R_RemoveMobjInterpolator(mobj);
// Here we use the same code as R_RemoveThinkerDelayed, but without reference counting (we're removing everything so it shouldn't matter) and without touching currentthinker since we aren't in P_RunThinkers
{
thinker_t *thinker = (thinker_t *)mobj;
thinker_t *next = thinker->next;
(next->prev = thinker->prev)->next = next;
Z_Free(thinker);
}
}
static CV_PossibleValue_t respawnitemtime_cons_t[] = {{1, "MIN"}, {300, "MAX"}, {0, NULL}};
@ -13767,6 +13854,8 @@ mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zo
if (!newmobj)
return NULL;
newmobj->hitlag = mobj->hitlag;
newmobj->destscale = P_ScaleFromMap(mobj->destscale, newmobj->destscale);
P_SetScale(newmobj, P_ScaleFromMap(mobj->scale, newmobj->scale));

View file

@ -126,8 +126,8 @@ typedef enum
MF_NOCLIP = 1<<12,
// Allow moves to any height, no gravity. For active floaters.
MF_FLOAT = 1<<13,
// Monitor powerup icon. These rise a bit.
MF_BOXICON = 1<<14,
// Change pitch/roll when touching slopes.
MF_SLOPE = 1<<14,
// Don't hit same species, explode on block.
// Player missiles as well as fireballs of various kinds.
MF_MISSILE = 1<<15,
@ -163,7 +163,7 @@ typedef enum
MF_NOSQUISH = 1<<30,
// Disable hitlag for this object
MF_NOHITLAGFORME = (INT32)(1U<<31),
// no more free slots, next up I suppose we can get rid of shit like MF_BOXICON?
// no more free slots, gotta get rid of more crusty base SRB2 flags
} mobjflag_t;
typedef enum

View file

@ -3345,6 +3345,19 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
mobj->player->viewz = mobj->player->mo->z + mobj->player->viewheight;
}
if (mobj->type == MT_SKYBOX)
{
mtag_t tag = mobj->movedir;
if (tag < 0 || tag > 15)
{
CONS_Debug(DBG_GAMELOGIC, "LoadMobjThinker: Skybox ID %d of netloaded object is not between 0 and 15!\n", tag);
}
else if (mobj->flags2 & MF2_AMBUSH)
skyboxcenterpnts[tag] = mobj;
else
skyboxviewpnts[tag] = mobj;
}
if (diff2 & MD2_WAYPOINTCAP)
P_SetTarget(&waypointcap, mobj);
@ -3972,10 +3985,14 @@ static void P_NetUnArchiveThinkers(void)
{
next = currentthinker->next;
if (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker)
if (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker || currentthinker->function.acp1 == (actionf_p1)P_NullPrecipThinker)
P_RemoveSavegameMobj((mobj_t *)currentthinker); // item isn't saved, don't remove it
else
{
(next->prev = currentthinker->prev)->next = next;
R_DestroyLevelInterpolators(currentthinker);
Z_Free(currentthinker);
}
}
}

View file

@ -170,6 +170,13 @@ mapthing_t *playerstarts[MAXPLAYERS];
mapthing_t *bluectfstarts[MAXPLAYERS];
mapthing_t *redctfstarts[MAXPLAYERS];
// Global state for PartialAddWadFile/MultiSetupWadFiles
// Might be replacable with parameters, but non-trivial when the functions are called on separate tics
static SINT8 partadd_stage = -1;
static boolean partadd_important = false;
UINT16 partadd_earliestfile = UINT16_MAX;
// Maintain *ZOOM TUBE* waypoints
// Renamed because SRB2Kart owns real waypoints.
mobj_t *tubewaypoints[NUMTUBEWAYPOINTSEQUENCES][TUBEWAYPOINTSEQUENCESIZE];
@ -2624,6 +2631,7 @@ static void P_ProcessLinedefsAfterSidedefs(void)
{
size_t i = numlines;
register line_t *ld = lines;
const boolean subtractTripwire = ((mapheaderinfo[gamemap - 1]->levelflags & LF_SUBTRACTNUM) == LF_SUBTRACTNUM);
for (; i--; ld++)
{
@ -2638,7 +2646,7 @@ static void P_ProcessLinedefsAfterSidedefs(void)
if (ld->tripwire)
{
ld->blendmode = AST_ADD;
ld->blendmode = (subtractTripwire ? AST_SUBTRACT : AST_ADD);
ld->alpha = 0xff;
}
@ -7350,7 +7358,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
// Reset the palette now all fades have been done
if (rendermode != render_none)
V_SetPaletteLump(GetPalette()); // Set the level palette
V_ReloadPalette(); // Set the level palette
if (!(reloadinggamestate || titlemapinaction))
{
@ -7745,7 +7753,7 @@ lumpnum_t wadnamelump = LUMPERROR;
INT16 wadnamemap = 0; // gamemap based
// Initialising map data (and catching replacements)...
UINT8 P_InitMapData(INT32 numexistingmapheaders)
UINT8 P_InitMapData(boolean existingmapheaders)
{
UINT8 ret = 0;
INT32 i;
@ -7759,20 +7767,9 @@ UINT8 P_InitMapData(INT32 numexistingmapheaders)
name = mapheaderinfo[i]->lumpname;
maplump = W_CheckNumForMap(name);
// Doesn't exist?
if (maplump == INT16_MAX)
{
#ifndef DEVELOP
if (!numexistingmapheaders)
{
I_Error("P_InitMapData: Base map %s has a header but no level\n", name);
}
#endif
continue;
}
// Always check for cup cache reassociations.
// (The core assumption is that cups < headers.)
if (maplump != LUMPERROR || mapheaderinfo[i]->lumpnum != LUMPERROR)
{
cupheader_t *cup = kartcupheaders;
INT32 j;
@ -7807,6 +7804,18 @@ UINT8 P_InitMapData(INT32 numexistingmapheaders)
}
}
// Doesn't exist in this set of files?
if (maplump == LUMPERROR)
{
#ifndef DEVELOP
if (!existingmapheaders)
{
I_Error("P_InitMapData: Base map %s has a header but no level\n", name);
}
#endif
continue;
}
// No change?
if (mapheaderinfo[i]->lumpnum == maplump)
continue;
@ -7816,7 +7825,7 @@ UINT8 P_InitMapData(INT32 numexistingmapheaders)
ret |= MAPRET_ADDED;
CONS_Printf("%s\n", name);
if (numexistingmapheaders && mapheaderinfo[i]->lumpnum != LUMPERROR)
if (existingmapheaders && mapheaderinfo[i]->lumpnum != LUMPERROR)
{
G_SetGameModified(multiplayer, true); // oops, double-defined - no record attack privileges for you
@ -7866,23 +7875,32 @@ UINT8 P_InitMapData(INT32 numexistingmapheaders)
return ret;
}
UINT16 p_adding_file = INT16_MAX;
//
// Add a wadfile to the active wad files,
// replace sounds, musics, patches, textures, sprites and maps
//
boolean P_AddWadFile(const char *wadfilename)
{
UINT16 wadnum;
if ((wadnum = P_PartialAddWadFile(wadfilename)) == UINT16_MAX)
return false;
P_MultiSetupWadFiles(true);
return true;
}
//
// Add a WAD file and do the per-WAD setup stages.
// Call P_MultiSetupWadFiles as soon as possible after any number of these.
//
UINT16 P_PartialAddWadFile(const char *wadfilename)
{
size_t i, j, sreplaces = 0, mreplaces = 0, digmreplaces = 0;
INT32 numexistingmapheaders = nummapheaders;
UINT16 numlumps, wadnum;
char *name;
lumpinfo_t *lumpinfo;
//boolean texturechange = false; ///\todo Useless; broken when back-frontporting PK3 changes?
UINT8 mapsadded = 0;
// Vars to help us with the position start and amount of each resource type.
// Useful for PK3s since they use folders.
// WADs use markers for some resources, but others such as sounds are checked lump-by-lump anyway.
@ -7902,10 +7920,19 @@ boolean P_AddWadFile(const char *wadfilename)
refreshdirmenu |= REFRESHDIR_NOTLOADED;
return false;
}
else
wadnum = (UINT16)(numwadfiles-1);
p_adding_file = wadnum;
wadnum = (UINT16)(numwadfiles-1);
// Init partadd.
if (wadfiles[wadnum]->important)
{
partadd_important = true;
}
if (partadd_stage != 0)
{
partadd_earliestfile = wadnum;
}
partadd_stage = 0;
switch(wadfiles[wadnum]->type)
{
@ -7939,8 +7966,6 @@ boolean P_AddWadFile(const char *wadfilename)
// R_LoadSpritsRange(wadnum, sprPos, sprNum);
// if (texNum) // Textures. TODO: R_LoadTextures() does the folder positioning once again. New function maybe?
// R_LoadTextures();
// if (mapNum) // Maps. TODO: Actually implement the map WAD loading code, lulz.
// P_LoadWadMapRange(wadnum, mapPos, mapNum);
break;
default:
lumpinfo = wadfiles[wadnum]->lumpinfo;
@ -8005,23 +8030,14 @@ boolean P_AddWadFile(const char *wadfilename)
// TEXTURES/etc. list.
R_LoadTexturesPwad(wadnum); // numtexture changes
// Reload ANIMDEFS
P_InitPicAnims();
// Reload BRIGHT
K_InitBrightmapsPwad(wadnum);
// Flush and reload HUD graphics
//ST_UnloadGraphics();
HU_LoadGraphics();
ST_LoadGraphics();
//
// look for skins
//
R_AddSkins(wadnum); // faB: wadfile index in wadfiles[]
R_PatchSkins(wadnum); // toast: PATCH PATCH
ST_ReloadSkinFaceGraphics();
//
// edit music defs
@ -8029,37 +8045,99 @@ boolean P_AddWadFile(const char *wadfilename)
S_LoadMusicDefs(wadnum);
//
// search for maps
// extra sprite/skin data
//
mapsadded = P_InitMapData(numexistingmapheaders);
if (!mapsadded)
CONS_Printf(M_GetText("No maps added\n"));
R_LoadSpriteInfoLumps(wadnum, numlumps);
#ifdef HWRENDER
HWR_ReloadModels();
#endif
// For anything that has to be done over every wadfile at once, see P_MultiSetupWadFiles.
// reload status bar (warning should have valid player!)
if (gamestate == GS_LEVEL)
ST_Start();
// Prevent savefile cheating
if (cursaveslot > 0)
cursaveslot = 0;
if ((mapsadded & MAPRET_CURRENTREPLACED) && gamestate == GS_LEVEL && (netgame || multiplayer))
{
CONS_Printf(M_GetText("Current map %d replaced by added file, ending the level to ensure consistency.\n"), gamemap);
if (server)
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
refreshdirmenu &= ~REFRESHDIR_GAMEDATA; // Under usual circumstances we'd wait for REFRESHDIR_GAMEDATA to disappear the next frame, but it's a bit too dangerous for that...
p_adding_file = INT16_MAX;
refreshdirmenu &= ~REFRESHDIR_GAMEDATA; // Under usual circumstances we'd wait for REFRESHDIR_ flags to disappear the next frame, but this one's a bit too dangerous for that...
return true;
}
// Only exists to make sure there's no way to overwrite partadd_stage externally
// unless you really push yourself.
SINT8 P_PartialAddGetStage(void)
{
return partadd_stage;
}
//
// Set up a series of partially added WAD files.
// Setup functions that iterate over every loaded WAD go here.
// If fullsetup false, only do one stage per call.
//
boolean P_MultiSetupWadFiles(boolean fullsetup)
{
if (partadd_stage < 0)
I_Error(M_GetText("P_MultiSetupWadFiles: Post-load addon setup attempted without loading any addons first"));
if (partadd_stage == 0)
{
// Flush and reload HUD graphics
//ST_UnloadGraphics();
HU_LoadGraphics();
ST_LoadGraphics();
ST_ReloadSkinFaceGraphics();
if (!partadd_important)
partadd_stage = -1; // everything done
else if (fullsetup)
partadd_stage++;
}
if (partadd_stage == 1)
{
// Prevent savefile cheating
if (cursaveslot >= 0)
cursaveslot = 0;
// Reload ANIMATED / ANIMDEFS
P_InitPicAnims();
// reload status bar (warning should have valid player!)
if (gamestate == GS_LEVEL)
ST_Start();
#ifdef HWRENDER
HWR_ReloadModels();
#endif
if (fullsetup)
partadd_stage++;
}
if (partadd_stage == 2)
{
UINT8 mapsadded = P_InitMapData(true);
if (!mapsadded)
CONS_Printf(M_GetText("No maps added\n"));
if ((mapsadded & MAPRET_CURRENTREPLACED)
&& (gamestate == GS_LEVEL)
&& (netgame || multiplayer))
{
CONS_Printf(M_GetText("Current map %d replaced by added file, ending the level to ensure consistency.\n"), gamemap);
if (server)
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
//if (fullsetup)
//partadd_stage++;
partadd_stage = -1;
}
I_Assert(!fullsetup || partadd_stage < 0);
if (partadd_stage < 0)
{
partadd_important = false;
partadd_earliestfile = UINT16_MAX;
return true;
}
partadd_stage++;
return false;
}

View file

@ -96,8 +96,6 @@ INT32 P_CheckLevelFlat(const char *flatname);
extern size_t nummapthings;
extern mapthing_t *mapthings;
extern UINT16 p_adding_file;
void P_SetupLevelSky(const char *skytexname, boolean global);
void P_RespawnThings(void);
boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate);
@ -108,11 +106,29 @@ boolean P_AddWadFile(const char *wadfilename);
#define MAPRET_ADDED (1)
#define MAPRET_CURRENTREPLACED (1<<1)
UINT8 P_InitMapData(INT32 numexistingmapheaders);
UINT8 P_InitMapData(boolean existingmapheaders);
extern lumpnum_t wadnamelump;
extern INT16 wadnamemap;
#define WADNAMECHECK(name) (!strncmp(name, "WADNAME", 7))
// WARNING: The following functions should be grouped as follows:
// any amount of PartialAdds followed by MultiSetups until returned true,
// as soon as possible.
UINT16 P_PartialAddWadFile(const char *wadfilename);
// Run a single stage of multisetup, or all of them if fullsetup set.
// fullsetup true: run everything
// otherwise multiple stages
// returns true if setup finished on this call, false otherwise (always true on fullsetup)
// throws I_Error if called without any partial adds started as a safeguard
boolean P_MultiSetupWadFiles(boolean fullsetup);
// Get the current setup stage.
// if negative, no PartialAdds done since last MultiSetup
// if 0, partial adds done but MultiSetup not called yet
// if positive, setup's partway done
SINT8 P_PartialAddGetStage(void);
extern UINT16 partadd_earliestfile;
boolean P_RunSOC(const char *socfilename);
void P_LoadSoundsRange(UINT16 wadnum, UINT16 first, UINT16 num);
void P_LoadMusicsRange(UINT16 wadnum, UINT16 first, UINT16 num);

View file

@ -148,7 +148,7 @@ static void GrowAnimDefs(void)
// A prototype; here instead of p_spec.h, so they're "private"
void P_ParseANIMDEFSLump(INT32 wadNum, UINT16 lumpnum);
void P_ParseAnimationDefintion(SINT8 istexture);
void P_ParseAnimationDefintion(void);
/** Sets up texture and flat animations.
*
@ -161,8 +161,6 @@ void P_ParseAnimationDefintion(SINT8 istexture);
* \author Steven McGranahan (original), Shadow Hog (had to rewrite it to handle multiple WADs), JTE (had to rewrite it to handle multiple WADs _correctly_)
*/
static boolean animdeftempflats = false; // only until ANIMDEFS flats are removed
void P_InitPicAnims(void)
{
// Init animation
@ -182,7 +180,6 @@ void P_InitPicAnims(void)
while (animdefsLumpNum != INT16_MAX)
{
animdeftempflats = ((p_adding_file == INT16_MAX) || p_adding_file == w);
P_ParseANIMDEFSLump(w, animdefsLumpNum);
animdefsLumpNum = W_CheckNumForNamePwad("ANIMDEFS", (UINT16)w, animdefsLumpNum + 1);
}
@ -204,31 +201,14 @@ void P_InitPicAnims(void)
lastanim = anims;
for (i = 0; animdefs[i].istexture != -1; i++)
{
if (animdefs[i].istexture == 1)
{
if (R_CheckTextureNumForName(animdefs[i].startname) == -1)
continue;
lastanim->picnum = R_TextureNumForName(animdefs[i].endname);
lastanim->basepic = R_TextureNumForName(animdefs[i].startname);
}
else
{
if (animdefs[i].istexture == 2)
{
CONS_Alert(CONS_WARNING, "ANIMDEFS flats are disabled; flat support in general will be removed soon! (%s, %s)\n", animdefs[i].startname, animdefs[i].endname);
}
if (animdefs[i].istexture != 1)
continue;
}
#if 0
{
if ((W_CheckNumForName(animdefs[i].startname)) == LUMPERROR)
continue;
lastanim->picnum = R_GetFlatNumForName(animdefs[i].endname);
lastanim->basepic = R_GetFlatNumForName(animdefs[i].startname);
}
#endif
if (R_CheckTextureNumForName(animdefs[i].startname) == -1)
continue;
lastanim->picnum = R_TextureNumForName(animdefs[i].endname);
lastanim->basepic = R_TextureNumForName(animdefs[i].startname);
lastanim->istexture = animdefs[i].istexture;
lastanim->numpics = lastanim->picnum - lastanim->basepic + 1;
@ -285,21 +265,20 @@ void P_ParseANIMDEFSLump(INT32 wadNum, UINT16 lumpnum)
if (stricmp(animdefsToken, "TEXTURE") == 0)
{
Z_Free(animdefsToken);
P_ParseAnimationDefintion(1);
P_ParseAnimationDefintion();
}
else if (stricmp(animdefsToken, "FLAT") == 0)
{
Z_Free(animdefsToken);
P_ParseAnimationDefintion(0);
I_Error("Error parsing ANIMDEFS lump: FLats are no longer supported by Ring Racers");
}
else if (stricmp(animdefsToken, "OSCILLATE") == 0)
{
// This probably came off the tail of an earlier definition. It's technically legal syntax, but we don't support it.
I_Error("Error parsing ANIMDEFS lump: Animation definitions utilizing \"OSCILLATE\" (the animation plays in reverse when it reaches the end) are not supported by SRB2");
I_Error("Error parsing ANIMDEFS lump: Animation definitions utilizing \"OSCILLATE\" (the animation plays in reverse when it reaches the end) are not supported by Ring Racers");
}
else
{
I_Error("Error parsing ANIMDEFS lump: Expected \"TEXTURE\" or \"FLAT\", got \"%s\"",animdefsToken);
I_Error("Error parsing ANIMDEFS lump: Expected \"TEXTURE\", got \"%s\"",animdefsToken);
}
// parse next line
while (*p != '\0' && *p != '\n') ++p;
@ -310,7 +289,7 @@ void P_ParseANIMDEFSLump(INT32 wadNum, UINT16 lumpnum)
Z_Free((void *)animdefsText);
}
void P_ParseAnimationDefintion(SINT8 istexture)
void P_ParseAnimationDefintion(void)
{
char *animdefsToken;
size_t animdefsTokenLength;
@ -353,8 +332,7 @@ void P_ParseAnimationDefintion(SINT8 istexture)
// Search for existing animdef
for (i = 0; i < maxanims; i++)
if (animdefs[i].istexture == istexture // Check if it's the same type!
&& stricmp(animdefsToken, animdefs[i].startname) == 0)
if (stricmp(animdefsToken, animdefs[i].startname) == 0)
{
//CONS_Alert(CONS_NOTICE, "Duplicate animation: %s\n", animdefsToken);
@ -376,10 +354,7 @@ void P_ParseAnimationDefintion(SINT8 istexture)
Z_Free(animdefsToken);
// set texture type
if (istexture)
animdefs[i].istexture = 1;
else
animdefs[i].istexture = (animdeftempflats ? 2 : 0);
animdefs[i].istexture = 1;
// "RANGE"
animdefsToken = M_GetToken(NULL);
@ -457,16 +432,6 @@ void P_ParseAnimationDefintion(SINT8 istexture)
}
animdefs[i].speed = animSpeed;
Z_Free(animdefsToken);
#ifdef WALLFLATS
// hehe... uhh.....
if (!istexture)
{
GrowAnimDefs();
M_Memcpy(&animdefs[maxanims-1], &animdefs[i], sizeof(animdef_t));
animdefs[maxanims-1].istexture = 1;
}
#endif
}
/** Checks for flats in levelflats that are part of a flat animation sequence
@ -2391,6 +2356,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
camera[i].y += y;
camera[i].z += z;
camera[i].subsector = R_PointInSubsector(camera[i].x, camera[i].y);
R_RelativeTeleportViewInterpolation(i, x, y, z, 0);
break;
}
}

View file

@ -17,6 +17,7 @@
#include "r_state.h"
#include "s_sound.h"
#include "r_main.h"
#include "r_fps.h"
/** \brief The P_MixUp function
@ -73,8 +74,12 @@ void P_MixUp(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle,
// move chasecam at new player location
for (i = 0; i <= r_splitscreen; i++)
{
if (thing->player == &players[displayplayers[i]] && camera[i].chase)
if (thing->player != &players[displayplayers[i]])
continue;
if (camera[i].chase)
P_ResetCamera(thing->player, &camera[i]);
R_ResetViewInterpolation(i + 1);
break;
}
// don't run in place after a teleport

View file

@ -24,6 +24,7 @@
#include "lua_hook.h"
#include "m_perfstats.h"
#include "i_system.h" // I_GetPreciseTime
#include "i_video.h" // rendermode
#include "r_main.h"
#include "r_fps.h"
@ -787,15 +788,20 @@ void P_Ticker(boolean run)
// Hack: ensure newview is assigned every tic.
// Ensures view interpolation is T-1 to T in poor network conditions
// We need a better way to assign view state decoupled from game logic
for (i = 0; i <= r_splitscreen; i++)
if (rendermode != render_none)
{
player_t *player = &players[displayplayers[i]];
const boolean skybox = (player->skybox.viewpoint && cv_skybox.value); // True if there's a skybox object and skyboxes are on
if (skybox)
for (i = 0; i <= r_splitscreen; i++)
{
R_SkyboxFrame(i);
player_t *player = &players[displayplayers[i]];
if (!player->mo)
continue;
const boolean skybox = (player->skybox.viewpoint && cv_skybox.value); // True if there's a skybox object and skyboxes are on
if (skybox)
{
R_SkyboxFrame(i);
}
R_SetupFrame(i);
}
R_SetupFrame(i);
}
}

View file

@ -216,6 +216,14 @@ void R_ResetViewInterpolation(UINT8 p)
}
}
void R_RelativeTeleportViewInterpolation(UINT8 p, fixed_t xdiff, fixed_t ydiff, fixed_t zdiff, angle_t angdiff)
{
pview_old[p].x += xdiff;
pview_old[p].y += ydiff;
pview_old[p].z += zdiff;
pview_old[p].angle += angdiff;
}
void R_SetViewContext(enum viewcontext_e _viewcontext)
{
UINT8 i = 0;

View file

@ -126,6 +126,8 @@ void R_InterpolateViewRollAngle(fixed_t frac);
void R_UpdateViewInterpolation(void);
// Reset the view states (e.g. after level load) so R_InterpolateView doesn't interpolate invalid data
void R_ResetViewInterpolation(UINT8 p);
// Update old view for seamless relative teleport
void R_RelativeTeleportViewInterpolation(UINT8 p, fixed_t xdiff, fixed_t ydiff, fixed_t zdiff, angle_t angdiff);
// Set the current view context (the viewvars pointed to by newview)
void R_SetViewContext(enum viewcontext_e _viewcontext);

View file

@ -106,10 +106,9 @@ rendermode_t chosenrendermode = render_none; // set by command line arguments
boolean highcolor = false;
static void Impl_SetVsync(void);
// synchronize page flipping with screen refresh
consvar_t cv_vidwait = CVAR_INIT ("vid_wait", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, Impl_SetVsync);
consvar_t cv_vidwait = CVAR_INIT ("vid_wait", "On", CV_SAVE, CV_OnOff, NULL);
static consvar_t cv_stretch = CVAR_INIT ("stretch", "Off", CV_SAVE|CV_NOSHOWHELP, CV_OnOff, NULL);
static consvar_t cv_alwaysgrabmouse = CVAR_INIT ("alwaysgrabmouse", "Off", CV_SAVE, CV_OnOff, NULL);
@ -185,6 +184,20 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen);
//static void Impl_SetWindowName(const char *title);
static void Impl_SetWindowIcon(void);
static void Impl_SetSoftwareVsync(int vsync)
{
#if SDL_VERSION_ATLEAST(2,0,18)
static int oldvsync = 0;
if (oldvsync != vsync)
{
SDL_RenderSetVSync(renderer, vsync);
}
oldvsync = vsync;
#else
(void)vsync;
#endif
}
static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen, SDL_bool reposition)
{
static SDL_bool wasfullscreen = SDL_FALSE;
@ -277,6 +290,7 @@ static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen, SDL_bool
}
SDL_PixelFormatEnumToMasks(sw_texture_format, &bpp, &rmask, &gmask, &bmask, &amask);
vidSurface = SDL_CreateRGBSurface(0, width, height, bpp, rmask, gmask, bmask, amask);
Impl_SetSoftwareVsync(cv_vidwait.value);
}
}
@ -1254,6 +1268,7 @@ void I_FinishUpdate(void)
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, &src_rect, NULL);
SDL_RenderPresent(renderer);
Impl_SetSoftwareVsync(cv_vidwait.value);
}
#ifdef HWRENDER
else if (rendermode == render_opengl)
@ -1478,15 +1493,6 @@ static SDL_bool Impl_CreateContext(void)
int flags = 0; // Use this to set SDL_RENDERER_* flags now
if (usesdl2soft)
flags |= SDL_RENDERER_SOFTWARE;
#if 0
// This shit is BROKEN.
// - The version of SDL we're using cannot toggle VSync at runtime. We'll need a new SDL version implemented to have this work properly.
// - cv_vidwait is initialized before config is loaded, so it's forced to default value at runtime, and forced off when switching. The config loading code would need restructured.
// - With both this & frame interpolation on, I_FinishUpdate takes x10 longer. At this point, it is simpler to use a standard FPS cap.
// So you can probably guess why I'm kinda over this, I'm just disabling it.
else if (cv_vidwait.value)
flags |= SDL_RENDERER_PRESENTVSYNC;
#endif
// 3 August 2022
// Possibly a Windows 11 issue; the default
@ -2006,11 +2012,3 @@ UINT32 I_GetRefreshRate(void)
// trouble querying mode over and over again.
return refresh_rate;
}
static void Impl_SetVsync(void)
{
#if SDL_VERSION_ATLEAST(2,0,18)
if (renderer)
SDL_RenderSetVSync(renderer, cv_vidwait.value);
#endif
}

View file

@ -1089,10 +1089,10 @@ sfxinfo_t S_sfx[NUMSFX] =
{"bhurry", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // v1.0.2 Battle overtime
{"bsnipe", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Banana sniping
{"sploss", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Down to yellow sparks
{"join", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Player joined server
{"leave", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Player left server
{"requst", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Got a Discord join request
{"syfail", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Funny sync failure
{"join", true, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Player joined server
{"leave", true, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Player left server
{"requst", true, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Got a Discord join request
{"syfail", true, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Funny sync failure
{"itfree", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // :shitsfree:
{"dbgsal", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Debug notification
{"cock", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Hammer cocks, bang bang
@ -1353,7 +1353,7 @@ sfxenum_t S_AddSoundFx(const char *name, boolean singular, INT32 flags, boolean
return i;
}
CONS_Alert(CONS_WARNING, M_GetText("No more free sound slots\n"));
I_Error("Out of Sound Freeslots while allocating \"%s\"\nLoad less addons to fix this.", name);
return 0;
}

View file

@ -411,12 +411,10 @@ static void ST_drawDebugInfo(void)
// Figure out some other way to display all of the RNG classes.
fixed_t peekres = P_RandomPeek(PR_UNDEFINED);
peekres *= 10000; // Change from fixed point
peekres >>= FRACBITS; // to displayable decimal
V_DrawRightAlignedString(320, height - 16, V_MONOSPACE, va("Init: %08x", P_GetInitSeed(PR_UNDEFINED)));
V_DrawRightAlignedString(320, height - 8, V_MONOSPACE, va("Seed: %08x", P_GetRandSeed(PR_UNDEFINED)));
V_DrawRightAlignedString(320, height, V_MONOSPACE, va("== : .%04d", peekres));
V_DrawRightAlignedString(320, height, V_MONOSPACE, va("== : %08x", peekres));
height -= 32;
}

View file

@ -86,11 +86,16 @@ static CV_PossibleValue_t constextsize_cons_t[] = {
static void CV_constextsize_OnChange(void);
consvar_t cv_constextsize = CVAR_INIT ("con_textsize", "Medium", CV_SAVE|CV_CALL, constextsize_cons_t, CV_constextsize_OnChange);
consvar_t cv_palette = CVAR_INIT ("palette", "", CV_CHEAT|CV_CALL|CV_NOINIT, NULL, CV_palette_OnChange);
consvar_t cv_palettenum = CVAR_INIT ("palettenum", "0", CV_CHEAT|CV_CALL|CV_NOINIT, CV_Unsigned, CV_palette_OnChange);
// local copy of the palette for V_GetColor()
RGBA_t *pLocalPalette = NULL;
RGBA_t *pMasterPalette = NULL;
RGBA_t *pGammaCorrectedPalette = NULL;
static size_t currentPaletteSize;
/*
The following was an extremely helpful resource when developing my Colour Cube LUT.
http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter24.html
@ -311,9 +316,12 @@ UINT32 V_GammaCorrect(UINT32 input, double power)
static void LoadPalette(const char *lumpname)
{
lumpnum_t lumpnum = W_GetNumForName(lumpname);
size_t i, palsize = W_LumpLength(lumpnum)/3;
size_t i, palsize;
UINT8 *pal;
currentPaletteSize = W_LumpLength(lumpnum);
palsize = currentPaletteSize / 3;
Cubeapply = InitCube();
if (pLocalPalette != pMasterPalette)
@ -400,8 +408,24 @@ const char *R_GetPalname(UINT16 num)
const char *GetPalette(void)
{
const char *user = cv_palette.string;
if (user && user[0])
{
if (W_CheckNumForName(user) == LUMPERROR)
{
CONS_Alert(CONS_WARNING,
"cv_palette %s lump does not exist\n", user);
}
else
{
return cv_palette.string;
}
}
if (gamestate == GS_LEVEL)
return R_GetPalname((encoremode ? mapheaderinfo[gamemap-1]->encorepal : mapheaderinfo[gamemap-1]->palette));
return "PLAYPAL";
}
@ -419,6 +443,19 @@ void V_SetPalette(INT32 palettenum)
if (!pLocalPalette)
V_ReloadPalette();
if (palettenum == 0)
{
palettenum = cv_palettenum.value;
if (palettenum * 256U > currentPaletteSize - 256)
{
CONS_Alert(CONS_WARNING,
"cv_palettenum %d out of range\n",
palettenum);
palettenum = 0;
}
}
#ifdef HWRENDER
if (rendermode == render_opengl)
HWR_SetPalette(&pLocalPalette[palettenum*256]);
@ -433,23 +470,12 @@ void V_SetPalette(INT32 palettenum)
void V_SetPaletteLump(const char *pal)
{
LoadPalette(pal);
#ifdef HWRENDER
if (rendermode == render_opengl)
HWR_SetPalette(pLocalPalette);
#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)
else
#endif
#endif
if (rendermode != render_none)
I_SetPalette(pLocalPalette);
#ifdef HASINVERT
R_MakeInvertmap();
#endif
V_SetPalette(0);
}
static void CV_palette_OnChange(void)
{
// reload palette
// recalculate Color Cube
V_ReloadPalette();
V_SetPalette(0);
}

View file

@ -34,7 +34,8 @@ extern consvar_t cv_ticrate, cv_constextsize,
cv_globalgamma, cv_globalsaturation,
cv_rhue, cv_yhue, cv_ghue, cv_chue, cv_bhue, cv_mhue,
cv_rgamma, cv_ygamma, cv_ggamma, cv_cgamma, cv_bgamma, cv_mgamma,
cv_rsaturation, cv_ysaturation, cv_gsaturation, cv_csaturation, cv_bsaturation, cv_msaturation;
cv_rsaturation, cv_ysaturation, cv_gsaturation, cv_csaturation, cv_bsaturation, cv_msaturation,
cv_palette, cv_palettenum;
// Allocates buffer screens, call before R_Init.
void V_Init(void);

View file

@ -1324,6 +1324,7 @@ lumpnum_t W_CheckNumForMap(const char *name)
lumpnum_t check = INT16_MAX;
UINT32 uhash, hash = quickncasehash(name, LUMPNUMCACHENAME);
INT32 i;
UINT16 firstfile = (partadd_earliestfile == UINT16_MAX) ? 0 : partadd_earliestfile;
// Check the lumpnumcache first. Loop backwards so that we check
// most recent entries first
@ -1339,7 +1340,7 @@ lumpnum_t W_CheckNumForMap(const char *name)
uhash = quickncasehash(name, 8); // Not a mistake, legacy system for short lumpnames
for (i = numwadfiles - 1; i >= 0; i--)
for (i = numwadfiles - 1; i >= firstfile; i--)
{
check = W_CheckNumForMapPwad(name, uhash, (UINT16)i, 0);

View file

@ -99,9 +99,10 @@ void* vres_GetPatch(virtlump_t *vlump, INT32 tag);
// =========================================================================
#define MAX_WADPATH 512
#define MAX_WADFILES 127 // maximum of wad files used at the same time
// Replay code relies on it being an UINT8 and, just to be safe, in case some wad counter somewhere is a SINT8, you should NOT go above 127 here if you're lazy like me.
// Besides, are there truly 127 wads worth your interrest?
#define MAX_WADFILES 255 // maximum of wad files used at the same time
// Replay code relies on it being an UINT8. There are no SINT8s handling WAD indices, though.
// Can be set all the way up to 255 but not 256,
// because an UINT8 will never be >= 256, probably breaking some conditionals.
#define lumpcache_t void *