Merge branch 'master' into new-menus

This commit is contained in:
James 2019-10-19 19:56:34 -04:00
commit d306df81c8
29 changed files with 943 additions and 544 deletions

View file

@ -493,6 +493,7 @@ OBJS:=$(i_main_o) \
$(OBJDIR)/y_inter.o \
$(OBJDIR)/st_stuff.o \
$(OBJDIR)/k_kart.o \
$(OBJDIR)/k_pwrlv.o \
$(OBJDIR)/m_aatree.o \
$(OBJDIR)/m_anigif.o \
$(OBJDIR)/m_argv.o \

View file

@ -70,8 +70,12 @@ CV_PossibleValue_t CV_Natural[] = {{1, "MIN"}, {999999999, "MAX"}, {0, NULL}};
//SRB2kart
CV_PossibleValue_t kartspeed_cons_t[] = {
{0, "Easy"}, {1, "Normal"}, {2, "Hard"},
{0, NULL}};
{-1, "Auto"},
{0, "Easy"},
{1, "Normal"},
{2, "Hard"},
{0, NULL}
};
// Filter consvars by EXECVERSION
// First implementation is 2 (1.0.2), so earlier configs default at 1 (1.0.0)
@ -1734,7 +1738,7 @@ void CV_AddValue(consvar_t *var, INT32 increment)
{
newvalue = var->value + 1;
if (newvalue > maxspeed)
newvalue = 0;
newvalue = -1;
var->value = newvalue;
var->string = var->PossibleValue[var->value].strvalue;
var->func();
@ -1743,7 +1747,7 @@ void CV_AddValue(consvar_t *var, INT32 increment)
else if (increment < 0) // Going down!
{
newvalue = var->value - 1;
if (newvalue < 0)
if (newvalue < -1)
newvalue = maxspeed;
var->value = newvalue;
var->string = var->PossibleValue[var->value].strvalue;

View file

@ -47,6 +47,7 @@
#include "lua_script.h"
#include "lua_hook.h"
#include "k_kart.h"
#include "k_pwrlv.h"
#ifdef CLIENT_LOADINGSCREEN
// cl loading screen
@ -1350,7 +1351,7 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled();
netbuffer->u.serverinfo.kartvars = (UINT8) (
(cv_kartspeed.value & SV_SPEEDMASK) |
(gamespeed & SV_SPEEDMASK) |
(dedicated ? SV_DEDICATED : 0) |
(D_IsJoinPasswordOn() ? SV_PASSWORD : 0)
);
@ -1420,6 +1421,11 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
p = PutFileNeeded(0);
if (cv_kartusepwrlv.value)
netbuffer->u.serverinfo.avgpwrlv = K_CalculatePowerLevelAvg();
else
netbuffer->u.serverinfo.avgpwrlv = -1;
HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u));
}
@ -1494,7 +1500,7 @@ static void SV_SendPlayerInfo(INT32 node)
*/
static boolean SV_SendServerConfig(INT32 node)
{
INT32 i;
INT32 i, j;
UINT8 *p, *op;
boolean waspacketsent;
@ -1517,10 +1523,13 @@ static boolean SV_SendServerConfig(INT32 node)
memset(netbuffer->u.servercfg.playercolor, 0xFF, sizeof(netbuffer->u.servercfg.playercolor));
memset(netbuffer->u.servercfg.adminplayers, -1, sizeof(netbuffer->u.servercfg.adminplayers));
memset(netbuffer->u.servercfg.powerlevels, 0, sizeof(netbuffer->u.servercfg.powerlevels));
for (i = 0; i < MAXPLAYERS; i++)
{
netbuffer->u.servercfg.adminplayers[i] = (SINT8)adminplayers[i];
for (j = 0; j < PWRLV_NUMTYPES; j++)
netbuffer->u.servercfg.powerlevels[i][j] = clientpowerlevels[i][j];
if (!playeringame[i])
continue;
@ -2306,6 +2315,7 @@ static void CL_ConnectToServer(boolean viams)
wipegamestate = GS_WAITINGPLAYERS;
ClearAdminPlayers();
K_ClearClientPowerLevels();
pnumnodes = 1;
oldtic = I_GetTime() - 1;
#ifndef NONET
@ -3156,6 +3166,21 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
break;
}
// SRB2Kart: kicks count as forfeit
switch (kickreason)
{
case KR_KICK:
case KR_BAN:
case KR_LEAVE:
// Intentional removals should be hit with a true forfeit.
K_PlayerForfeit(pnum, true);
break;
default:
// Otherwise, give remaining players the point compensation, but doesn't penalize who left.
K_PlayerForfeit(pnum, false);
break;
}
if (playernode[pnum] == playernode[consoleplayer])
{
#ifdef DUMPCONSISTENCY
@ -3324,6 +3349,7 @@ void SV_ResetServer(void)
playernode[i] = UINT8_MAX;
sprintf(player_names[i], "Player %d", i + 1);
adminplayers[i] = -1; // Populate the entire adminplayers array with -1.
K_ClearClientPowerLevels();
}
mynode = 0;
@ -3399,6 +3425,7 @@ void D_QuitNetGame(void)
D_CloseConnection();
ClearAdminPlayers();
K_ClearClientPowerLevels();
DEBFILE("===========================================================================\n"
" Log finish\n"
@ -3445,11 +3472,12 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
return;
}
node = READUINT8(*p);
newplayernum = READUINT8(*p);
splitscreenplayer = newplayernum/MAXPLAYERS;
newplayernum %= MAXPLAYERS;
node = (UINT8)READUINT8(*p);
newplayernum = (UINT8)READUINT8(*p);
splitscreenplayer = (UINT8)READUINT8(*p);
CONS_Debug(DBG_NETPLAY, "addplayer: %d %d %d\n", node, newplayernum, splitscreenplayer);
// Clear player before joining, lest some things get set incorrectly
CL_ClearPlayer(newplayernum);
@ -3466,7 +3494,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
if (splitscreenplayer)
{
displayplayers[splitscreenplayer] = newplayernum;
DEBFILE(va("spawning one of my sister number %d\n", splitscreenplayer));
DEBFILE(va("spawning sister # %d\n", splitscreenplayer));
if (splitscreenplayer == 1 && botingame)
players[newplayernum].bot = 1;
}
@ -3477,6 +3505,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
displayplayers[i] = newplayernum;
DEBFILE("spawning me\n");
}
D_SendPlayerConfig();
addedtogame = true;
}
@ -3532,7 +3561,6 @@ static void Got_RemovePlayer(UINT8 **p, INT32 playernum)
static boolean SV_AddWaitingPlayers(void)
{
INT32 node, n, newplayer = false;
XBOXSTATIC UINT8 buf[2];
UINT8 newplayernum = 0;
// What is the reason for this? Why can't newplayernum always be 0?
@ -3545,6 +3573,9 @@ static boolean SV_AddWaitingPlayers(void)
// splitscreen can allow 2+ players in one node
for (; nodewaiting[node] > 0; nodewaiting[node]--)
{
UINT8 buf[3];
UINT8 *buf_p = buf;
newplayer = true;
// search for a free playernum
@ -3552,8 +3583,10 @@ static boolean SV_AddWaitingPlayers(void)
for (; newplayernum < MAXPLAYERS; newplayernum++)
{
for (n = 0; n < MAXNETNODES; n++)
if (nodetoplayer[n] == newplayernum || nodetoplayer2[n] == newplayernum
|| nodetoplayer3[n] == newplayernum || nodetoplayer4[n] == newplayernum)
if (nodetoplayer[n] == newplayernum
|| nodetoplayer2[n] == newplayernum
|| nodetoplayer3[n] == newplayernum
|| nodetoplayer4[n] == newplayernum)
break;
if (n == MAXNETNODES)
break;
@ -3565,28 +3598,23 @@ static boolean SV_AddWaitingPlayers(void)
playernode[newplayernum] = (UINT8)node;
buf[0] = (UINT8)node;
buf[1] = newplayernum;
WRITEUINT8(buf_p, (UINT8)node);
WRITEUINT8(buf_p, newplayernum);
if (playerpernode[node] < 1)
nodetoplayer[node] = newplayernum;
else if (playerpernode[node] < 2)
{
nodetoplayer2[node] = newplayernum;
buf[1] += MAXPLAYERS;
}
else if (playerpernode[node] < 3)
{
nodetoplayer3[node] = newplayernum;
buf[1] += MAXPLAYERS*2;
}
else
{
else if (playerpernode[node] < 4)
nodetoplayer4[node] = newplayernum;
buf[1] += MAXPLAYERS*3;
}
WRITEUINT8(buf_p, playerpernode[node]); // splitscreen num
playerpernode[node]++;
SendNetXCmd(XD_ADDPLAYER, &buf, 2);
SendNetXCmd(XD_ADDPLAYER, buf, buf_p - buf);
DEBFILE(va("Server added player %d node %d\n", newplayernum, node));
// use the next free slot (we can't put playeringame[newplayernum] = true here)
@ -4081,7 +4109,7 @@ static void HandlePacketFromAwayNode(SINT8 node)
case PT_SERVERCFG: // Positive response of client join request
{
INT32 j;
INT32 j, k;
UINT8 *scp;
if (server && serverrunning && node != servernode)
@ -4104,7 +4132,11 @@ static void HandlePacketFromAwayNode(SINT8 node)
I_Error("Bad gametype in cliserv!");
modifiedgame = netbuffer->u.servercfg.modifiedgame;
for (j = 0; j < MAXPLAYERS; j++)
{
adminplayers[j] = netbuffer->u.servercfg.adminplayers[j];
for (k = 0; k < PWRLV_NUMTYPES; k++)
clientpowerlevels[j][k] = netbuffer->u.servercfg.powerlevels[j][k];
}
memcpy(server_context, netbuffer->u.servercfg.server_context, 8);
}

View file

@ -18,6 +18,7 @@
#include "d_netcmd.h"
#include "tables.h"
#include "d_player.h"
#include "k_pwrlv.h" // PWRLV_NUMTYPES
#include "md5.h"
@ -325,6 +326,7 @@ typedef struct
UINT8 gametype;
UINT8 modifiedgame;
SINT8 adminplayers[MAXPLAYERS]; // Needs to be signed
UINT16 powerlevels[MAXPLAYERS][PWRLV_NUMTYPES]; // SRB2kart: player power levels
char server_context[8]; // Unique context id, generated at server startup.
@ -387,6 +389,10 @@ typedef struct
UINT8 actnum;
UINT8 iszone;
UINT8 fileneeded[MAXFILENEEDED]; // is filled with writexxx (byteptr.h)
// Anything beyond this point won't be read by the normal SRB2 Master Server display.
// The MS uses a simple unpack, so the size of the packet above shouldn't be changed, either.
// As long as those two conditions are met, we can add as much information as we want to the end.
INT16 avgpwrlv; // Kart avg power level
} ATTRPACK serverinfo_pak;
typedef struct

View file

@ -1558,7 +1558,7 @@ void D_SRB2Main(void)
else if (!dedicated && M_MapLocked(pstartmap))
I_Error("You need to unlock this level before you can warp to it!\n");
else
D_MapChange(pstartmap, gametype, (boolean)cv_kartencore.value, true, 0, false, false);
D_MapChange(pstartmap, gametype, (cv_kartencore.value == 1), true, 0, false, false);
}
}
else if (M_CheckParm("-skipintro"))

View file

@ -47,6 +47,7 @@
#include "m_cond.h"
#include "m_anigif.h"
#include "k_kart.h" // SRB2kart
#include "k_pwrlv.h"
#include "y_inter.h"
#ifdef NETGAME_DEVMODE
@ -61,6 +62,7 @@
static void Got_NameAndColor(UINT8 **cp, INT32 playernum);
static void Got_WeaponPref(UINT8 **cp, INT32 playernum);
static void Got_PowerLevel(UINT8 **cp, INT32 playernum);
static void Got_Mapcmd(UINT8 **cp, INT32 playernum);
static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum);
static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum);
@ -358,12 +360,13 @@ consvar_t cv_kartminimap = {"kartminimap", "4", CV_SAVE, kartminimap_cons_t, NUL
consvar_t cv_kartcheck = {"kartcheck", "Yes", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t kartinvinsfx_cons_t[] = {{0, "Music"}, {1, "SFX"}, {0, NULL}};
consvar_t cv_kartinvinsfx = {"kartinvinsfx", "SFX", CV_SAVE, kartinvinsfx_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartspeed = {"kartspeed", "Normal", CV_NETVAR|CV_CALL|CV_NOINIT, kartspeed_cons_t, KartSpeed_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartspeed = {"kartspeed", "Auto", CV_NETVAR|CV_CALL|CV_NOINIT, kartspeed_cons_t, KartSpeed_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t kartbumpers_cons_t[] = {{1, "MIN"}, {12, "MAX"}, {0, NULL}};
consvar_t cv_kartbumpers = {"kartbumpers", "3", CV_NETVAR|CV_CHEAT, kartbumpers_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartfrantic = {"kartfrantic", "Off", CV_NETVAR|CV_CHEAT|CV_CALL|CV_NOINIT, CV_OnOff, KartFrantic_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartcomeback = {"kartcomeback", "On", CV_NETVAR|CV_CHEAT|CV_CALL|CV_NOINIT, CV_OnOff, KartComeback_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartencore = {"kartencore", "Off", CV_NETVAR|CV_CALL|CV_NOINIT, CV_OnOff, KartEncore_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t kartencore_cons_t[] = {{-1, "Auto"}, {0, "Off"}, {1, "On"}, {0, NULL}};
consvar_t cv_kartencore = {"kartencore", "Auto", CV_NETVAR|CV_CALL|CV_NOINIT, kartencore_cons_t, KartEncore_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t kartvoterulechanges_cons_t[] = {{0, "Never"}, {1, "Sometimes"}, {2, "Frequent"}, {3, "Always"}, {0, NULL}};
consvar_t cv_kartvoterulechanges = {"kartvoterulechanges", "Frequent", CV_NETVAR, kartvoterulechanges_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t kartspeedometer_cons_t[] = {{0, "Off"}, {1, "Percentage"}, {2, "Kilometers"}, {3, "Miles"}, {4, "Fracunits"}, {0, NULL}};
@ -373,6 +376,8 @@ consvar_t cv_kartvoices = {"kartvoices", "Tasteful", CV_SAVE, kartvoices_cons_t,
consvar_t cv_karteliminatelast = {"karteliminatelast", "Yes", CV_NETVAR|CV_CHEAT|CV_CALL|CV_NOSHOWHELP, CV_YesNo, KartEliminateLast_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartusepwrlv = {"kartusepwrlv", "Yes", CV_NETVAR|CV_CHEAT, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t kartdebugitem_cons_t[] = {{-1, "MIN"}, {NUMKARTITEMS-1, "MAX"}, {0, NULL}};
consvar_t cv_kartdebugitem = {"kartdebugitem", "0", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, kartdebugitem_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t kartdebugamount_cons_t[] = {{1, "MIN"}, {255, "MAX"}, {0, NULL}};
@ -500,6 +505,7 @@ const char *netxcmdnames[MAXNETXCMD - 1] =
"MODIFYVOTE",
"PICKVOTE",
"REMOVEPLAYER",
"POWERLEVEL",
#ifdef HAVE_BLUA
"LUACMD",
"LUAVAR"
@ -531,6 +537,7 @@ void D_RegisterServerCommands(void)
RegisterNetXCmd(XD_NAMEANDCOLOR, Got_NameAndColor);
RegisterNetXCmd(XD_WEAPONPREF, Got_WeaponPref);
RegisterNetXCmd(XD_POWERLEVEL, Got_PowerLevel);
RegisterNetXCmd(XD_MAP, Got_Mapcmd);
RegisterNetXCmd(XD_EXITLEVEL, Got_ExitLevelcmd);
RegisterNetXCmd(XD_ADDFILE, Got_Addfilecmd);
@ -1924,6 +1931,17 @@ static void Got_WeaponPref(UINT8 **cp,INT32 playernum)
players[playernum].pflags |= PF_ANALOGMODE;
}
static void Got_PowerLevel(UINT8 **cp,INT32 playernum)
{
UINT16 race = (UINT16)READUINT16(*cp);
UINT16 battle = (UINT16)READUINT16(*cp);
clientpowerlevels[playernum][PWRLV_RACE] = min(PWRLVRECORD_MAX, race);
clientpowerlevels[playernum][PWRLV_BATTLE] = min(PWRLVRECORD_MAX, battle);
CONS_Debug(DBG_GAMELOGIC, "set player %d to power %d\n", playernum, race);
}
void D_SendPlayerConfig(void)
{
SendNameAndColor();
@ -1940,6 +1958,31 @@ void D_SendPlayerConfig(void)
SendWeaponPref3();
if (splitscreen > 2)
SendWeaponPref4();
{
UINT8 buf[4];
UINT8 *buf_p = buf;
WRITEUINT16(buf_p, vspowerlevel[PWRLV_RACE]);
WRITEUINT16(buf_p, vspowerlevel[PWRLV_BATTLE]);
SendNetXCmd(XD_POWERLEVEL, buf, 4);
}
if (splitscreen)
{
UINT8 buf[4];
UINT8 *buf_p = buf;
WRITEUINT16(buf_p, 0);
WRITEUINT16(buf_p, 0);
SendNetXCmd2(XD_POWERLEVEL, buf, 4);
if (splitscreen > 1)
SendNetXCmd3(XD_POWERLEVEL, buf, 4);
if (splitscreen > 2)
SendNetXCmd4(XD_POWERLEVEL, buf, 4);
}
}
// Only works for displayplayer, sorry!
@ -2352,7 +2395,7 @@ void D_SetupVote(void)
UINT8 secondgt = G_SometimesGetDifferentGametype();
INT16 votebuffer[3] = {-1,-1,-1};
if (cv_kartencore.value && G_RaceGametype())
if ((cv_kartencore.value == 1) && G_RaceGametype())
WRITEUINT8(p, (gametype|0x80));
else
WRITEUINT8(p, gametype);
@ -2523,7 +2566,7 @@ static void Command_Map_f(void)
// new encoremode value
// use cvar by default
newencoremode = (boolean)cv_kartencore.value;
newencoremode = (cv_kartencore.value == 1);
if (COM_CheckParm("-encore"))
{
@ -3650,6 +3693,9 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
if (K_IsPlayerWanted(&players[playernum]))
K_CalculateBattleWanted();
}
K_PlayerForfeit(playernum, true);
players[playernum].health = 1;
if (players[playernum].mo)
players[playernum].mo->health = 1;
@ -5739,7 +5785,7 @@ static void KartSpeed_OnChange(void)
if (!M_SecretUnlocked(SECRET_HARDSPEED) && cv_kartspeed.value == 2)
{
CONS_Printf(M_GetText("You haven't earned this yet.\n"));
CV_StealthSetValue(&cv_kartspeed, 1);
CV_StealthSet(&cv_kartspeed, cv_kartspeed.defaultvalue);
return;
}
@ -5759,10 +5805,10 @@ static void KartEncore_OnChange(void)
{
if (G_RaceGametype())
{
if ((boolean)cv_kartencore.value != encoremode && gamestate == GS_LEVEL /*&& leveltime > starttime*/)
CONS_Printf(M_GetText("Encore Mode will be turned %s next round.\n"), cv_kartencore.value ? M_GetText("on") : M_GetText("off"));
if ((cv_kartencore.value == 1) != encoremode && gamestate == GS_LEVEL /*&& leveltime > starttime*/)
CONS_Printf(M_GetText("Encore Mode will be set to %s next round.\n"), cv_kartencore.string);
else
CONS_Printf(M_GetText("Encore Mode has been turned %s.\n"), cv_kartencore.value ? M_GetText("on") : M_GetText("off"));
CONS_Printf(M_GetText("Encore Mode has been set to %s.\n"), cv_kartencore.string);
}
}

View file

@ -120,8 +120,8 @@ extern consvar_t cv_kartencore;
extern consvar_t cv_kartvoterulechanges;
extern consvar_t cv_kartspeedometer;
extern consvar_t cv_kartvoices;
extern consvar_t cv_karteliminatelast;
extern consvar_t cv_kartusepwrlv;
extern consvar_t cv_votetime;
@ -178,9 +178,10 @@ typedef enum
XD_MODIFYVOTE, // 23
XD_PICKVOTE, // 24
XD_REMOVEPLAYER,// 25
XD_POWERLEVEL, // 26
#ifdef HAVE_BLUA
XD_LUACMD, // 26
XD_LUAVAR, // 27
XD_LUACMD, // 27
XD_LUAVAR, // 28
#endif
MAXNETXCMD
} netxcmd_t;

View file

@ -2663,6 +2663,19 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
ty = UC_PLAYTIME + offset;
re = atoi(params[1]);
}
else if ((offset=0) || fastcmp(params[0], "POWERLEVEL"))
{
PARAMCHECK(2);
ty = UC_POWERLEVEL;
re = atoi(params[1]);
x1 = atoi(params[2]);
if (x1 < 0 || x1 >= PWRLV_NUMTYPES)
{
deh_warning("Power level type %d out of range (0 - %d)", x1, PWRLV_NUMTYPES-1);
return;
}
}
else if ((offset=0) || fastcmp(params[0], "GAMECLEAR")
|| (++offset && fastcmp(params[0], "ALLEMERALDS")))
//|| (++offset && fastcmp(params[0], "ULTIMATECLEAR")))
@ -2715,7 +2728,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
if (x1 < 0 || x1 >= NUMMAPS)
{
deh_warning("Level number %d out of range (1 - %d)", re, NUMMAPS);
deh_warning("Level number %d out of range (1 - %d)", x1, NUMMAPS);
return;
}
}

View file

@ -59,6 +59,8 @@
#include "../z_zone.h"
#include "../g_input.h"
#include "../k_pwrlv.h"
#include "../console.h"
#ifdef __GNUG__
@ -618,6 +620,11 @@ void I_Quit (void)
#ifndef NONET
D_SaveBan(); // save the ban list
#endif
// Make sure you lose points for ALT-F4
if (Playing())
K_PlayerForfeit(consoleplayer, true);
G_SaveGameData(); // Tails 12-08-2002
if (demorecording)
G_CheckDemoStatus();

View file

@ -158,9 +158,6 @@ extern FILE *logstream;
// AND appveyor.yml, for the build bots!
#endif
// Maintain compatibility with 1.0.x record attack replays?
//#define DEMO_COMPAT_100
// Does this version require an added patch file?
// Comment or uncomment this as necessary.
#define USE_PATCH_FILE

View file

@ -496,7 +496,6 @@ extern tic_t wantedcalcdelay;
extern tic_t indirectitemcooldown;
extern tic_t hyubgone;
extern tic_t mapreset;
extern UINT8 nospectategrief;
extern boolean thwompsactive;
extern SINT8 spbplace;

View file

@ -48,6 +48,7 @@
#include "m_cond.h" // condition sets
#include "md5.h" // demo checksums
#include "k_kart.h" // SRB2kart
#include "k_pwrlv.h"
gameaction_t gameaction;
gamestate_t gamestate = GS_NULL;
@ -274,7 +275,6 @@ tic_t wantedcalcdelay; // Time before it recalculates WANTED
tic_t indirectitemcooldown; // Cooldown before any more Shrink, SPB, or any other item that works indirectly is awarded
tic_t hyubgone; // Cooldown before hyudoro is allowed to be rerolled
tic_t mapreset; // Map reset delay when enough players have joined an empty game
UINT8 nospectategrief; // How many players need to be in-game to eliminate last; for preventing spectate griefing
boolean thwompsactive; // Thwomps activate on lap 2
SINT8 spbplace; // SPB exists, give the person behind better items
@ -2346,7 +2346,7 @@ void G_Ticker(boolean run)
G_DoReborn(consoleplayer);*/
D_MapChange(gamemap, gametype, cv_kartencore.value, true, 1, false, false);
D_MapChange(gamemap, gametype, (cv_kartencore.value == 1), true, 1, false, false);
}
for (i = 0; i < MAXPLAYERS; i++)
@ -2378,21 +2378,7 @@ void G_Ticker(boolean run)
if (playeringame[i])
{
//@TODO all this throwdir stuff shouldn't be here! But it stays for now to maintain 1.0.4 compat...
// Remove for 1.1!
// SRB2kart
// Save the dir the player is holding
// to allow items to be thrown forward or backward.
if (cmd->buttons & BT_FORWARD)
players[i].kartstuff[k_throwdir] = 1;
else if (cmd->buttons & BT_BACKWARD)
players[i].kartstuff[k_throwdir] = -1;
else
players[i].kartstuff[k_throwdir] = 0;
G_CopyTiccmd(cmd, &netcmds[buf][i], 1);
// Use the leveltime sent in the player's ticcmd to determine control lag
cmd->latency = modeattacking ? 0 : min(((leveltime & 0xFF) - cmd->latency) & 0xFF, MAXPREDICTTICS-1); //@TODO add a cvar to allow setting this max
}
@ -3056,8 +3042,17 @@ mapthing_t *G_FindRaceStart(INT32 playernum)
continue;
if (j == i)
continue;
if (players[j].score == players[i].score)
num++;
if (netgame && cv_kartusepwrlv.value)
{
if (clientpowerlevels[j][PWRLV_RACE] == clientpowerlevels[i][PWRLV_RACE])
num++;
}
else
{
if (players[j].score == players[i].score)
num++;
}
}
if (num > 1) // found dupes
@ -3065,8 +3060,21 @@ mapthing_t *G_FindRaceStart(INT32 playernum)
}
else
{
if (players[i].score > players[playernum].score || i < playernum)
if (i < playernum)
pos++;
else
{
if (netgame && cv_kartusepwrlv.value)
{
if (clientpowerlevels[i][PWRLV_RACE] > clientpowerlevels[playernum][PWRLV_RACE])
pos++;
}
else
{
if (players[i].score > players[playernum].score)
pos++;
}
}
}
}
@ -3395,9 +3403,10 @@ boolean G_BattleGametype(void)
//
INT16 G_SometimesGetDifferentGametype(void)
{
boolean encorepossible = (M_SecretUnlocked(SECRET_ENCORE) && G_RaceGametype());
boolean encorepossible = ((M_SecretUnlocked(SECRET_ENCORE) || encorescramble == 1) && G_RaceGametype());
if (!cv_kartvoterulechanges.value) // never
if (!cv_kartvoterulechanges.value // never
&& encorescramble != 1) // destroying the code for this one instance
return gametype;
if (randmapbuffer[NUMMAPS] > 0 && (encorepossible || cv_kartvoterulechanges.value != 3))
@ -3405,25 +3414,33 @@ INT16 G_SometimesGetDifferentGametype(void)
randmapbuffer[NUMMAPS]--;
if (encorepossible)
{
switch (cv_kartvoterulechanges.value)
if (encorescramble != -1)
encorepossible = (boolean)encorescramble; // FORCE to what was scrambled on intermission
else
{
case 3: // always
randmapbuffer[NUMMAPS] = 0; // gotta prep this in case it isn't already set
break;
case 2: // frequent
encorepossible = M_RandomChance(FRACUNIT>>1);
break;
case 1: // sometimes
default:
encorepossible = M_RandomChance(FRACUNIT>>2);
break;
switch (cv_kartvoterulechanges.value)
{
case 3: // always
randmapbuffer[NUMMAPS] = 0; // gotta prep this in case it isn't already set
break;
case 2: // frequent
encorepossible = M_RandomChance(FRACUNIT>>1);
break;
case 1: // sometimes
default:
encorepossible = M_RandomChance(FRACUNIT>>2);
break;
}
}
if (encorepossible != (boolean)cv_kartencore.value)
if (encorepossible != (cv_kartencore.value == 1))
return (gametype|0x80);
}
return gametype;
}
if (!cv_kartvoterulechanges.value) // never (again)
return gametype;
switch (cv_kartvoterulechanges.value) // okay, we're having a gametype change! when's the next one, luv?
{
case 3: // always
@ -3893,7 +3910,7 @@ void G_NextLevel(void)
}
forceresetplayers = false;
deferencoremode = (boolean)cv_kartencore.value;
deferencoremode = (cv_kartencore.value == 1);
}
gameaction = ga_worlddone;
@ -4056,9 +4073,13 @@ void G_LoadGameData(void)
// to new gamedata
G_ClearRecords(); // main and nights records
M_ClearSecrets(); // emblems, unlocks, maps visited, etc
totalplaytime = 0; // total play time (separate from all)
matchesplayed = 0; // SRB2Kart: matches played & finished
for (i = 0; i < PWRLV_NUMTYPES; i++) // SRB2Kart: online rank system
vspowerlevel[i] = PWRLVRECORD_START;
if (M_CheckParm("-nodata"))
return; // Don't load.
@ -4089,6 +4110,9 @@ void G_LoadGameData(void)
totalplaytime = READUINT32(save_p);
matchesplayed = READUINT32(save_p);
for (i = 0; i < PWRLV_NUMTYPES; i++)
vspowerlevel[i] = READUINT16(save_p);
modded = READUINT8(save_p);
// Aha! Someone's been screwing with the save file!
@ -4234,6 +4258,9 @@ void G_SaveGameData(boolean force)
WRITEUINT32(save_p, totalplaytime);
WRITEUINT32(save_p, matchesplayed);
for (i = 0; i < PWRLV_NUMTYPES; i++)
WRITEUINT16(save_p, vspowerlevel[i]);
btemp = (UINT8)(savemoddata); // what used to be here was profoundly dunderheaded
WRITEUINT8(save_p, btemp);
@ -4746,7 +4773,7 @@ char *G_BuildMapTitle(INT32 mapnum)
// DEMO RECORDING
//
#define DEMOVERSION 0x0002
#define DEMOVERSION 0x0003
#define DEMOHEADER "\xF0" "KartReplay" "\x0F"
#define DF_GHOST 0x01 // This demo contains ghost data too!
@ -4757,12 +4784,6 @@ char *G_BuildMapTitle(INT32 mapnum)
#define DF_ENCORE 0x40
#define DF_MULTIPLAYER 0x80 // This demo was recorded in multiplayer mode!
#ifdef DEMO_COMPAT_100
#define DF_FILELIST 0x08 // This demo contains an extra files list
#define DF_GAMETYPEMASK 0x30
#define DF_GAMESHIFT 4
#endif
#define DEMO_SPECTATOR 0x40
// For demos
@ -4904,7 +4925,6 @@ void G_ReadDemoExtraData(void)
kartspeed = READUINT8(demo_p);
kartweight = READUINT8(demo_p);
if (stricmp(skins[players[p].skin].name, name) != 0)
FindClosestSkinForStats(p, kartspeed, kartweight);
@ -5622,16 +5642,9 @@ void G_ConsGhostTic(INT32 playernum)
else
ghostext[playernum].desyncframes = 0;
if (
#ifdef DEMO_COMPAT_100
demo.version != 0x0001 &&
#endif
(
players[playernum].kartstuff[k_itemtype] != ghostext[playernum].kartitem ||
players[playernum].kartstuff[k_itemamount] != ghostext[playernum].kartamount ||
players[playernum].kartstuff[k_bumper] != ghostext[playernum].kartbumpers
)
)
if (players[playernum].kartstuff[k_itemtype] != ghostext[playernum].kartitem
|| players[playernum].kartstuff[k_itemamount] != ghostext[playernum].kartamount
|| players[playernum].kartstuff[k_bumper] != ghostext[playernum].kartbumpers)
{
if (demosynced)
CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n"));
@ -5659,10 +5672,6 @@ void G_GhostTicker(void)
// Skip normal demo data.
UINT8 ziptic = READUINT8(g->p);
#ifdef DEMO_COMPAT_100
if (g->version != 0x0001)
{
#endif
while (ziptic != DW_END) // Get rid of extradata stuff
{
if (ziptic == 0) // Only support player 0 info for now
@ -5686,9 +5695,6 @@ void G_GhostTicker(void)
}
ziptic = READUINT8(g->p); // Back to actual ziptic stuff
#ifdef DEMO_COMPAT_100
}
#endif
if (ziptic & ZT_FWD)
g->p++;
@ -5708,18 +5714,12 @@ void G_GhostTicker(void)
// Grab ghost data.
ziptic = READUINT8(g->p);
#ifdef DEMO_COMPAT_100
if (g->version != 0x0001)
{
#endif
if (ziptic == 0xFF)
goto skippedghosttic; // Didn't write ghost info this frame
else if (ziptic != 0)
I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this
ziptic = READUINT8(g->p);
#ifdef DEMO_COMPAT_100
}
#endif
if (ziptic & GZT_XYZ)
{
g->oldmo.x = READFIXED(g->p);
@ -5860,15 +5860,8 @@ void G_GhostTicker(void)
g->p += 12; // kartitem, kartamount, kartbumpers
}
#ifdef DEMO_COMPAT_100
if (g->version != 0x0001)
{
#endif
if (READUINT8(g->p) != 0xFF) // Make sure there isn't other ghost data here.
I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this
#ifdef DEMO_COMPAT_100
}
#endif
skippedghosttic:
// Tick ghost colors (Super and Mario Invincibility flashing)
@ -6378,20 +6371,15 @@ void G_BeginRecording(void)
switch ((demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT)
{
case ATTACKING_NONE: // 0
break;
case ATTACKING_RECORD: // 1
demotime_p = demo_p;
WRITEUINT32(demo_p,UINT32_MAX); // time
WRITEUINT32(demo_p,UINT32_MAX); // lap
break;
/*case ATTACKING_NIGHTS: // 2
demotime_p = demo_p;
WRITEUINT32(demo_p,UINT32_MAX); // time
WRITEUINT32(demo_p,0); // score
break;*/
default: // 3
break;
case ATTACKING_NONE: // 0
break;
case ATTACKING_RECORD: // 1
demotime_p = demo_p;
WRITEUINT32(demo_p,UINT32_MAX); // time
WRITEUINT32(demo_p,UINT32_MAX); // lap
break;
default: // 3
break;
}
WRITEUINT32(demo_p,P_GetInitSeed());
@ -6431,6 +6419,9 @@ void G_BeginRecording(void)
// Score, since Kart uses this to determine where you start on the map
WRITEUINT32(demo_p, player->score);
// Power Levels
WRITEUINT16(demo_p, clientpowerlevels[p][G_BattleGametype() ? PWRLV_BATTLE : PWRLV_RACE]);
// Kart speed and weight
WRITEUINT8(demo_p, skins[player->skin].kartspeed);
WRITEUINT8(demo_p, skins[player->skin].kartweight);
@ -6529,18 +6520,13 @@ void G_SetDemoTime(UINT32 ptime, UINT32 plap)
{
if (!demo.recording || !demotime_p)
return;
if (demoflags & DF_RECORDATTACK)
{
WRITEUINT32(demotime_p, ptime);
WRITEUINT32(demotime_p, plap);
demotime_p = NULL;
}
/*else if (demoflags & DF_NIGHTSATTACK)
{
WRITEUINT32(demotime_p, ptime);
WRITEUINT32(demotime_p, pscore);
demotime_p = NULL;
}*/
}
static void G_LoadDemoExtraFiles(UINT8 **pp)
@ -6783,13 +6769,6 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
case DEMOVERSION: // latest always supported
p += 64; // full demo title
break;
#ifdef DEMO_COMPAT_100
case 0x0001:
// Old replays gotta go :]
CONS_Alert(CONS_NOTICE, M_GetText("File '%s' outdated version. It will be overwritten. Nyeheheh.\n"), oldname);
Z_Free(buffer);
return UINT8_MAX;
#endif
// too old, cannot support.
default:
CONS_Alert(CONS_NOTICE, M_GetText("File '%s' invalid format. It will be overwritten.\n"), oldname);
@ -6881,12 +6860,6 @@ void G_LoadDemoInfo(menudemo_t *pdemo)
info_p += 64;
break;
#ifdef DEMO_COMPAT_100
case 0x0001:
pdemo->type = MD_OUTDATED;
sprintf(pdemo->title, "Legacy Replay");
break;
#endif
// too old, cannot support.
default:
CONS_Alert(CONS_ERROR, M_GetText("%s is an incompatible replay format and cannot be played.\n"), pdemo->filepath);
@ -6921,16 +6894,6 @@ void G_LoadDemoInfo(menudemo_t *pdemo)
Z_Free(infobuffer);
return;
}
#ifdef DEMO_COMPAT_100
else if (pdemoversion == 0x0001)
{
CONS_Alert(CONS_ERROR, M_GetText("%s is a legacy multiplayer replay and cannot be played.\n"), pdemo->filepath);
pdemo->type = MD_INVALID;
sprintf(pdemo->title, "INVALID REPLAY");
Z_Free(infobuffer);
return;
}
#endif
pdemo->gametype = READUINT8(info_p);
@ -7131,10 +7094,6 @@ void G_DoPlayDemo(char *defdemoname)
demo_p += 64;
break;
#ifdef DEMO_COMPAT_100
case 0x0001:
break;
#endif
// too old, cannot support.
default:
snprintf(msg, 1024, M_GetText("%s is an incompatible replay format and cannot be played.\n"), pdemoname);
@ -7163,24 +7122,6 @@ void G_DoPlayDemo(char *defdemoname)
demo_p += 16; // mapmd5
demoflags = READUINT8(demo_p);
#ifdef DEMO_COMPAT_100
if (demo.version == 0x0001)
{
if (demoflags & DF_MULTIPLAYER)
{
snprintf(msg, 1024, M_GetText("%s is an alpha multiplayer replay and cannot be played.\n"), pdemoname);
CONS_Alert(CONS_ERROR, "%s", msg);
M_StartMessage(msg, NULL, MM_NOTHING);
Z_Free(pdemoname);
Z_Free(demobuffer);
demo.playback = false;
demo.title = false;
return;
}
}
else
{
#endif
gametype = READUINT8(demo_p);
if (demo.title) // Titledemos should always play and ought to always be compatible with whatever wadlist is running.
@ -7238,9 +7179,6 @@ void G_DoPlayDemo(char *defdemoname)
return;
}
}
#ifdef DEMO_COMPAT_100
}
#endif
modeattacking = (demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT;
multiplayer = !!(demoflags & DF_MULTIPLAYER);
@ -7268,109 +7206,20 @@ void G_DoPlayDemo(char *defdemoname)
// Random seed
randseed = READUINT32(demo_p);
#ifdef DEMO_COMPAT_100
if (demo.version != 0x0001)
#endif
demo_p += 4; // Extrainfo location
#ifdef DEMO_COMPAT_100
if (demo.version == 0x0001)
// ...*map* not loaded?
if (!gamemap || (gamemap > NUMMAPS) || !mapheaderinfo[gamemap-1] || !(mapheaderinfo[gamemap-1]->menuflags & LF2_EXISTSHACK))
{
// Player name
M_Memcpy(player_names[0],demo_p,16);
demo_p += 16;
// Skin
M_Memcpy(skin,demo_p,16);
demo_p += 16;
// Color
M_Memcpy(color,demo_p,16);
demo_p += 16;
demo_p += 5; // Backwards compat - some stats
// SRB2kart
kartspeed[0] = READUINT8(demo_p);
kartweight[0] = READUINT8(demo_p);
//
demo_p += 9; // Backwards compat - more stats
// Skin not loaded?
if (!SetPlayerSkin(0, skin))
{
snprintf(msg, 1024, M_GetText("%s features a character that is not currently loaded.\n"), pdemoname);
CONS_Alert(CONS_ERROR, "%s", msg);
M_StartMessage(msg, NULL, MM_NOTHING);
Z_Free(pdemoname);
Z_Free(demobuffer);
demo.playback = false;
demo.title = false;
return;
}
// ...*map* not loaded?
if (!gamemap || (gamemap > NUMMAPS) || !mapheaderinfo[gamemap-1] || !(mapheaderinfo[gamemap-1]->menuflags & LF2_EXISTSHACK))
{
snprintf(msg, 1024, M_GetText("%s features a course that is not currently loaded.\n"), pdemoname);
CONS_Alert(CONS_ERROR, "%s", msg);
M_StartMessage(msg, NULL, MM_NOTHING);
Z_Free(pdemoname);
Z_Free(demobuffer);
demo.playback = false;
demo.title = false;
return;
}
// Set color
for (i = 0; i < MAXSKINCOLORS; i++)
if (!stricmp(KartColor_Names[i],color)) // SRB2kart
{
players[0].skincolor = i;
break;
}
// net var data
CV_LoadNetVars(&demo_p);
// Sigh ... it's an empty demo.
if (*demo_p == DEMOMARKER)
{
snprintf(msg, 1024, M_GetText("%s contains no data to be played.\n"), pdemoname);
CONS_Alert(CONS_ERROR, "%s", msg);
M_StartMessage(msg, NULL, MM_NOTHING);
Z_Free(pdemoname);
Z_Free(demobuffer);
demo.playback = false;
demo.title = false;
return;
}
snprintf(msg, 1024, M_GetText("%s features a course that is not currently loaded.\n"), pdemoname);
CONS_Alert(CONS_ERROR, "%s", msg);
M_StartMessage(msg, NULL, MM_NOTHING);
Z_Free(pdemoname);
memset(&oldcmd,0,sizeof(oldcmd));
memset(&oldghost,0,sizeof(oldghost));
memset(&ghostext,0,sizeof(ghostext));
CONS_Alert(CONS_WARNING, M_GetText("Demo version does not match game version. Desyncs may occur.\n"));
// console warning messages
#if defined(SKIPERRORS) && !defined(DEVELOP)
demosynced = (!skiperrors);
#else
demosynced = true;
#endif
// didn't start recording right away.
demo.deferstart = false;
consoleplayer = 0;
memset(displayplayers, 0, sizeof(displayplayers));
memset(playeringame, 0, sizeof(playeringame));
playeringame[0] = true;
goto post_compat;
Z_Free(demobuffer);
demo.playback = false;
demo.title = false;
return;
}
#endif
// net var data
CV_LoadNetVars(&demo_p);
@ -7485,6 +7334,9 @@ void G_DoPlayDemo(char *defdemoname)
// Score, since Kart uses this to determine where you start on the map
players[p].score = READUINT32(demo_p);
// Power Levels
clientpowerlevels[p][G_BattleGametype() ? PWRLV_BATTLE : PWRLV_RACE] = READUINT16(demo_p);
// Kart stats, temporarily
kartspeed[p] = READUINT8(demo_p);
kartweight[p] = READUINT8(demo_p);
@ -7509,10 +7361,6 @@ void G_DoPlayDemo(char *defdemoname)
R_ExecuteSetViewSize();
#ifdef DEMO_COMPAT_100
post_compat:
#endif
P_SetRandSeed(randseed);
G_InitNew(demoflags & DF_ENCORE, G_BuildMapName(gamemap), true, true); // Doesn't matter whether you reset or not here, given changes to resetplayer.
@ -7603,10 +7451,6 @@ void G_AddGhost(char *defdemoname)
case DEMOVERSION: // latest always supported
p += 64; // title
break;
#ifdef DEMO_COMPAT_100
case 0x0001:
break;
#endif
// too old, cannot support.
default:
CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Demo version incompatible.\n"), pdemoname);
@ -7647,15 +7491,9 @@ void G_AddGhost(char *defdemoname)
return;
}
#ifdef DEMO_COMPAT_100
if (ghostversion != 0x0001)
#endif
p++; // gametype
p++; // gametype
G_SkipDemoExtraFiles(&p); // Don't wanna modify the file list for ghosts.
#ifdef DEMO_COMPAT_100
if (ghostversion != 0x0001)
#endif
G_SkipDemoExtraFiles(&p); // Don't wanna modify the file list for ghosts.
switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT)
{
case ATTACKING_NONE: // 0
@ -7671,41 +7509,6 @@ void G_AddGhost(char *defdemoname)
}
p += 4; // random seed
#ifdef DEMO_COMPAT_100
if (ghostversion == 0x0001)
{
// Player name (TODO: Display this somehow if it doesn't match cv_playername!)
M_Memcpy(name, p,16);
p += 16;
// Skin
M_Memcpy(skin, p,16);
p += 16;
// Color
M_Memcpy(color, p,16);
p += 16;
// Ghosts do not have a player structure to put this in.
p++; // charability
p++; // charability2
p++; // actionspd
p++; // mindash
p++; // maxdash
// SRB2kart
p++; // kartspeed
p++; // kartweight
//
p++; // normalspeed
p++; // runspeed
p++; // thrustfactor
p++; // accelstart
p++; // acceleration
p += 4; // jumpfactor
}
else
#endif
p += 4; // Extra data location reference
// net var data
@ -7725,10 +7528,6 @@ void G_AddGhost(char *defdemoname)
return;
}
#ifdef DEMO_COMPAT_100
if (ghostversion != 0x0001)
{
#endif
if (READUINT8(p) != 0)
{
CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot.\n"), pdemoname);
@ -7750,6 +7549,7 @@ void G_AddGhost(char *defdemoname)
p += 16;
p += 4; // score
p += 2; // powerlevel
kartspeed = READUINT8(p);
kartweight = READUINT8(p);
@ -7761,9 +7561,6 @@ void G_AddGhost(char *defdemoname)
Z_Free(buffer);
return;
}
#ifdef DEMO_COMPAT_100
}
#endif
for (i = 0; i < numskins; i++)
if (!stricmp(skins[i].name,skin))
@ -7863,18 +7660,13 @@ void G_UpdateStaffGhostName(lumpnum_t l)
ghostversion = READUINT16(p);
switch(ghostversion)
{
case DEMOVERSION: // latest always supported
p += 64; // full demo title
break;
case DEMOVERSION: // latest always supported
p += 64; // full demo title
break;
#ifdef DEMO_COMPAT_100
case 0x0001:
break;
#endif
// too old, cannot support.
default:
goto fail;
// too old, cannot support.
default:
goto fail;
}
p += 16; // demo checksum
@ -7894,43 +7686,22 @@ void G_UpdateStaffGhostName(lumpnum_t l)
goto fail; // we don't NEED to do it here, but whatever
}
#ifdef DEMO_COMPAT_100
if (ghostversion != 0x0001)
#endif
p++; // Gametype
#ifdef DEMO_COMPAT_100
if (ghostversion != 0x0001)
#endif
G_SkipDemoExtraFiles(&p);
switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT)
{
case ATTACKING_NONE: // 0
break;
case ATTACKING_RECORD: // 1
p += 8; // demo time, lap
break;
/*case ATTACKING_NIGHTS: // 2
p += 8; // demo time left, score
break;*/
default: // 3
break;
case ATTACKING_NONE: // 0
break;
case ATTACKING_RECORD: // 1
p += 8; // demo time, lap
break;
default: // 3
break;
}
p += 4; // random seed
#ifdef DEMO_COMPAT_100
if (ghostversion == 0x0001)
{
// Player name
M_Memcpy(dummystaffname, p,16);
dummystaffname[16] = '\0';
goto fail; // Not really a failure but whatever
}
#endif
p += 4; // Extrainfo location marker
// Ehhhh don't need ghostversion here (?) so I'll reuse the var here
@ -8015,10 +7786,6 @@ void G_DoPlayMetal(void)
{
case DEMOVERSION: // latest always supported
break;
#ifdef DEMO_COMPAT_100
case 0x0001:
I_Error("You need to implement demo compat here, doofus! %s:%d", __FILE__, __LINE__);
#endif
// too old, cannot support.
default:
CONS_Alert(CONS_WARNING, M_GetText("Failed to load bot recording for this map, format version incompatible.\n"));

View file

@ -3049,7 +3049,7 @@ static void HU_DrawRankings(void)
}
V_DrawCenteredString(256, 8, 0, "GAME SPEED");
V_DrawCenteredString(256, 16, hilicol, cv_kartspeed.string);
V_DrawCenteredString(256, 16, hilicol, kartspeed_cons_t[gamespeed].strvalue);
}
// When you play, you quickly see your score because your name is displayed in white.

View file

@ -4,6 +4,8 @@
/// \brief SRB2kart general.
/// All of the SRB2kart-unique stuff.
#include "k_kart.h"
#include "k_pwrlv.h"
#include "doomdef.h"
#include "hu_stuff.h"
#include "g_game.h"
@ -18,7 +20,6 @@
#include "z_zone.h"
#include "m_misc.h"
#include "m_cond.h"
#include "k_kart.h"
#include "f_finale.h"
#include "lua_hud.h" // For Lua hud checks
#include "lua_hook.h" // For MobjDamage and ShouldDamage
@ -31,8 +32,6 @@
// battlewanted is an array of the WANTED player nums, -1 for no player in that slot
// indirectitemcooldown is timer before anyone's allowed another Shrink/SPB
// mapreset is set when enough players fill an empty server
// nospectategrief is the players in-game needed to eliminate the person in last
//{ SRB2kart Color Code
@ -580,6 +579,7 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_kartspeedometer);
CV_RegisterVar(&cv_kartvoices);
CV_RegisterVar(&cv_karteliminatelast);
CV_RegisterVar(&cv_kartusepwrlv);
CV_RegisterVar(&cv_votetime);
CV_RegisterVar(&cv_kartdebugitem);

301
src/k_pwrlv.c Normal file
View file

@ -0,0 +1,301 @@
/// \file k_pwrlv.c
/// \brief SRB2Kart Power Levels
#include "k_pwrlv.h"
#include "d_netcmd.h"
#include "g_game.h"
#include "s_sound.h"
#include "m_random.h"
#include "m_cond.h" // M_UpdateUnlockablesAndExtraEmblems
#include "p_tick.h" // leveltime
// Online rankings for the main gametypes.
// This array is saved to the gamedata.
UINT16 vspowerlevel[PWRLV_NUMTYPES];
// Client-sided calculations done for Power Levels.
// This is done so that clients will never be able to hack someone else's score over the server.
UINT16 clientpowerlevels[MAXPLAYERS][PWRLV_NUMTYPES];
// Which players spec-scummed, and their power level before scumming.
// On race finish, everyone is considered to have "won" against these people.
INT16 nospectategrief[MAXPLAYERS];
// Game setting scrambles based on server Power Level
SINT8 speedscramble = -1;
SINT8 encorescramble = -1;
void K_ClearClientPowerLevels(void)
{
UINT8 i, j;
for (i = 0; i < MAXPLAYERS; i++)
for (j = 0; j < PWRLV_NUMTYPES; j++)
clientpowerlevels[i][j] = 0;
}
// Adapted from this: http://wiki.tockdom.com/wiki/Player_Rating
INT16 K_CalculatePowerLevelInc(INT16 diff)
{
INT16 control[10] = {0,0,0,1,8,50,125,125,125,125};
fixed_t increment = 0;
fixed_t x;
UINT8 j;
#define MAXDIFF (PWRLVRECORD_MAX - 1)
if (diff > MAXDIFF)
diff = MAXDIFF;
if (diff < -MAXDIFF)
diff = -MAXDIFF;
#undef MAXDIFF
x = ((diff-2)<<FRACBITS) / PWRLVRECORD_DEF;
for (j = 3; j < 10; j++) // Just skipping to 3 since 0 thru 2 will always just add 0...
{
fixed_t f = abs(x - ((j-4)<<FRACBITS));
fixed_t add;
if (f >= (2<<FRACBITS))
{
continue; //add = 0;
}
else if (f >= (1<<FRACBITS))
{
fixed_t f2 = (2<<FRACBITS) - f;
add = FixedMul(FixedMul(f2, f2), f2) / 6;
}
else
{
add = ((3*FixedMul(FixedMul(f, f), f)) - (6*FixedMul(f, f)) + (4<<FRACBITS)) / 6;
}
increment += (add * control[j]);
}
return (INT16)(increment >> FRACBITS);
}
INT16 K_CalculatePowerLevelAvg(void)
{
fixed_t avg = 0;
UINT8 div = 0;
SINT8 t = PWRLV_DISABLED;
UINT8 i;
if (!netgame || !cv_kartusepwrlv.value)
return 0; // No average.
if (G_RaceGametype())
t = PWRLV_RACE;
else if (G_BattleGametype())
t = PWRLV_BATTLE;
if (t == PWRLV_DISABLED)
return 0; // Hmm?!
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator
|| clientpowerlevels[i][t] == 0) // splitscreen player
continue;
avg += clientpowerlevels[i][t];
div++;
}
if (!div)
return 0; // No average.
avg /= div;
return (INT16)(avg >> FRACBITS);
}
// -- K_UpdatePowerLevels could not be moved here due to usage of y_data, unfortunately. --
void K_SetPowerLevelScrambles(SINT8 powertype)
{
switch (powertype)
{
case PWRLV_RACE:
if (cv_kartspeed.value == -1 || cv_kartencore.value == -1)
{
UINT8 speed = atoi(cv_kartspeed.defaultvalue);
boolean encore = false;
INT16 avg = 0, min = 0;
UINT8 i, t = 0;
avg = K_CalculatePowerLevelAvg();
for (i = 0; i < MAXPLAYERS; i++)
{
if (min == 0 || clientpowerlevels[i][0] < min)
min = clientpowerlevels[i][0];
}
if (min >= 7800)
{
if (avg >= 8200)
t = 5;
else
t = 4;
}
else if (min >= 6800)
{
if (avg >= 7200)
t = 4;
else
t = 3;
}
else if (min >= 5800)
{
if (avg >= 6200)
t = 3;
else
t = 2;
}
else if (min >= 3800)
{
if (avg >= 4200)
t = 2;
else
t = 1;
}
else if (min >= 1800)
{
if (avg >= 2200)
t = 1;
else
t = 0;
}
else
t = 0;
switch (t)
{
case 5:
speed = 2;
encore = true;
break;
case 4:
speed = M_RandomChance((7<<FRACBITS)/10) ? 2 : 1;
encore = M_RandomChance(FRACUNIT>>1);
break;
case 3:
speed = M_RandomChance((3<<FRACBITS)/10) ? 2 : 1;
encore = M_RandomChance(FRACUNIT>>2);
break;
case 2:
speed = 1;
encore = M_RandomChance(FRACUNIT>>3);
break;
case 1: default:
speed = 1;
encore = false;
break;
case 0:
speed = M_RandomChance((3<<FRACBITS)/10) ? 0 : 1;
encore = false;
break;
}
if (cv_kartspeed.value == -1)
speedscramble = speed;
else
speedscramble = -1;
if (cv_kartencore.value == -1)
encorescramble = (encore ? 1 : 0);
else
encorescramble = -1;
}
break;
default:
break;
}
}
void K_PlayerForfeit(UINT8 playernum, boolean pointloss)
{
UINT8 p = 0;
INT32 powertype = PWRLV_DISABLED;
UINT16 yourpower = PWRLVRECORD_DEF;
UINT16 theirpower = PWRLVRECORD_DEF;
INT16 diff = 0; // Loser PWR.LV - Winner PWR.LV
INT16 inc = 0;
UINT8 i;
// power level & spectating is netgames only
if (!netgame)
return;
// This server isn't using power levels anyway!
if (!cv_kartusepwrlv.value)
return;
// Hey, I just got here!
if (players[playernum].jointime <= 1)
return;
// 20 sec into the match counts as a forfeit -- automatic loss against every other player in the match.
if (gamestate != GS_LEVEL || leveltime <= starttime+(20*TICRATE))
return;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator)
p++;
}
if (p < 2) // no players
return;
if (G_RaceGametype())
powertype = PWRLV_RACE;
else if (G_BattleGametype())
powertype = PWRLV_BATTLE;
if (powertype == PWRLV_DISABLED) // No power type?!
return;
if (clientpowerlevels[playernum][powertype] == 0) // splitscreen guests don't record power level changes
return;
yourpower = clientpowerlevels[playernum][powertype];
// Set up the point compensation.
nospectategrief[playernum] = yourpower;
if (!pointloss) // This is set for stuff like sync-outs, which shouldn't be so harsh on the victim!
return;
for (i = 0; i < MAXPLAYERS; i++)
{
if (i == playernum)
continue;
theirpower = PWRLVRECORD_DEF;
if (clientpowerlevels[i][powertype] != 0) // No power level acts as 5000 (used for splitscreen guests)
theirpower = clientpowerlevels[i][powertype];
diff = yourpower - theirpower;
inc -= K_CalculatePowerLevelInc(diff);
}
if (inc == 0) // No change.
return;
if (yourpower + inc > PWRLVRECORD_MAX) // I mean... we're subtracting... but y'know how it is :V
inc -= ((yourpower + inc) - PWRLVRECORD_MAX);
if (yourpower + inc < PWRLVRECORD_MIN)
inc -= ((yourpower + inc) - PWRLVRECORD_MIN);
clientpowerlevels[playernum][powertype] += inc;
if (playernum == consoleplayer)
{
vspowerlevel[powertype] = clientpowerlevels[playernum][powertype];
if (M_UpdateUnlockablesAndExtraEmblems(true))
S_StartSound(NULL, sfx_ncitem);
G_SaveGameData(true); // save your punishment!
}
}

31
src/k_pwrlv.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef __K_PWRLV__
#define __K_PWRLV__
#include "doomtype.h"
#include "doomdef.h"
#define PWRLV_DISABLED -1
#define PWRLV_RACE 0
#define PWRLV_BATTLE 1
#define PWRLV_NUMTYPES 2
#define PWRLVRECORD_START 1000 // 5000?
#define PWRLVRECORD_DEF 5000
#define PWRLVRECORD_MIN 1
#define PWRLVRECORD_MAX 9999
extern SINT8 speedscramble;
extern SINT8 encorescramble;
extern UINT16 vspowerlevel[PWRLV_NUMTYPES];
extern UINT16 clientpowerlevels[MAXPLAYERS][PWRLV_NUMTYPES];
extern INT16 nospectategrief[MAXPLAYERS];
void K_ClearClientPowerLevels(void);
INT16 K_CalculatePowerLevelInc(INT16 diff);
INT16 K_CalculatePowerLevelAvg(void);
//void K_UpdatePowerLevels(void);
void K_SetPowerLevelScrambles(SINT8 powertype);
void K_PlayerForfeit(UINT8 playernum, boolean nopointloss);
#endif

View file

@ -21,6 +21,7 @@
#include "r_things.h" // numskins
//#include "r_draw.h" // R_GetColorByName
#include "k_kart.h" // K_GetKartColorByName
#include "k_pwrlv.h"
// Map triggers for linedef executors
// 32 triggers, one bit each
@ -289,6 +290,8 @@ UINT8 M_CheckCondition(condition_t *cn)
return (totalplaytime >= (unsigned)cn->requirement);
case UC_MATCHESPLAYED: // Requires any level completed >= x times
return (matchesplayed >= (unsigned)cn->requirement);
case UC_POWERLEVEL: // Requires power level >= x on a certain gametype
return (vspowerlevel[cn->extrainfo1] >= (unsigned)cn->requirement);
case UC_GAMECLEAR: // Requires game beaten >= x times
return (timesBeaten >= (unsigned)cn->requirement);
case UC_ALLEMERALDS: // Requires game beaten with all 7 emeralds >= x times

View file

@ -22,28 +22,29 @@ typedef enum
{
UC_PLAYTIME, // PLAYTIME [tics]
UC_MATCHESPLAYED, // SRB2Kart: MATCHESPLAYED [x played]
UC_GAMECLEAR, // GAMECLEAR <x times>
UC_ALLEMERALDS, // ALLEMERALDS <x times>
//UC_ULTIMATECLEAR, // ULTIMATECLEAR <x times>
//UC_OVERALLSCORE, // OVERALLSCORE [score to beat]
UC_OVERALLTIME, // OVERALLTIME [time to beat, tics]
//UC_OVERALLRINGS, // OVERALLRINGS [rings to beat]
UC_MAPVISITED, // MAPVISITED [map number]
UC_MAPBEATEN, // MAPBEATEN [map number]
UC_MAPALLEMERALDS, // MAPALLEMERALDS [map number]
//UC_MAPULTIMATE, // MAPULTIMATE [map number]
//UC_MAPPERFECT, // MAPPERFECT [map number]
//UC_MAPSCORE, // MAPSCORE [map number] [score to beat]
UC_MAPTIME, // MAPTIME [map number] [time to beat, tics]
//UC_MAPRINGS, // MAPRINGS [map number] [rings to beat]
//UC_NIGHTSSCORE, // NIGHTSSCORE [map number] <mare, omit or "0" for overall> [score to beat]
//UC_NIGHTSTIME, // NIGHTSTIME [map number] <mare, omit "0" overall> [time to beat, tics]
//UC_NIGHTSGRADE, // NIGHTSGRADE [map number] <mare, omit "0" overall> [grade]
UC_TRIGGER, // TRIGGER [trigger number]
UC_TOTALEMBLEMS, // TOTALEMBLEMS [number of emblems]
UC_EMBLEM, // EMBLEM [emblem number]
UC_EXTRAEMBLEM, // EXTRAEMBLEM [extra emblem number]
UC_CONDITIONSET, // CONDITIONSET [condition set number]
UC_POWERLEVEL, // SRB2Kart: POWERLEVEL [power level to reach] [gametype, "0" for race, "1" for battle]
UC_GAMECLEAR, // GAMECLEAR <x times>
UC_ALLEMERALDS, // ALLEMERALDS <x times>
//UC_ULTIMATECLEAR, // ULTIMATECLEAR <x times>
//UC_OVERALLSCORE, // OVERALLSCORE [score to beat]
UC_OVERALLTIME, // OVERALLTIME [time to beat, tics]
//UC_OVERALLRINGS, // OVERALLRINGS [rings to beat]
UC_MAPVISITED, // MAPVISITED [map number]
UC_MAPBEATEN, // MAPBEATEN [map number]
UC_MAPALLEMERALDS, // MAPALLEMERALDS [map number]
//UC_MAPULTIMATE, // MAPULTIMATE [map number]
//UC_MAPPERFECT, // MAPPERFECT [map number]
//UC_MAPSCORE, // MAPSCORE [map number] [score to beat]
UC_MAPTIME, // MAPTIME [map number] [time to beat, tics]
//UC_MAPRINGS, // MAPRINGS [map number] [rings to beat]
//UC_NIGHTSSCORE, // NIGHTSSCORE [map number] <mare, omit or "0" for overall> [score to beat]
//UC_NIGHTSTIME, // NIGHTSTIME [map number] <mare, omit "0" overall> [time to beat, tics]
//UC_NIGHTSGRADE, // NIGHTSGRADE [map number] <mare, omit "0" overall> [grade]
UC_TRIGGER, // TRIGGER [trigger number]
UC_TOTALEMBLEMS, // TOTALEMBLEMS [number of emblems]
UC_EMBLEM, // EMBLEM [emblem number]
UC_EXTRAEMBLEM, // EXTRAEMBLEM [extra emblem number]
UC_CONDITIONSET, // CONDITIONSET [condition set number]
} conditiontype_t;
// Condition Set information

View file

@ -3558,8 +3558,6 @@ void A_BubbleCheck(mobj_t *actor)
//
void A_AttractChase(mobj_t *actor)
{
fixed_t z;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_AttractChase", actor))
return;
@ -3603,12 +3601,11 @@ void A_AttractChase(mobj_t *actor)
{
fixed_t offz = FixedMul(80*actor->target->scale, FINESINE(FixedAngle((90 - (9 * abs(10 - actor->extravalue1))) << FRACBITS) >> ANGLETOFINESHIFT));
//P_SetScale(actor, (actor->destscale = actor->target->scale));
z = actor->target->z;
if (( actor->eflags & MFE_VERTICALFLIP ))
z -= actor->height + offz;
else
z += actor->target->height + offz;
P_TeleportMove(actor, actor->target->x, actor->target->y, z);
actor->z = actor->target->z;
K_MatchGenericExtraFlags(actor, actor->target);
P_TeleportMove(actor, actor->target->x, actor->target->y,
actor->z +
( actor->target->height + offz )* P_MobjFlip(actor));
actor->extravalue1++;
}
}
@ -3635,15 +3632,12 @@ void A_AttractChase(mobj_t *actor)
fixed_t dist = (actor->target->radius/4) * (16 - actor->extravalue1);
P_SetScale(actor, (actor->destscale = actor->target->scale - ((actor->target->scale/14) * actor->extravalue1)));
z = actor->target->z;
if (( actor->eflags & MFE_VERTICALFLIP ))
z += actor->target->height - actor->height - 24 * actor->target->scale;
else
z += 24 * actor->target->scale;
actor->z = actor->target->z;
K_MatchGenericExtraFlags(actor, actor->target);
P_TeleportMove(actor,
actor->target->x + FixedMul(dist, FINECOSINE(actor->angle >> ANGLETOFINESHIFT)),
actor->target->y + FixedMul(dist, FINESINE(actor->angle >> ANGLETOFINESHIFT)),
z);
actor->z + actor->target->scale * 24 * P_MobjFlip(actor));
actor->angle += ANG30;
actor->extravalue1++;

View file

@ -27,6 +27,7 @@
#include "m_misc.h"
#include "v_video.h" // video flags for CEchos
#include "k_kart.h" // SRB2kart
#include "k_pwrlv.h"
// CTF player names
#define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : ""
@ -2047,7 +2048,8 @@ void P_CheckPointLimit(void)
// Checks whether or not to end a race netgame.
boolean P_CheckRacers(void)
{
INT32 i, j, numplayersingame = 0;
INT32 i, j, numplayersingame = 0, numexiting = 0;
boolean griefed = false;
// Check if all the players in the race have finished. If so, end the level.
for (i = 0; i < MAXPLAYERS; i++)
@ -2064,57 +2066,53 @@ boolean P_CheckRacers(void)
return true;
}
if (cv_karteliminatelast.value)
for (j = 0; j < MAXPLAYERS; j++)
{
for (j = 0; j < MAXPLAYERS; j++)
if (nospectategrief[j] != -1) // prevent spectate griefing
griefed = true;
if (!playeringame[j] || players[j].spectator)
continue;
numplayersingame++;
if (players[j].exiting)
numexiting++;
}
if (cv_karteliminatelast.value && numplayersingame > 1 && !griefed)
{
// check if we just got unlucky and there was only one guy who was a problem
for (j = i+1; j < MAXPLAYERS; j++)
{
if (!playeringame[j] || players[j].spectator)
if (!playeringame[j] || players[j].spectator || players[j].exiting || !players[j].lives)
continue;
numplayersingame++;
break;
}
if (numplayersingame > 1 && nospectategrief > 0 && numplayersingame >= nospectategrief) // prevent spectate griefing
if (j == MAXPLAYERS) // finish anyways, force a time over
{
// check if we just got unlucky and there was only one guy who was a problem
for (j = i+1; j < MAXPLAYERS; j++)
{
if (!playeringame[j] || players[j].spectator || players[j].exiting || !players[j].lives)
continue;
break;
}
if (j == MAXPLAYERS) // finish anyways, force a time over
{
P_DoTimeOver(&players[i]);
countdown = countdown2 = 0;
return true;
}
P_DoTimeOver(&players[i]);
countdown = countdown2 = 0;
return true;
}
}
if (!countdown) // Check to see if the winners have finished, to set countdown.
{
UINT8 numingame = 0, numexiting = 0;
UINT8 winningpos = 1;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
continue;
numingame++;
if (players[i].exiting)
numexiting++;
}
winningpos = max(1, numingame/2);
if (numingame % 2) // any remainder?
winningpos = max(1, numplayersingame/2);
if (numplayersingame % 2) // any remainder?
winningpos++;
if (numexiting >= winningpos)
countdown = (((netgame || multiplayer) ? cv_countdowntime.value : 30)*TICRATE) + 1; // 30 seconds to finish, get going!
}
if (numplayersingame < 2) // reset nospectategrief in free play
{
for (j = 0; j < MAXPLAYERS; j++)
nospectategrief[j] = -1;
}
return false;
}

View file

@ -8744,18 +8744,13 @@ void P_MobjThinker(mobj_t *mobj)
return;
}
mobj->z = mobj->target->z;
K_MatchGenericExtraFlags(mobj, mobj->target);
{
fixed_t z;
z = mobj->target->z;
if (( mobj->eflags & MFE_VERTICALFLIP ))
z -= mobj->height;
else
z += mobj->target->height;
P_TeleportMove(mobj, mobj->target->x + FINECOSINE(mobj->angle >> ANGLETOFINESHIFT),
mobj->target->y + FINESINE(mobj->angle >> ANGLETOFINESHIFT),
z);
}
P_TeleportMove(mobj, mobj->target->x + FINECOSINE(mobj->angle >> ANGLETOFINESHIFT),
mobj->target->y + FINESINE(mobj->angle >> ANGLETOFINESHIFT),
mobj->z + mobj->target->height * P_MobjFlip(mobj));
break;
case MT_TIREGREASE:
if (!mobj->target || P_MobjWasRemoved(mobj->target) || !mobj->target->player

View file

@ -34,6 +34,9 @@
#include "p_slopes.h"
#endif
// SRB2Kart
#include "k_pwrlv.h"
savedata_t savedata;
UINT8 *save_p;
@ -3302,7 +3305,10 @@ static void P_NetArchiveMisc(void)
WRITEUINT32(save_p, indirectitemcooldown);
WRITEUINT32(save_p, hyubgone);
WRITEUINT32(save_p, mapreset);
WRITEUINT8(save_p, nospectategrief);
for (i = 0; i < MAXPLAYERS; i++)
WRITEINT16(save_p, nospectategrief[i]);
WRITEUINT8(save_p, thwompsactive);
WRITESINT8(save_p, spbplace);
@ -3419,7 +3425,10 @@ static inline boolean P_NetUnArchiveMisc(void)
indirectitemcooldown = READUINT32(save_p);
hyubgone = READUINT32(save_p);
mapreset = READUINT32(save_p);
nospectategrief = READUINT8(save_p);
for (i = 0; i < MAXPLAYERS; i++)
nospectategrief[i] = READINT16(save_p);
thwompsactive = (boolean)READUINT8(save_p);
spbplace = READSINT8(save_p);

View file

@ -84,6 +84,7 @@
// SRB2Kart
#include "k_kart.h"
#include "k_pwrlv.h"
//
// Map MD5, calculated on level load.
@ -2396,7 +2397,12 @@ static void P_LevelInitStuff(void)
if (G_BattleGametype())
gamespeed = 0;
else
gamespeed = (UINT8)cv_kartspeed.value;
{
if (cv_kartspeed.value == -1)
gamespeed = ((speedscramble == -1) ? atoi(cv_kartspeed.defaultvalue) : (UINT8)speedscramble);
else
gamespeed = (UINT8)cv_kartspeed.value;
}
franticitems = (boolean)cv_kartfrantic.value;
comeback = (boolean)cv_kartcomeback.value;
}
@ -2405,6 +2411,7 @@ static void P_LevelInitStuff(void)
battlewanted[i] = -1;
memset(&battleovertime, 0, sizeof(struct battleovertime));
speedscramble = encorescramble = -1;
}
//
@ -3290,7 +3297,10 @@ boolean P_SetupLevel(boolean skipprecip)
indirectitemcooldown = 0;
hyubgone = 0;
mapreset = 0;
nospectategrief = 0;
for (i = 0; i < MAXPLAYERS; i++)
nospectategrief[i] = -1;
thwompsactive = false;
spbplace = -1;

View file

@ -3637,10 +3637,9 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
break;
case 9: // Ring Drainer (Floor Touch)
case 10: // Ring Drainer (No Floor Touch)
if (leveltime % (TICRATE/2) == 0 && player->mo->health > 1)
if (leveltime % (TICRATE/2) == 0 && player->kartstuff[k_rings] > 0)
{
player->mo->health--;
player->health--;
player->kartstuff[k_rings]--;
S_StartSound(player->mo, sfx_itemup);
}
break;
@ -4309,10 +4308,6 @@ DoneSection2:
// Play the starpost sound for 'consistency'
// S_StartSound(player->mo, sfx_strpst);
// Figure out how many are playing on the last lap, to prevent spectate griefing
if (!nospectategrief && player->laps >= (UINT8)(cv_numlaps.value - 1))
nospectategrief = nump;
thwompsactive = true; // Lap 2 effects
}
else if (player->starpostnum)

View file

@ -617,33 +617,10 @@ void P_Ticker(boolean run)
}
if (demo.playback)
{
#ifdef DEMO_COMPAT_100
if (demo.version == 0x0001)
{
G_ReadDemoTiccmd(&players[consoleplayer].cmd, 0);
}
else
{
#endif
G_ReadDemoExtraData();
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
{
//@TODO all this throwdir stuff shouldn't be here! But it's added to maintain 1.0.4 compat for now...
// Remove for 1.1!
if (players[i].cmd.buttons & BT_FORWARD)
players[i].kartstuff[k_throwdir] = 1;
else if (players[i].cmd.buttons & BT_BACKWARD)
players[i].kartstuff[k_throwdir] = -1;
else
players[i].kartstuff[k_throwdir] = 0;
G_ReadDemoTiccmd(&players[i].cmd, i);
}
#ifdef DEMO_COMPAT_100
}
#endif
G_ReadDemoExtraData();
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
G_ReadDemoTiccmd(&players[i].cmd, i);
}
for (i = 0; i < MAXPLAYERS; i++)
@ -761,11 +738,6 @@ void P_Ticker(boolean run)
}
else if (demo.playback) // Use Ghost data for consistency checks.
{
#ifdef DEMO_COMPAT_100
if (demo.version == 0x0001)
G_ConsGhostTic(0);
else
#endif
G_ConsAllGhostTics();
}

View file

@ -8140,8 +8140,6 @@ void P_PlayerThink(player_t *player)
cmd = &player->cmd;
//@TODO This fixes a one-tic latency on direction handling, AND makes behavior consistent while paused, but is not BC with 1.0.4. Do this for 1.1!
#if 0
// SRB2kart
// Save the dir the player is holding
// to allow items to be thrown forward or backward.
@ -8151,7 +8149,6 @@ void P_PlayerThink(player_t *player)
player->kartstuff[k_throwdir] = -1;
else
player->kartstuff[k_throwdir] = 0;
#endif
// Add some extra randomization.
if (cmd->forwardmove)

View file

@ -165,6 +165,7 @@ static char returnWadPath[256];
#include "../d_net.h"
#include "../g_game.h"
#include "../filesrch.h"
#include "../k_pwrlv.h"
#include "endtxt.h"
#include "sdlmain.h"
@ -3056,6 +3057,11 @@ void I_Quit(void)
#ifndef NONET
D_SaveBan(); // save the ban list
#endif
// Make sure you lose points for ALT-F4
if (Playing())
K_PlayerForfeit(consoleplayer, true);
G_SaveGameData(false); // Tails 12-08-2002
//added:16-02-98: when recording a demo, should exit using 'q' key,
// but sometimes we forget and use 'F10'.. so save here too.

View file

@ -233,6 +233,7 @@ static char returnWadPath[256];
#include "../d_net.h"
#include "../g_game.h"
#include "../filesrch.h"
#include "../k_pwrlv.h"
#include "endtxt.h"
#include "sdlmain.h"
@ -2977,6 +2978,11 @@ void I_Quit(void)
#ifndef NONET
D_SaveBan(); // save the ban list
#endif
// Make sure you lose points for ALT-F4
if (Playing())
K_PlayerForfeit(consoleplayer, true);
G_SaveGameData(); // Tails 12-08-2002
//added:16-02-98: when recording a demo, should exit using 'q' key,
// but sometimes we forget and use 'F10'.. so save here too.

View file

@ -39,6 +39,7 @@
#include "m_random.h" // M_RandomKey
#include "g_input.h" // PLAYER1INPUTDOWN
#include "k_kart.h" // colortranslations
#include "k_pwrlv.h"
#include "lua_hook.h" // IntermissionThinker hook
#ifdef HWRENDER
@ -89,7 +90,7 @@ typedef union
INT32 numplayers; // Number of players being displayed
char levelstring[64]; // holds levelnames up to 64 characters
// SRB2kart
UINT8 increase[MAXPLAYERS]; // how much did the score increase by?
INT16 increase[MAXPLAYERS]; // how much did the score increase by?
UINT8 jitter[MAXPLAYERS]; // wiggle
UINT32 val[MAXPLAYERS]; // Gametype-specific value
UINT8 pos[MAXPLAYERS]; // player positions. used for ties
@ -109,6 +110,7 @@ static boolean usetile;
boolean usebuffer = false;
static boolean useinterpic;
static INT32 timer;
static INT32 powertype = PWRLV_DISABLED;
static INT32 intertic;
static INT32 endtic = -1;
@ -192,11 +194,13 @@ static void Y_CompareBattle(INT32 i)
static void Y_CompareRank(INT32 i)
{
UINT8 increase = ((data.match.increase[i] == UINT8_MAX) ? 0 : data.match.increase[i]);
if (!(data.match.val[data.match.numplayers] == UINT32_MAX || (players[i].score - increase) > data.match.val[data.match.numplayers]))
INT16 increase = ((data.match.increase[i] == INT16_MIN) ? 0 : data.match.increase[i]);
UINT32 score = (powertype != -1 ? clientpowerlevels[i][powertype] : players[i].score);
if (!(data.match.val[data.match.numplayers] == UINT32_MAX || (score - increase) > data.match.val[data.match.numplayers]))
return;
data.match.val[data.match.numplayers] = (players[i].score - increase);
data.match.val[data.match.numplayers] = (score - increase);
data.match.num[data.match.numplayers] = i;
}
@ -204,7 +208,7 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
{
INT32 i, j;
boolean completed[MAXPLAYERS];
INT32 numplayersingame = 0;
INT32 numplayersingame = 0, numgriefers = 0;
// Initialize variables
if (rankingsmode > 1)
@ -256,14 +260,17 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
{
data.match.val[i] = UINT32_MAX;
if (nospectategrief[i] != -1)
numgriefers++;
if (!playeringame[i] || players[i].spectator)
{
data.match.increase[i] = UINT8_MAX;
data.match.increase[i] = INT16_MIN;
continue;
}
if (!rankingsmode)
data.match.increase[i] = UINT8_MAX;
data.match.increase[i] = INT16_MIN;
numplayersingame++;
}
@ -275,8 +282,6 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
for (j = 0; j < numplayersingame; j++)
{
INT32 nump = ((G_RaceGametype() && nospectategrief > 0) ? nospectategrief : numplayersingame);
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator || completed[i])
@ -298,9 +303,10 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
else
data.match.pos[data.match.numplayers] = data.match.numplayers+1;
if (!rankingsmode && !(players[i].pflags & PF_TIMEOVER) && (data.match.pos[data.match.numplayers] < nump))
if ((!rankingsmode && powertype == -1) // Single player rankings (grand prix). Online rank is handled below.
&& !(players[i].pflags & PF_TIMEOVER) && (data.match.pos[data.match.numplayers] < (numplayersingame + numgriefers)))
{
data.match.increase[i] = nump - data.match.pos[data.match.numplayers];
data.match.increase[i] = (numplayersingame + numgriefers) - data.match.pos[data.match.numplayers];
players[i].score += data.match.increase[i];
}
@ -429,7 +435,7 @@ void Y_IntermissionDrawer(void)
const char *timeheader;
if (data.match.rankingsmode)
timeheader = "RANK";
timeheader = "PWR.LV";
else
timeheader = (intertype == int_race ? "TIME" : "SCORE");
@ -491,21 +497,23 @@ void Y_IntermissionDrawer(void)
if (data.match.rankingsmode)
{
if (data.match.increase[data.match.num[i]] != UINT8_MAX)
if (!clientpowerlevels[data.match.num[i]][powertype]) // No power level (splitscreen guests)
STRBUFCPY(strtime, "----");
else
{
if (data.match.increase[data.match.num[i]] > 9)
snprintf(strtime, sizeof strtime, "(+%02d)", data.match.increase[data.match.num[i]]);
else
snprintf(strtime, sizeof strtime, "(+ %d)", data.match.increase[data.match.num[i]]);
if (data.match.increase[data.match.num[i]] != INT16_MIN)
{
snprintf(strtime, sizeof strtime, "(%d)", data.match.increase[data.match.num[i]]);
if (data.match.numplayers > NUMFORNEWCOLUMN)
V_DrawRightAlignedThinString(x+135+gutter, y-1, V_6WIDTHSPACE, strtime);
else
V_DrawRightAlignedString(x+120+gutter, y, 0, strtime);
if (data.match.numplayers > NUMFORNEWCOLUMN)
V_DrawRightAlignedThinString(x+133+gutter, y-1, V_6WIDTHSPACE, strtime);
else
V_DrawRightAlignedString(x+118+gutter, y, 0, strtime);
}
snprintf(strtime, sizeof strtime, "%d", data.match.val[i]);
}
snprintf(strtime, sizeof strtime, "%d", data.match.val[i]);
if (data.match.numplayers > NUMFORNEWCOLUMN)
V_DrawRightAlignedThinString(x+152+gutter, y-1, V_6WIDTHSPACE, strtime);
else
@ -589,9 +597,14 @@ dotimer:
break;
}
// Make it obvious that scrambling is happening next round.
if (cv_scrambleonchange.value && cv_teamscramble.value && (intertic/TICRATE % 2 == 0))
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, hilicol, M_GetText("Teams will be scrambled next round!"));
//if ((intertic/TICRATE) & 1) // Make it obvious that scrambling is happening next round. (OR NOT, I GUESS)
//{
/*if (cv_scrambleonchange.value && cv_teamscramble.value)
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, hilicol, M_GetText("Teams will be scrambled next round!"));*/
if (speedscramble != -1 && speedscramble != gamespeed)
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24, hilicol|V_ALLOWLOWERCASE|V_SNAPTOBOTTOM,
va(M_GetText("Next race will be %s Speed!"), kartspeed_cons_t[speedscramble].strvalue));
//}
}
//
@ -674,13 +687,46 @@ void Y_Ticker(void)
{
if (data.match.num[q] == MAXPLAYERS
|| !data.match.increase[data.match.num[q]]
|| data.match.increase[data.match.num[q]] == UINT8_MAX)
|| data.match.increase[data.match.num[q]] == INT16_MIN)
continue;
r++;
data.match.jitter[data.match.num[q]] = 1;
if (--data.match.increase[data.match.num[q]])
kaching = false;
if (powertype != -1)
{
// Power Levels
if (abs(data.match.increase[data.match.num[q]]) < 10)
{
// Not a lot of point increase left, just set to 0 instantly
data.match.increase[data.match.num[q]] = 0;
}
else
{
SINT8 remove = 0; // default (should not happen)
if (data.match.increase[data.match.num[q]] < 0)
remove = -10;
else if (data.match.increase[data.match.num[q]] > 0)
remove = 10;
// Remove 10 points at a time
data.match.increase[data.match.num[q]] -= remove;
// Still not zero, no kaching yet
if (data.match.increase[data.match.num[q]] != 0)
kaching = false;
}
}
else
{
// Basic bitch points
if (data.match.increase[data.match.num[q]])
{
if (--data.match.increase[data.match.num[q]])
kaching = false;
}
}
}
if (r)
@ -780,6 +826,153 @@ static void Y_UpdateRecordReplays(void)
//CV_AddValue(&cv_nextmap, -1);
}
static void K_UpdatePowerLevels(void)
{
INT32 i, j;
INT32 numplayersingame = 0, numgriefers = 0;
INT16 increment[MAXPLAYERS];
// Compare every single player against each other for power level increases.
// Every player you won against gives you more points, and vice versa.
// The amount of points won per match-up depends on the difference between the loser's power and the winner's power.
// See K_CalculatePowerLevelInc for more info.
for (i = 0; i < MAXPLAYERS; i++)
{
increment[i] = 0;
if (nospectategrief[i] != -1)
numgriefers++;
if (!playeringame[i] || players[i].spectator)
continue;
numplayersingame++;
}
for (i = 0; i < numplayersingame; i++)
{
UINT16 yourpower = PWRLVRECORD_DEF;
UINT16 theirpower = PWRLVRECORD_DEF;
INT16 diff = 0; // Loser PWR.LV - Winner PWR.LV
INT16 inc = 0; // Total pt increment
UINT8 ipnum = data.match.num[i];
UINT8 jpnum;
CONS_Debug(DBG_GAMELOGIC, "Power Level Gain for player %d:\n", ipnum);
if (clientpowerlevels[ipnum][powertype] == 0) // splitscreen guests don't record power level changes
continue;
yourpower = clientpowerlevels[ipnum][powertype];
CONS_Debug(DBG_GAMELOGIC, "Player %d's PWR.LV: %d\n", ipnum, yourpower);
for (j = 0; j < numplayersingame; j++)
{
boolean won = false;
jpnum = data.match.num[j];
if (i == j || ipnum == jpnum) // Same person
continue;
CONS_Debug(DBG_GAMELOGIC, "Player %d VS Player %d:\n", ipnum, jpnum);
if (data.match.val[i] == data.match.val[j]) // Tie -- neither get any points for this match up.
{
CONS_Debug(DBG_GAMELOGIC, "TIE, no change.\n");
continue;
}
theirpower = PWRLVRECORD_DEF;
if (clientpowerlevels[jpnum][powertype] != 0) // No power level acts as 5000 (used for splitscreen guests)
theirpower = clientpowerlevels[jpnum][powertype];
CONS_Debug(DBG_GAMELOGIC, "Player %d's PWR.LV: %d\n", jpnum, theirpower);
if (G_RaceGametype())
{
if (data.match.val[i] < data.match.val[j])
won = true;
}
else
{
if (data.match.val[i] > data.match.val[j])
won = true;
}
if (won) // This player won!
{
diff = theirpower - yourpower;
inc += K_CalculatePowerLevelInc(diff);
CONS_Debug(DBG_GAMELOGIC, "WON! Diff is %d, total increment is %d\n", diff, inc);
}
else // This player lost...
{
diff = yourpower - theirpower;
inc -= K_CalculatePowerLevelInc(diff);
CONS_Debug(DBG_GAMELOGIC, "LOST... Diff is %d, total increment is %d\n", diff, inc);
}
}
if (numgriefers != 0) // Automatic win against quitters.
{
for (jpnum = 0; jpnum < MAXPLAYERS; jpnum++)
{
if (nospectategrief[jpnum] == -1) // Empty slot
continue;
if (ipnum == jpnum) // Same person
continue;
CONS_Debug(DBG_GAMELOGIC, "Player %d VS Player %d (griefer):\n", ipnum, jpnum);
theirpower = PWRLVRECORD_DEF;
if (nospectategrief[jpnum] != 0) // No power level acts as 5000 (used for splitscreen guests)
theirpower = nospectategrief[jpnum];
CONS_Debug(DBG_GAMELOGIC, "Player %d's PWR.LV: %d\n", jpnum, theirpower);
diff = theirpower - yourpower;
inc += K_CalculatePowerLevelInc(diff);
CONS_Debug(DBG_GAMELOGIC, "AUTO-WON! Diff is %d, total increment is %d\n", diff, inc);
}
}
if (inc == 0)
{
data.match.increase[ipnum] = INT16_MIN;
CONS_Debug(DBG_GAMELOGIC, "Total Result: No increment, no change.\n");
continue;
}
if (yourpower + inc > PWRLVRECORD_MAX)
inc -= ((yourpower + inc) - PWRLVRECORD_MAX);
if (yourpower + inc < PWRLVRECORD_MIN)
inc -= ((yourpower + inc) - PWRLVRECORD_MIN);
CONS_Debug(DBG_GAMELOGIC, "Total Result: Increment of %d.\n", inc);
increment[ipnum] = inc;
}
CONS_Debug(DBG_GAMELOGIC, "Setting final power levels...\n");
for (i = 0; i < MAXPLAYERS; i++)
{
if (increment[i] == 0)
continue;
data.match.increase[i] = increment[i];
clientpowerlevels[i][powertype] += data.match.increase[i];
if (i == consoleplayer)
{
CONS_Debug(DBG_GAMELOGIC, "Player %d is you! Saving...\n", i);
vspowerlevel[powertype] = clientpowerlevels[i][powertype];
if (M_UpdateUnlockablesAndExtraEmblems(true))
S_StartSound(NULL, sfx_ncitem);
G_SaveGameData(true);
}
}
}
//
// Y_StartIntermission
//
@ -794,6 +987,19 @@ void Y_StartIntermission(void)
I_Error("endtic is dirty");
#endif
// set player Power Level type
powertype = PWRLV_DISABLED;
if (netgame && cv_kartusepwrlv.value)
{
if (G_RaceGametype())
powertype = PWRLV_RACE;
else if (G_BattleGametype())
powertype = PWRLV_BATTLE;
}
K_SetPowerLevelScrambles(powertype);
if (!multiplayer)
{
timer = 0;
@ -870,6 +1076,8 @@ void Y_StartIntermission(void)
break;
}
if (powertype != -1)
K_UpdatePowerLevels();
bgpatch = W_CachePatchName("MENUBG", PU_STATIC);
widebgpatch = W_CachePatchName("WEIRDRES", PU_STATIC);