Merge branch 'master' into sal-waypoints

This commit is contained in:
TehRealSalt 2019-10-15 21:34:41 -04:00
commit e7d0fde5fd
45 changed files with 2158 additions and 1020 deletions

View file

@ -60,6 +60,7 @@
# Compile with GCC 4.6x version, add 'GCC46=1'
# Compile a profile version, add 'PROFILEMODE=1'
# Compile a debug version, add 'DEBUGMODE=1'
# Compile for the testers group (they don't get to play unless we're watching *wink*), add 'TESTERS=1'
# Compile with extra warnings, add 'WARNINGMODE=1'
# Compile without NASM's tmap.nas, add 'NOASM=1'
# Compile without 3D hardware support, add 'NOHW=1'
@ -434,6 +435,10 @@ else
endif
CFLAGS+=-g $(OPTS) $(ARCHOPTS) $(WINDRESFLAGS)
ifdef TESTERS
OPTS+=-DTESTERS
endif
ifdef YASM
ifdef STABS
NASMOPTS?= -g stabs
@ -492,6 +497,7 @@ OBJS:=$(i_main_o) \
$(OBJDIR)/k_waypoint.o\
$(OBJDIR)/k_pathfind.o\
$(OBJDIR)/k_bheap.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)
@ -1769,7 +1773,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();
@ -1778,7 +1782,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)
@ -3623,6 +3651,11 @@ boolean Playing(void)
boolean SV_SpawnServer(void)
{
#ifdef TESTERS
/* Just don't let the testers play. Easy. */
I_Error("What do you think you're doing?");
return false;
#else
if (demo.playback)
G_StopDemo(); // reset engine parameter
if (metalplayback)
@ -3649,6 +3682,7 @@ boolean SV_SpawnServer(void)
}
return SV_AddWaitingPlayers();
#endif
}
void SV_StopServer(void)
@ -4075,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)
@ -4098,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

@ -308,13 +308,6 @@ static void D_Display(void)
wipedefindex = gamestate; // wipe_xxx_toblack
if (gamestate == GS_TITLESCREEN && wipegamestate != GS_INTRO)
wipedefindex = wipe_timeattack_toblack;
else if (gamestate == GS_INTERMISSION)
{
if (intertype == int_spec) // Special Stage
wipedefindex = wipe_specinter_toblack;
else //if (intertype != int_coop) // Multiplayer
wipedefindex = wipe_multinter_toblack;
}
if (rendermode != render_none)
{
@ -325,7 +318,7 @@ static void D_Display(void)
F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
F_WipeEndScreen();
F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK);
F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK, "FADEMAP0", false, false);
}
if (gamestate != GS_LEVEL && rendermode != render_none)
@ -556,7 +549,7 @@ static void D_Display(void)
if (rendermode != render_none)
{
F_WipeEndScreen();
F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK);
F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK, "FADEMAP0", true, false);
}
}
@ -1566,7 +1559,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}};
@ -411,7 +416,8 @@ consvar_t cv_itemfinder = {"itemfinder", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff,
// Scoring type options
consvar_t cv_match_scoring = {"matchscoring", "Normal", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, match_scoring_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_overtime = {"overtime", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t overtime_cons_t[] = {{0, "No"}, {1, "Yes"}, {2, "Super"}, {0, NULL}};
consvar_t cv_overtime = {"overtime", "Yes", CV_NETVAR|CV_CHEAT, overtime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_rollingdemos = {"rollingdemos", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
@ -501,6 +507,7 @@ const char *netxcmdnames[MAXNETXCMD - 1] =
"MODIFYVOTE",
"PICKVOTE",
"REMOVEPLAYER",
"POWERLEVEL",
#ifdef HAVE_BLUA
"LUACMD",
"LUAVAR"
@ -538,6 +545,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);
@ -1915,6 +1923,17 @@ static void Got_WeaponPref(UINT8 **cp,INT32 playernum)
players[playernum].pflags |= PF_FLIPCAM;
}
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();
@ -1931,6 +1950,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!
@ -2343,7 +2387,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);
@ -2514,7 +2558,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"))
{
@ -3641,6 +3685,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;
@ -4735,8 +4782,8 @@ static void TimeLimit_OnChange(void)
if (cv_timelimit.value != 0)
{
CONS_Printf(M_GetText("Levels will end after %d minute%s.\n"),cv_timelimit.value,cv_timelimit.value == 1 ? "" : "s"); // Graue 11-17-2003
timelimitintics = cv_timelimit.value * 60 * TICRATE;
CONS_Printf(M_GetText("Levels will end after %d second%s.\n"),cv_timelimit.value,cv_timelimit.value == 1 ? "" : "s"); // Graue 11-17-2003
timelimitintics = cv_timelimit.value * TICRATE;
//add hidetime for tag too!
if (G_TagGametype())
@ -4786,9 +4833,9 @@ void D_GameTypeChanged(INT32 lastgametype)
case GT_TEAMMATCH:
if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits
{
// default settings for match: no timelimit, no pointlimit
CV_SetValue(&cv_pointlimit, 0);
CV_SetValue(&cv_timelimit, 0);
// default settings for match: 2 mins, no pointlimit
CV_SetValue(&cv_pointlimit, 0);
CV_SetValue(&cv_timelimit, 120);
}
if (!cv_itemrespawntime.changed)
CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally
@ -5094,7 +5141,7 @@ static void Hidetime_OnChange(void)
//uh oh, gotta change timelimitintics now too
if (G_TagGametype())
timelimitintics = (cv_timelimit.value * 60 * TICRATE) + (hidetime * TICRATE);
timelimitintics = (cv_timelimit.value * TICRATE) + (hidetime * TICRATE);
}
static void Command_Showmap_f(void)
@ -5727,7 +5774,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;
}
@ -5747,10 +5794,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;
@ -179,9 +179,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

@ -329,6 +329,7 @@ typedef enum
k_tiregrease, // Reduced friction timer after hitting a horizontal spring
k_springstars, // Spawn stars around a player when they hit a spring
k_springcolor, // Color of spring stars
k_killfield, // How long have you been in the kill field, stay in too long and lose a bumper
NUMKARTSTUFF
} kartstufftype_t;

View file

@ -2553,6 +2553,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")))
@ -2605,7 +2618,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;
}
}
@ -3257,29 +3270,13 @@ static void readwipes(MYFILE *f)
else if (fastcmp(pword, "FINAL"))
wipeoffset = wipe_intermission_final;
}
else if (fastncmp(word, "SPECINTER_", 10))
{
pword = word + 10;
if (fastcmp(pword, "TOBLACK"))
wipeoffset = wipe_specinter_toblack;
else if (fastcmp(pword, "FINAL"))
wipeoffset = wipe_specinter_final;
}
else if (fastncmp(word, "VOTING_", 7))
{
pword = word + 7;
if (fastcmp(pword, "TOBLACK"))
wipeoffset = wipe_specinter_toblack;
wipeoffset = wipe_voting_toblack;
else if (fastcmp(pword, "FINAL"))
wipeoffset = wipe_specinter_final;
}
else if (fastncmp(word, "MULTINTER_", 10))
{
pword = word + 10;
if (fastcmp(pword, "TOBLACK"))
wipeoffset = wipe_multinter_toblack;
else if (fastcmp(pword, "FINAL"))
wipeoffset = wipe_multinter_final;
wipeoffset = wipe_voting_final;
}
else if (fastncmp(word, "CONTINUING_", 11))
{
@ -3331,11 +3328,13 @@ static void readwipes(MYFILE *f)
else if (fastcmp(pword, "FINAL"))
wipeoffset = wipe_gameend_final;
}
else if (fastncmp(word, "SPECLEVEL_", 10))
else if (fastncmp(word, "ENCORE_", 7))
{
pword = word + 10;
if (fastcmp(pword, "TOWHITE"))
wipeoffset = wipe_speclevel_towhite;
pword = word + 7;
if (fastcmp(pword, "TOINVERT"))
wipeoffset = wipe_encore_toinvert;
else if (fastcmp(pword, "TOWHITE"))
wipeoffset = wipe_encore_towhite;
}
if (wipeoffset < 0)
@ -3345,10 +3344,10 @@ static void readwipes(MYFILE *f)
}
if (value == UINT8_MAX
&& (wipeoffset <= wipe_level_toblack || wipeoffset >= wipe_speclevel_towhite))
&& (wipeoffset <= wipe_level_toblack || wipeoffset >= wipe_encore_toinvert))
{
// Cannot disable non-toblack wipes
// (or the level toblack wipe, or the special towhite wipe)
// (or the level toblack wipe, or the special encore wipe)
deh_warning("Wipes: can't disable wipe of type '%s'", word);
continue;
}
@ -7215,6 +7214,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_TIREGREASE",
"S_OVERTIMEFOG",
"S_OVERTIMEORB",
"S_OVERTIMEBEAM",
#ifdef SEENAMES
"S_NAMECHECK",
#endif
@ -8009,6 +8012,10 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_DRAFTDUST",
"MT_TIREGREASE",
"MT_OVERTIMEFOG",
"MT_OVERTIMEORB",
"MT_OVERTIMEBEAM",
#ifdef SEENAMES
"MT_NAMECHECK",
#endif
@ -8493,7 +8500,8 @@ static const char *const KARTSTUFF_LIST[] = {
"GROWCANCEL",
"TIREGREASE",
"SPRINGSTARS",
"SPRINGCOLOR"
"SPRINGCOLOR",
"KILLFIELD"
};
#endif

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

@ -475,7 +475,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;
@ -487,6 +486,15 @@ extern INT16 votelevels[5][2];
extern SINT8 votes[MAXPLAYERS];
extern SINT8 pickedvote;
/** Battle overtime information
*/
extern struct battleovertime
{
UINT16 enabled; ///< Has this been initalized yet?
fixed_t radius, minradius; ///< Radius of kill field
fixed_t x, y, z; ///< Position to center on
} battleovertime;
extern tic_t hidetime;
extern UINT32 timesBeaten; // # of times the game has been beaten.

View file

@ -220,7 +220,7 @@ void F_StartIntro(void)
F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
F_WipeEndScreen();
F_RunWipe(wipedefs[wipe_level_final], false);
F_RunWipe(wipedefs[wipe_intro_toblack], false, "FADEMAP0", false, false);
}
if (introtoplay)
@ -306,7 +306,7 @@ void F_IntroDrawer(void)
F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
F_WipeEndScreen();
F_RunWipe(99,true);
F_RunWipe(99, true, "FADEMAP0", false, false);
}
// Stay on black for a bit. =)
@ -1420,7 +1420,7 @@ void F_CutsceneDrawer(void)
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, cutscenes[cutnum]->scene[scenenum].fadecolor);
F_WipeEndScreen();
F_RunWipe(cutscenes[cutnum]->scene[scenenum].fadeinid, true);
F_RunWipe(cutscenes[cutnum]->scene[scenenum].fadeinid, true, NULL, false, false);
F_WipeStartScreen();
}
@ -1440,7 +1440,7 @@ void F_CutsceneDrawer(void)
if (dofadenow && rendermode != render_none)
{
F_WipeEndScreen();
F_RunWipe(cutscenes[cutnum]->scene[scenenum].fadeoutid, true);
F_RunWipe(cutscenes[cutnum]->scene[scenenum].fadeoutid, true, NULL, false, false);
}
V_DrawString(textxpos, textypos, 0, cutscene_disptext);

View file

@ -74,12 +74,13 @@ extern INT32 lastwipetic;
void F_WipeStartScreen(void);
void F_WipeEndScreen(void);
void F_RunWipe(UINT8 wipetype, boolean drawMenu);
void F_RunWipe(UINT8 wipetype, boolean drawMenu, const char *colormap, boolean reverse, boolean encorewiggle);
enum
{
wipe_credits_intermediate, // makes a good 0 I guess.
// Gamestate wipes
wipe_level_toblack,
wipe_intermission_toblack,
wipe_voting_toblack,
@ -92,11 +93,11 @@ enum
wipe_intro_toblack,
wipe_cutscene_toblack,
// custom intermissions
wipe_specinter_toblack,
wipe_multinter_toblack,
wipe_speclevel_towhite,
// Specialized wipes
wipe_encore_toinvert,
wipe_encore_towhite,
// "From black" wipes
wipe_level_final,
wipe_intermission_final,
wipe_voting_final,
@ -109,10 +110,6 @@ enum
wipe_intro_final,
wipe_cutscene_final,
// custom intermissions
wipe_specinter_final,
wipe_multinter_final,
NUMWIPEDEFS,
WIPEFINALSHIFT = wipe_level_final - wipe_level_toblack
};

View file

@ -16,8 +16,10 @@
#include "i_video.h"
#include "v_video.h"
#include "r_data.h" // NearestColor
#include "r_draw.h" // transtable
#include "p_pspr.h" // tr_transxxx
#include "w_wad.h"
#include "z_zone.h"
@ -47,35 +49,31 @@ UINT8 wipedefs[NUMWIPEDEFS] = {
99, // wipe_credits_intermediate (0)
0, // wipe_level_toblack
UINT8_MAX, // wipe_intermission_toblack
0, // wipe_intermission_toblack
0, // wipe_voting_toblack,
UINT8_MAX, // wipe_continuing_toblack
3, // wipe_titlescreen_toblack
0, // wipe_continuing_toblack
0, // wipe_titlescreen_toblack
0, // wipe_timeattack_toblack
99, // wipe_credits_toblack
0, // wipe_evaluation_toblack
0, // wipe_gameend_toblack
UINT8_MAX, // wipe_intro_toblack (hardcoded)
UINT8_MAX, // wipe_cutscene_toblack (hardcoded)
99, // wipe_cutscene_toblack (hardcoded)
UINT8_MAX, // wipe_specinter_toblack
UINT8_MAX, // wipe_multinter_toblack
99, // wipe_speclevel_towhite
72, // wipe_encore_toinvert
99, // wipe_encore_towhite
3, // wipe_level_final
UINT8_MAX, // wipe_level_final
0, // wipe_intermission_final
0, // wipe_voting_final
0, // wipe_continuing_final
3, // wipe_titlescreen_final
0, // wipe_titlescreen_final
0, // wipe_timeattack_final
99, // wipe_credits_final
0, // wipe_evaluation_final
0, // wipe_gameend_final
99, // wipe_intro_final (hardcoded)
99, // wipe_cutscene_final (hardcoded)
0, // wipe_specinter_final
0 // wipe_multinter_final
99 // wipe_cutscene_final (hardcoded)
};
//--------------------------------------------------------------------------
@ -86,9 +84,13 @@ boolean WipeInAction = false;
INT32 lastwipetic = 0;
#ifndef NOWIPE
#define GENLEN 31
static UINT8 *wipe_scr_start; //screen 3
static UINT8 *wipe_scr_end; //screen 4
static UINT8 *wipe_scr; //screen 0 (main drawing)
static UINT8 pallen;
static fixed_t paldiv;
/** Create fademask_t from lump
@ -181,7 +183,7 @@ static fademask_t *F_GetFadeMask(UINT8 masknum, UINT8 scrnnum) {
*
* \param fademask pixels to change
*/
static void F_DoWipe(fademask_t *fademask)
static void F_DoWipe(fademask_t *fademask, lighttable_t *fadecolormap, boolean reverse)
{
// Software mask wipe -- optimized; though it might not look like it!
// Okay, to save you wondering *how* this is more optimized than the simpler
@ -199,6 +201,10 @@ static void F_DoWipe(fademask_t *fademask)
// look a little messy; sorry!) but it simultaneously runs at twice the speed.
// In addition, we precalculate all the X and Y positions that we need to draw
// from and to, so it uses a little extra memory, but again, helps it run faster.
// ---
// Sal: I kinda destroyed some of this code by introducing Genesis-style fades.
// A colormap can be provided in F_RunWipe, which the white/black values will be
// remapped to the appropriate entry in the fade colormap.
{
// wipe screen, start, end
UINT8 *w = wipe_scr;
@ -242,6 +248,8 @@ static void F_DoWipe(fademask_t *fademask)
maskx = masky = 0;
do
{
UINT8 m = *mask;
draw_rowstart = scrxpos[maskx];
draw_rowend = scrxpos[maskx + 1];
draw_linestart = scrypos[masky];
@ -250,28 +258,31 @@ static void F_DoWipe(fademask_t *fademask)
relativepos = (draw_linestart * vid.width) + draw_rowstart;
draw_linestogo = draw_lineend - draw_linestart;
if (*mask == 0)
if (reverse)
m = ((pallen-1) - m);
if (m == 0)
{
// shortcut - memcpy source to work
while (draw_linestogo--)
{
M_Memcpy(w_base+relativepos, s_base+relativepos, draw_rowend-draw_rowstart);
M_Memcpy(w_base+relativepos, (reverse ? e_base : s_base)+relativepos, draw_rowend-draw_rowstart);
relativepos += vid.width;
}
}
else if (*mask == 10)
else if (m >= (pallen-1))
{
// shortcut - memcpy target to work
while (draw_linestogo--)
{
M_Memcpy(w_base+relativepos, e_base+relativepos, draw_rowend-draw_rowstart);
M_Memcpy(w_base+relativepos, (reverse ? s_base : e_base)+relativepos, draw_rowend-draw_rowstart);
relativepos += vid.width;
}
}
else
{
// pointer to transtable that this mask would use
transtbl = transtables + ((9 - *mask)<<FF_TRANSSHIFT);
transtbl = transtables + ((9 - m)<<FF_TRANSSHIFT);
// DRAWING LOOP
while (draw_linestogo--)
@ -282,7 +293,17 @@ static void F_DoWipe(fademask_t *fademask)
draw_rowstogo = draw_rowend - draw_rowstart;
while (draw_rowstogo--)
*w++ = transtbl[ ( *e++ << 8 ) + *s++ ];
{
if (fadecolormap != NULL)
{
if (reverse)
*w++ = fadecolormap[ ( m << 8 ) + *e++ ];
else
*w++ = fadecolormap[ ( m << 8 ) + *s++ ];
}
else
*w++ = transtbl[ ( *e++ << 8 ) + *s++ ];
}
relativepos += vid.width;
}
@ -334,20 +355,86 @@ void F_WipeEndScreen(void)
#endif
}
/** Wiggle post processor for encore wipes
*/
static void F_DoEncoreWiggle(UINT8 time)
{
UINT8 *tmpscr = wipe_scr_start;
UINT8 *srcscr = wipe_scr;
angle_t disStart = (time * 128) & FINEMASK;
INT32 y, sine, newpix, scanline;
for (y = 0; y < vid.height; y++)
{
sine = (FINESINE(disStart) * (time*12))>>FRACBITS;
scanline = y / vid.dupy;
if (scanline & 1)
sine = -sine;
newpix = abs(sine);
if (sine < 0)
{
M_Memcpy(&tmpscr[(y*vid.width)+newpix], &srcscr[(y*vid.width)], vid.width-newpix);
// Cleanup edge
while (newpix)
{
tmpscr[(y*vid.width)+newpix] = srcscr[(y*vid.width)];
newpix--;
}
}
else
{
M_Memcpy(&tmpscr[(y*vid.width)], &srcscr[(y*vid.width) + sine], vid.width-newpix);
// Cleanup edge
while (newpix)
{
tmpscr[(y*vid.width) + vid.width - newpix] = srcscr[(y*vid.width) + (vid.width-1)];
newpix--;
}
}
disStart += (time*8); //the offset into the displacement map, increment each game loop
disStart &= FINEMASK; //clip it to FINEMASK
}
}
/** After setting up the screens you want to wipe,
* calling this will do a 'typical' wipe.
*/
void F_RunWipe(UINT8 wipetype, boolean drawMenu)
void F_RunWipe(UINT8 wipetype, boolean drawMenu, const char *colormap, boolean reverse, boolean encorewiggle)
{
#ifdef NOWIPE
(void)wipetype;
(void)drawMenu;
(void)colormap;
(void)reverse;
(void)encorewiggle;
#else
tic_t nowtime;
UINT8 wipeframe = 0;
fademask_t *fmask;
paldiv = FixedDiv(257<<FRACBITS, 11<<FRACBITS);
lumpnum_t clump = LUMPERROR;
lighttable_t *fcolor = NULL;
if (colormap != NULL)
clump = W_GetNumForName(colormap);
if (clump != LUMPERROR && wipetype != UINT8_MAX)
{
pallen = 32;
fcolor = Z_MallocAlign((256 * pallen), PU_STATIC, NULL, 8);
W_ReadLump(clump, fcolor);
}
else
{
pallen = 11;
reverse = false;
}
paldiv = FixedDiv(257<<FRACBITS, pallen<<FRACBITS);
// Init the wipe
WipeInAction = true;
@ -372,7 +459,16 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu)
HWR_DoWipe(wipetype, wipeframe-1); // send in the wipe type and wipeframe because we need to cache the graphic
else
#endif
F_DoWipe(fmask);
F_DoWipe(fmask, fcolor, reverse);
if (encorewiggle)
{
#ifdef HWRENDER
if (rendermode != render_opengl)
#endif
F_DoEncoreWiggle(wipeframe);
}
I_OsPolling();
I_UpdateNoBlit();
@ -386,6 +482,13 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu)
NetKeepAlive(); // Update the network so we don't cause timeouts
}
WipeInAction = false;
if (fcolor)
{
Z_Free(fcolor);
fcolor = NULL;
}
#endif
}

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;
@ -261,13 +262,15 @@ INT16 votelevels[5][2]; // Levels that were rolled by the host
SINT8 votes[MAXPLAYERS]; // Each player's vote
SINT8 pickedvote; // What vote the host rolls
// Battle overtime system
struct battleovertime battleovertime;
// Server-sided, synched variables
SINT8 battlewanted[4]; // WANTED players in battle, worth x2 points
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
@ -2337,7 +2340,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++)
@ -2369,21 +2372,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
}
@ -3042,8 +3031,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
@ -3051,8 +3049,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++;
}
}
}
}
@ -3382,9 +3393,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))
@ -3392,25 +3404,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
@ -3879,7 +3899,7 @@ void G_NextLevel(void)
}
forceresetplayers = false;
deferencoremode = (boolean)cv_kartencore.value;
deferencoremode = (cv_kartencore.value == 1);
}
gameaction = ga_worlddone;
@ -4042,9 +4062,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.
@ -4075,6 +4099,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!
@ -4220,6 +4247,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);
@ -4732,7 +4762,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!
@ -4743,12 +4773,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
@ -4890,7 +4914,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);
@ -5608,16 +5631,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"));
@ -5645,10 +5661,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
@ -5672,9 +5684,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++;
@ -5694,18 +5703,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);
@ -5846,15 +5849,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)
@ -6364,20 +6360,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());
@ -6417,6 +6408,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);
@ -6515,18 +6509,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)
@ -6769,13 +6758,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);
@ -6867,12 +6849,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);
@ -6907,16 +6883,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);
@ -7117,10 +7083,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);
@ -7149,24 +7111,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.
@ -7224,9 +7168,6 @@ void G_DoPlayDemo(char *defdemoname)
return;
}
}
#ifdef DEMO_COMPAT_100
}
#endif
modeattacking = (demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT;
multiplayer = !!(demoflags & DF_MULTIPLAYER);
@ -7254,109 +7195,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);
@ -7471,6 +7323,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);
@ -7495,10 +7350,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.
@ -7589,10 +7440,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);
@ -7633,15 +7480,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
@ -7657,41 +7498,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
@ -7711,10 +7517,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);
@ -7736,6 +7538,7 @@ void G_AddGhost(char *defdemoname)
p += 16;
p += 4; // score
p += 2; // powerlevel
kartspeed = READUINT8(p);
kartweight = READUINT8(p);
@ -7747,9 +7550,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))
@ -7849,18 +7649,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
@ -7880,43 +7675,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
@ -8001,10 +7775,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

@ -3017,7 +3017,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

@ -70,7 +70,7 @@ char sprnames[NUMSPRITES + 1][5] =
"DUCK","GTRE","CHES","CHIM","DRGN","LZMN","PGSS","ZTCH","MKMA","MKMP",
"RTCH","BOWL","BOWH","BRRL","BRRR","HRSE","TOAH","BFRT","OFRT","RFRT",
"PFRT","ASPK","HBST","HBSO","HBSF","WBLZ","WBLN","FWRK","MXCL","RGSP",
"DRAF","GRES","XMS4","XMS5","VIEW"
"DRAF","GRES","OTFG","XMS4","XMS5","VIEW"
};
// Doesn't work with g++, needs actionf_p1 (don't modify this comment)
@ -3454,6 +3454,10 @@ state_t states[NUMSTATES] =
{SPR_GRES, FF_ANIMATE|FF_PAPERSPRITE, -1, {NULL}, 2, 4, S_NULL}, // S_TIREGREASE
{SPR_OTFG, FF_FULLBRIGHT|FF_TRANS50, TICRATE, {NULL}, 0, 0, S_NULL}, // S_OVERTIMEFOG
{SPR_OTFG, 2|FF_FULLBRIGHT|FF_PAPERSPRITE, 1, {NULL}, 0, 0, S_NULL}, // S_OVERTIMEORB
{SPR_OTFG, 1|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_NULL}, // S_OVERTIMEBEAM
#ifdef SEENAMES
{SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK
#endif
@ -20300,6 +20304,87 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
{ // MT_OVERTIMEFOG
-1, // doomednum
S_OVERTIMEFOG, // 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_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
16<<FRACBITS, // radius
32<<FRACBITS, // height
-1, // display offset
100, // mass
0, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_OVERTIMEORB
-1, // doomednum
S_OVERTIMEORB, // 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_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
16<<FRACBITS, // radius
48<<FRACBITS, // height
-1, // display offset
100, // mass
0, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_OVERTIMEBEAM
-1, // doomednum
S_OVERTIMEBEAM, // 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_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
48<<FRACBITS, // radius
48<<FRACBITS, // height
-1, // display offset
100, // mass
0, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
// ============================================================================================================================//
#ifdef SEENAMES

View file

@ -784,6 +784,8 @@ typedef enum sprite
SPR_DRAF,
SPR_GRES,
SPR_OTFG,
// Xmas-specific sprites that don't fit aboxe
SPR_XMS4,
SPR_XMS5,
@ -4116,6 +4118,10 @@ typedef enum state
S_TIREGREASE,
S_OVERTIMEFOG,
S_OVERTIMEORB,
S_OVERTIMEBEAM,
#ifdef SEENAMES
S_NAMECHECK,
#endif
@ -4927,6 +4933,10 @@ typedef enum mobj_type
MT_DRAFTDUST,
MT_TIREGREASE,
MT_OVERTIMEFOG,
MT_OVERTIMEORB,
MT_OVERTIMEBEAM,
#ifdef SEENAMES
MT_NAMECHECK,
#endif

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
@ -33,8 +34,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
@ -582,6 +581,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);
@ -5140,6 +5140,25 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
player->mo->colorized = false;
player->mo->color = player->skincolor;
}
}
else if (player->kartstuff[k_killfield]) // You're gonna REALLY diiiiie
{
const INT32 flashtime = 4<<(4-(player->kartstuff[k_killfield]/TICRATE));
if (player->kartstuff[k_killfield] == 1 || (player->kartstuff[k_killfield] % (flashtime/2) != 0))
{
player->mo->colorized = false;
player->mo->color = player->skincolor;
}
else if (player->kartstuff[k_killfield] % flashtime == 0)
{
player->mo->colorized = true;
player->mo->color = SKINCOLOR_BYZANTIUM;
}
else
{
player->mo->colorized = true;
player->mo->color = SKINCOLOR_RUBY;
}
}
else if (player->kartstuff[k_ringboost] && (leveltime & 1)) // ring boosting
{
@ -5312,8 +5331,28 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
K_KartPlayerHUDUpdate(player);
if (G_BattleGametype() && player->kartstuff[k_bumper] > 0)
player->kartstuff[k_wanted]++;
if (G_BattleGametype() && player->kartstuff[k_bumper] > 0
&& !player->kartstuff[k_spinouttimer] && !player->kartstuff[k_squishedtimer]
&& !player->kartstuff[k_respawn] && !player->powers[pw_flashing])
{
player->kartstuff[k_wanted]++;
if (battleovertime.enabled >= 10*TICRATE)
{
if (P_AproxDistance(player->mo->x - battleovertime.x, player->mo->y - battleovertime.y) > battleovertime.radius)
{
player->kartstuff[k_killfield]++;
if (player->kartstuff[k_killfield] > 4*TICRATE)
{
K_SpinPlayer(player, NULL, 0, NULL, false);
//player->kartstuff[k_killfield] = 1;
}
}
else if (player->kartstuff[k_killfield] > 0)
player->kartstuff[k_killfield]--;
}
}
else if (player->kartstuff[k_killfield] > 0)
player->kartstuff[k_killfield]--;
if (P_IsObjectOnGround(player->mo))
player->kartstuff[k_waterskip] = 0;
@ -6175,7 +6214,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
&& NO_HYUDORO && !(HOLDING_ITEM
|| player->kartstuff[k_itemamount]
|| player->kartstuff[k_itemroulette]
|| player->kartstuff[k_growshrinktimer] // Being disabled during Shrink was unintended but people seemed to be okay with it sooo...
|| player->kartstuff[k_growshrinktimer] > 0
|| player->kartstuff[k_rocketsneakertimer]
|| player->kartstuff[k_eggmanexplode]))
player->kartstuff[k_userings] = 1;
@ -7265,7 +7304,8 @@ static patch_t *kp_lapanim_number[10][3];
static patch_t *kp_lapanim_emblem[2];
static patch_t *kp_lapanim_hand[3];
static patch_t *kp_yougotem;
static patch_t *kp_yougotem;
static patch_t *kp_itemminimap;
void K_LoadKartHUDGraphics(void)
{
@ -7565,7 +7605,8 @@ void K_LoadKartHUDGraphics(void)
kp_lapanim_hand[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
}
kp_yougotem = (patch_t *) W_CachePatchName("YOUGOTEM", PU_HUDGFX);
kp_yougotem = (patch_t *) W_CachePatchName("YOUGOTEM", PU_HUDGFX);
kp_itemminimap = (patch_t *) W_CachePatchName("MMAPITEM", PU_HUDGFX);
}
// For the item toggle menu
@ -9089,229 +9130,279 @@ static void K_drawKartPlayerCheck(void)
V_DrawMappedPatch(x, CHEK_Y, V_HUDTRANS|splitflags, kp_check[pnum], colormap);
}
}
}
}
static void K_drawKartMinimapHead(mobj_t *mo, INT32 x, INT32 y, INT32 flags, patch_t *AutomapPic)
{
// amnum xpos & ypos are the icon's speed around the HUD.
// The number being divided by is for how fast it moves.
// The higher the number, the slower it moves.
static void K_drawKartMinimapIcon(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hudy, INT32 flags, patch_t *icon, UINT8 *colormap, patch_t *AutomapPic)
{
// amnum xpos & ypos are the icon's speed around the HUD.
// The number being divided by is for how fast it moves.
// The higher the number, the slower it moves.
// am xpos & ypos are the icon's starting position. Withouht
// it, they wouldn't 'spawn' on the top-right side of the HUD.
fixed_t amnumxpos, amnumypos;
INT32 amxpos, amypos;
node_t *bsp = &nodes[numnodes-1];
fixed_t maxx, minx, maxy, miny;
fixed_t mapwidth, mapheight;
fixed_t xoffset, yoffset;
fixed_t xscale, yscale, zoom;
maxx = maxy = INT32_MAX;
minx = miny = INT32_MIN;
minx = bsp->bbox[0][BOXLEFT];
maxx = bsp->bbox[0][BOXRIGHT];
miny = bsp->bbox[0][BOXBOTTOM];
maxy = bsp->bbox[0][BOXTOP];
if (bsp->bbox[1][BOXLEFT] < minx)
minx = bsp->bbox[1][BOXLEFT];
if (bsp->bbox[1][BOXRIGHT] > maxx)
maxx = bsp->bbox[1][BOXRIGHT];
if (bsp->bbox[1][BOXBOTTOM] < miny)
miny = bsp->bbox[1][BOXBOTTOM];
if (bsp->bbox[1][BOXTOP] > maxy)
maxy = bsp->bbox[1][BOXTOP];
// You might be wondering why these are being bitshift here
// it's because mapwidth and height would otherwise overflow for maps larger than half the size possible...
// map boundaries and sizes will ALWAYS be whole numbers thankfully
// later calculations take into consideration that these are actually not in terms of FRACUNIT though
minx >>= FRACBITS;
maxx >>= FRACBITS;
miny >>= FRACBITS;
maxy >>= FRACBITS;
mapwidth = maxx - minx;
mapheight = maxy - miny;
// These should always be small enough to be bitshift back right now
xoffset = (minx + mapwidth/2)<<FRACBITS;
yoffset = (miny + mapheight/2)<<FRACBITS;
xscale = FixedDiv(AutomapPic->width, mapwidth);
yscale = FixedDiv(AutomapPic->height, mapheight);
zoom = FixedMul(min(xscale, yscale), FRACUNIT-FRACUNIT/20);
amnumxpos = (FixedMul(objx, zoom) - FixedMul(xoffset, zoom));
amnumypos = -(FixedMul(objy, zoom) - FixedMul(yoffset, zoom));
if (encoremode)
amnumxpos = -amnumxpos;
amxpos = amnumxpos + ((hudx + AutomapPic->width/2 - (icon->width/2))<<FRACBITS);
amypos = amnumypos + ((hudy + AutomapPic->height/2 - (icon->height/2))<<FRACBITS);
// do we want this? it feels unnecessary. easier to just modify the amnumxpos?
/*if (encoremode)
{
flags |= V_FLIP;
amxpos = -amnumxpos + ((hudx + AutomapPic->width/2 + (icon->width/2))<<FRACBITS);
}*/
V_DrawFixedPatch(amxpos, amypos, FRACUNIT, flags, icon, colormap);
}
static void K_drawKartMinimap(void)
{
INT32 lumpnum;
patch_t *AutomapPic;
INT32 i = 0;
INT32 x, y;
INT32 minimaptrans, splitflags = (splitscreen == 3 ? 0 : V_SNAPTORIGHT); // flags should only be 0 when it's centered (4p split)
UINT8 skin = 0;
UINT8 *colormap = NULL;
SINT8 localplayers[4];
SINT8 numlocalplayers = 0;
// Draw the HUD only when playing in a level.
// hu_stuff needs this, unlike st_stuff.
if (gamestate != GS_LEVEL)
return;
// am xpos & ypos are the icon's starting position. Withouht
// it, they wouldn't 'spawn' on the top-right side of the HUD.
// Only draw for the first player
// Maybe move this somewhere else where this won't be a concern?
if (stplyr != &players[displayplayers[0]])
return;
lumpnum = W_CheckNumForName(va("%sR", G_BuildMapName(gamemap)));
if (lumpnum != -1)
AutomapPic = W_CachePatchName(va("%sR", G_BuildMapName(gamemap)), PU_HUDGFX);
else
return; // no pic, just get outta here
x = MINI_X - (AutomapPic->width/2);
y = MINI_Y - (AutomapPic->height/2);
if (timeinmap > 105)
{
minimaptrans = cv_kartminimap.value;
if (timeinmap <= 113)
minimaptrans = ((((INT32)timeinmap) - 105)*minimaptrans)/(113-105);
if (!minimaptrans)
return;
}
else
return;
minimaptrans = ((10-minimaptrans)<<FF_TRANSSHIFT);
splitflags |= minimaptrans;
if (encoremode)
V_DrawScaledPatch(x+(AutomapPic->width), y, splitflags|V_FLIP, AutomapPic);
else
V_DrawScaledPatch(x, y, splitflags, AutomapPic);
if (!(splitscreen == 2))
{
splitflags &= ~minimaptrans;
splitflags |= V_HUDTRANSHALF;
}
// let offsets transfer to the heads, too!
if (encoremode)
x += SHORT(AutomapPic->leftoffset);
else
x -= SHORT(AutomapPic->leftoffset);
y -= SHORT(AutomapPic->topoffset);
// Draw the super item in Battle
if (G_BattleGametype() && battleovertime.enabled)
{
if (battleovertime.enabled >= 10*TICRATE || (battleovertime.enabled & 1))
{
const INT32 prevsplitflags = splitflags;
splitflags &= ~V_HUDTRANSHALF;
splitflags |= V_HUDTRANS;
colormap = R_GetTranslationColormap(TC_RAINBOW, (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))), GTC_CACHE);
K_drawKartMinimapIcon(battleovertime.x, battleovertime.y, x, y, splitflags, kp_itemminimap, colormap, AutomapPic);
splitflags = prevsplitflags;
}
}
// initialize
for (i = 0; i < 4; i++)
localplayers[i] = -1;
// Player's tiny icons on the Automap. (drawn opposite direction so player 1 is drawn last in splitscreen)
if (ghosts)
{
demoghost *g = ghosts;
while (g)
{
if (g->mo->skin)
skin = ((skin_t*)g->mo->skin)-skins;
else
skin = 0;
if (g->mo->color)
{
if (g->mo->colorized)
colormap = R_GetTranslationColormap(TC_RAINBOW, g->mo->color, GTC_CACHE);
else
colormap = R_GetTranslationColormap(skin, g->mo->color, GTC_CACHE);
}
else
colormap = NULL;
K_drawKartMinimapIcon(g->mo->x, g->mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic);
g = g->next;
}
if (!stplyr->mo || stplyr->spectator) // do we need the latter..?
return;
localplayers[numlocalplayers] = stplyr-players;
numlocalplayers++;
}
else
{
for (i = MAXPLAYERS-1; i >= 0; i--)
{
if (!playeringame[i])
continue;
if (!players[i].mo || players[i].spectator)
continue;
if (i != displayplayers[0] || splitscreen)
{
if (G_BattleGametype() && players[i].kartstuff[k_bumper] <= 0)
continue;
if (players[i].kartstuff[k_hyudorotimer] > 0)
{
if (!((players[i].kartstuff[k_hyudorotimer] < 1*TICRATE/2
|| players[i].kartstuff[k_hyudorotimer] > hyudorotime-(1*TICRATE/2))
&& !(leveltime & 1)))
continue;
}
}
if (i == displayplayers[0] || i == displayplayers[1] || i == displayplayers[2] || i == displayplayers[3])
{
// Draw display players on top of everything else
localplayers[numlocalplayers] = i;
numlocalplayers++;
continue;
}
if (players[i].mo->skin)
skin = ((skin_t*)players[i].mo->skin)-skins;
else
skin = 0;
if (players[i].mo->color)
{
if (players[i].mo->colorized)
colormap = R_GetTranslationColormap(TC_RAINBOW, players[i].mo->color, GTC_CACHE);
else
colormap = R_GetTranslationColormap(skin, players[i].mo->color, GTC_CACHE);
}
else
colormap = NULL;
K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic);
// Target reticule
if ((G_RaceGametype() && players[i].kartstuff[k_position] == spbplace)
|| (G_BattleGametype() && K_IsPlayerWanted(&players[i])))
K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic);
}
}
// draw our local players here, opaque.
splitflags &= ~V_HUDTRANSHALF;
splitflags |= V_HUDTRANS;
for (i = 0; i < numlocalplayers; i++)
{
if (i == -1)
continue; // this doesn't interest us
if (players[localplayers[i]].mo->skin)
skin = ((skin_t*)players[localplayers[i]].mo->skin)-skins;
else
skin = 0;
if (players[localplayers[i]].mo->color)
{
if (players[localplayers[i]].mo->colorized)
colormap = R_GetTranslationColormap(TC_RAINBOW, players[localplayers[i]].mo->color, GTC_CACHE);
else
colormap = R_GetTranslationColormap(skin, players[localplayers[i]].mo->color, GTC_CACHE);
}
else
colormap = NULL;
K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic);
UINT8 skin = 0;
fixed_t amnumxpos, amnumypos;
INT32 amxpos, amypos;
node_t *bsp = &nodes[numnodes-1];
fixed_t maxx, minx, maxy, miny;
fixed_t mapwidth, mapheight;
fixed_t xoffset, yoffset;
fixed_t xscale, yscale, zoom;
if (mo->skin)
skin = ((skin_t*)mo->skin)-skins;
maxx = maxy = INT32_MAX;
minx = miny = INT32_MIN;
minx = bsp->bbox[0][BOXLEFT];
maxx = bsp->bbox[0][BOXRIGHT];
miny = bsp->bbox[0][BOXBOTTOM];
maxy = bsp->bbox[0][BOXTOP];
if (bsp->bbox[1][BOXLEFT] < minx)
minx = bsp->bbox[1][BOXLEFT];
if (bsp->bbox[1][BOXRIGHT] > maxx)
maxx = bsp->bbox[1][BOXRIGHT];
if (bsp->bbox[1][BOXBOTTOM] < miny)
miny = bsp->bbox[1][BOXBOTTOM];
if (bsp->bbox[1][BOXTOP] > maxy)
maxy = bsp->bbox[1][BOXTOP];
// You might be wondering why these are being bitshift here
// it's because mapwidth and height would otherwise overflow for maps larger than half the size possible...
// map boundaries and sizes will ALWAYS be whole numbers thankfully
// later calculations take into consideration that these are actually not in terms of FRACUNIT though
minx >>= FRACBITS;
maxx >>= FRACBITS;
miny >>= FRACBITS;
maxy >>= FRACBITS;
mapwidth = maxx - minx;
mapheight = maxy - miny;
// These should always be small enough to be bitshift back right now
xoffset = (minx + mapwidth/2)<<FRACBITS;
yoffset = (miny + mapheight/2)<<FRACBITS;
xscale = FixedDiv(AutomapPic->width, mapwidth);
yscale = FixedDiv(AutomapPic->height, mapheight);
zoom = FixedMul(min(xscale, yscale), FRACUNIT-FRACUNIT/20);
amnumxpos = (FixedMul(mo->x, zoom) - FixedMul(xoffset, zoom));
amnumypos = -(FixedMul(mo->y, zoom) - FixedMul(yoffset, zoom));
if (encoremode)
amnumxpos = -amnumxpos;
amxpos = amnumxpos + ((x + AutomapPic->width/2 - (facemmapprefix[skin]->width/2))<<FRACBITS);
amypos = amnumypos + ((y + AutomapPic->height/2 - (facemmapprefix[skin]->height/2))<<FRACBITS);
// do we want this? it feels unnecessary. easier to just modify the amnumxpos?
/*if (encoremode)
{
flags |= V_FLIP;
amxpos = -amnumxpos + ((x + AutomapPic->width/2 + (facemmapprefix[skin]->width/2))<<FRACBITS);
}*/
if (!mo->color) // 'default' color
V_DrawSciencePatch(amxpos, amypos, flags, facemmapprefix[skin], FRACUNIT);
else
{
UINT8 *colormap;
if (mo->colorized)
colormap = R_GetTranslationColormap(TC_RAINBOW, mo->color, GTC_CACHE);
else
colormap = R_GetTranslationColormap(skin, mo->color, GTC_CACHE);
V_DrawFixedPatch(amxpos, amypos, FRACUNIT, flags, facemmapprefix[skin], colormap);
if (mo->player
&& ((G_RaceGametype() && mo->player->kartstuff[k_position] == spbplace)
|| (G_BattleGametype() && K_IsPlayerWanted(mo->player))))
{
V_DrawFixedPatch(amxpos - (4<<FRACBITS), amypos - (4<<FRACBITS), FRACUNIT, flags, kp_wantedreticle, NULL);
}
}
}
static void K_drawKartMinimap(void)
{
INT32 lumpnum;
patch_t *AutomapPic;
INT32 i = 0;
INT32 x, y;
INT32 minimaptrans, splitflags = (splitscreen == 3 ? 0 : V_SNAPTORIGHT); // flags should only be 0 when it's centered (4p split)
SINT8 localplayers[4];
SINT8 numlocalplayers = 0;
// Draw the HUD only when playing in a level.
// hu_stuff needs this, unlike st_stuff.
if (gamestate != GS_LEVEL)
return;
if (stplyr != &players[displayplayers[0]])
return;
lumpnum = W_CheckNumForName(va("%sR", G_BuildMapName(gamemap)));
if (lumpnum != -1)
AutomapPic = W_CachePatchName(va("%sR", G_BuildMapName(gamemap)), PU_HUDGFX);
else
return; // no pic, just get outta here
x = MINI_X - (AutomapPic->width/2);
y = MINI_Y - (AutomapPic->height/2);
if (timeinmap > 105)
{
minimaptrans = cv_kartminimap.value;
if (timeinmap <= 113)
minimaptrans = ((((INT32)timeinmap) - 105)*minimaptrans)/(113-105);
if (!minimaptrans)
return;
}
else
return;
minimaptrans = ((10-minimaptrans)<<FF_TRANSSHIFT);
splitflags |= minimaptrans;
if (encoremode)
V_DrawScaledPatch(x+(AutomapPic->width), y, splitflags|V_FLIP, AutomapPic);
else
V_DrawScaledPatch(x, y, splitflags, AutomapPic);
if (!(splitscreen == 2))
{
splitflags &= ~minimaptrans;
splitflags |= V_HUDTRANSHALF;
}
// let offsets transfer to the heads, too!
if (encoremode)
x += SHORT(AutomapPic->leftoffset);
else
x -= SHORT(AutomapPic->leftoffset);
y -= SHORT(AutomapPic->topoffset);
// initialize
for (i = 0; i < 4; i++)
localplayers[i] = -1;
// Player's tiny icons on the Automap. (drawn opposite direction so player 1 is drawn last in splitscreen)
if (ghosts)
{
demoghost *g = ghosts;
while (g)
{
K_drawKartMinimapHead(g->mo, x, y, splitflags, AutomapPic);
g = g->next;
}
if (!stplyr->mo || stplyr->spectator) // do we need the latter..?
return;
localplayers[numlocalplayers] = stplyr-players;
numlocalplayers++;
}
else
{
for (i = MAXPLAYERS-1; i >= 0; i--)
{
if (!playeringame[i])
continue;
if (!players[i].mo || players[i].spectator)
continue;
if (i != displayplayers[0] || splitscreen)
{
if (G_BattleGametype() && players[i].kartstuff[k_bumper] <= 0)
continue;
if (players[i].kartstuff[k_hyudorotimer] > 0)
{
if (!((players[i].kartstuff[k_hyudorotimer] < 1*TICRATE/2
|| players[i].kartstuff[k_hyudorotimer] > hyudorotime-(1*TICRATE/2))
&& !(leveltime & 1)))
continue;
}
}
if (P_IsDisplayPlayer(&players[i]))
{
// Draw display players on top of everything else
localplayers[numlocalplayers] = i;
numlocalplayers++;
continue;
}
K_drawKartMinimapHead(players[i].mo, x, y, splitflags, AutomapPic);
}
}
// draw our local players here, opaque.
splitflags &= ~V_HUDTRANSHALF;
splitflags |= V_HUDTRANS;
for (i = 0; i < numlocalplayers; i++)
{
if (i == -1)
continue; // this doesn't interest us
K_drawKartMinimapHead(players[localplayers[i]].mo, x, y, splitflags, AutomapPic);
}
}
// Target reticule
if ((G_RaceGametype() && players[localplayers[i]].kartstuff[k_position] == spbplace)
|| (G_BattleGametype() && K_IsPlayerWanted(&players[localplayers[i]])))
K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic);
}
}
static void K_drawKartStartCountdown(void)
{

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

@ -422,7 +422,7 @@ static void GIF_headwrite(void)
WRITEUINT16(p, rheight);
// colors, aspect, etc
WRITEUINT8(p, 0xF7);
WRITEUINT8(p, 0xFF); // TRANSPARENTPIXEL
WRITEUINT8(p, 0x00);
WRITEUINT8(p, 0x00);

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

@ -57,6 +57,7 @@
#include "st_stuff.h"
#include "i_sound.h"
#include "k_kart.h" // SRB2kart
#include "k_pwrlv.h"
#include "d_player.h" // KITEM_ constants
#include "i_joy.h" // for joystick menu controls
@ -89,6 +90,10 @@ int snprintf(char *str, size_t n, const char *fmt, ...);
#define SLIDER_WIDTH (8*SLIDER_RANGE+6)
#define SERVERS_PER_PAGE 11
#if defined (NONET) || defined (TESTERS)
#define NOMENUHOST
#endif
typedef enum
{
QUITMSG = 0,
@ -241,14 +246,18 @@ static menu_t SP_TimeAttackDef, SP_ReplayDef, SP_GuestReplayDef, SP_GhostDef;
// Multiplayer
#ifndef NONET
#ifndef TESTERS
static void M_StartServerMenu(INT32 choice);
#endif
static void M_ConnectMenu(INT32 choice);
static void M_ConnectMenuModChecks(INT32 choice);
static void M_Refresh(INT32 choice);
static void M_Connect(INT32 choice);
static void M_ChooseRoom(INT32 choice);
#endif
#ifndef TESTERS
static void M_StartOfflineServerMenu(INT32 choice);
#endif
static void M_StartServer(INT32 choice);
static void M_SetupMultiPlayer(INT32 choice);
static void M_SetupMultiPlayer2(INT32 choice);
@ -474,9 +483,14 @@ static menuitem_t MainMenu[] =
{
{IT_SUBMENU|IT_STRING, NULL, "Extras", &SR_MainDef, 76},
//{IT_CALL |IT_STRING, NULL, "1 Player", M_SinglePlayerMenu, 84},
#ifdef TESTERS
{IT_GRAYEDOUT, NULL, "Time Attack", NULL, 84},
#else
{IT_CALL |IT_STRING, NULL, "Time Attack", M_TimeAttack, 84},
#endif
{IT_SUBMENU|IT_STRING, NULL, "Multiplayer", &MP_MainDef, 92},
{IT_CALL |IT_STRING, NULL, "Options", M_Options, 100},
/* I don't think is useful at all... */
{IT_CALL |IT_STRING, NULL, "Addons", M_Addons, 108},
{IT_CALL |IT_STRING, NULL, "Quit Game", M_QuitSRB2, 116},
};
@ -727,7 +741,9 @@ static menuitem_t SR_PandorasBox[] =
// Sky Room Custom Unlocks
static menuitem_t SR_MainMenu[] =
{
#ifndef TESTERS
{IT_STRING|IT_SUBMENU, NULL, "Unlockables", &SR_UnlockChecklistDef, 100},
#endif
{IT_CALL|IT_STRING|IT_CALL_NOTMODIFIED, NULL, "Statistics", M_Statistics, 108},
{IT_CALL|IT_STRING, NULL, "Replay Hut", M_ReplayHut, 116},
{IT_DISABLED, NULL, "", NULL, 0}, // Custom1
@ -961,12 +977,16 @@ static menuitem_t MP_MainMenu[] =
{IT_STRING|IT_KEYHANDLER,NULL, "Player setup...", M_SetupMultiHandler,18},
{IT_HEADER, NULL, "Host a game", NULL, 100-24},
#ifndef NONET
#ifndef NOMENUHOST
{IT_STRING|IT_CALL, NULL, "Internet/LAN...", M_StartServerMenu, 110-24},
#else
{IT_GRAYEDOUT, NULL, "Internet/LAN...", NULL, 110-24},
#endif
#ifdef TESTERS
{IT_GRAYEDOUT, NULL, "Offline...", NULL, 118-24},
#else
{IT_STRING|IT_CALL, NULL, "Offline...", M_StartOfflineServerMenu, 118-24},
#endif
{IT_HEADER, NULL, "Join a game", NULL, 132-24},
#ifndef NONET
@ -1482,8 +1502,7 @@ static menuitem_t OP_GameOptionsMenu[] =
{IT_STRING | IT_CVAR, NULL, "Starting Bumpers", &cv_kartbumpers, 110},
{IT_STRING | IT_CVAR, NULL, "Karma Comeback", &cv_kartcomeback, 120},
{IT_STRING | IT_CVAR, NULL, "Force Character", &cv_forceskin, 140},
{IT_STRING | IT_CVAR, NULL, "Restrict Character Changes", &cv_restrictskinchange, 150},
{IT_STRING | IT_CVAR, NULL, "Track Power Levels", &cv_kartusepwrlv, 140},
};
static menuitem_t OP_ServerOptionsMenu[] =
@ -3029,7 +3048,11 @@ void M_StartControlPanel(void)
//MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
currentMenu = &MainDef;
#ifdef TESTERS
itemOn = multiplr;
#else
itemOn = singleplr;
#endif
}
else if (modeattacking)
{
@ -4092,6 +4115,14 @@ static void M_DrawCenteredMenu(void)
W_CachePatchName(currentMenu->menuitems[i].patch,PU_CACHE), graymap);
y += LINEHEIGHT;
break;
case IT_TRANSTEXT:
if (currentMenu->menuitems[i].alphaKey)
y = currentMenu->y+currentMenu->menuitems[i].alphaKey;
/* FALLTHRU */
case IT_TRANSTEXT2:
V_DrawCenteredString(x, y, V_TRANSLUCENT, currentMenu->menuitems[i].text);
y += SMALLLINEHEIGHT;
break;
}
}
@ -6210,6 +6241,8 @@ static char *M_GetConditionString(condition_t cond)
G_TicsToSeconds(cond.requirement));
case UC_MATCHESPLAYED:
return va("Play %d matches", cond.requirement);
case UC_POWERLEVEL:
return va("Reach power level %d in %s", cond.requirement, (cond.extrainfo1 == PWRLV_BATTLE ? "Battle" : "Race"));
case UC_GAMECLEAR:
if (cond.requirement > 1)
return va("Beat game %d times", cond.requirement);
@ -7294,7 +7327,7 @@ static void M_Statistics(INT32 choice)
static void M_DrawStatsMaps(int location)
{
INT32 y = 80, i = -1;
INT32 y = 88, i = -1;
INT16 mnum;
extraemblem_t *exemblem;
boolean dotopname = true, dobottomarrow = (location < statsMax);
@ -7403,9 +7436,14 @@ static void M_DrawLevelStats(void)
G_TicsToHours(totalplaytime),
G_TicsToMinutes(totalplaytime, false),
G_TicsToSeconds(totalplaytime)));
V_DrawString(20, 42, highlightflags, "Total Matches:");
V_DrawRightAlignedString(BASEVIDWIDTH-16, 42, 0, va("%i played", matchesplayed));
V_DrawString(20, 52, highlightflags, "Online Power Level:");
V_DrawRightAlignedString(BASEVIDWIDTH-16, 52, 0, va("Race: %i", vspowerlevel[PWRLV_RACE]));
V_DrawRightAlignedString(BASEVIDWIDTH-16, 60, 0, va("Battle: %i", vspowerlevel[PWRLV_BATTLE]));
for (i = 0; i < NUMMAPS; i++)
{
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
@ -7420,18 +7458,18 @@ static void M_DrawLevelStats(void)
besttime += mainrecords[i]->time;
}
V_DrawString(20, 62, highlightflags, "Combined time records:");
V_DrawString(20, 70, highlightflags, "Combined time records:");
sprintf(beststr, "%i:%02i:%02i.%02i", G_TicsToHours(besttime), G_TicsToMinutes(besttime, false), G_TicsToSeconds(besttime), G_TicsToCentiseconds(besttime));
V_DrawRightAlignedString(BASEVIDWIDTH-16, 62, (mapsunfinished ? warningflags : 0), beststr);
V_DrawRightAlignedString(BASEVIDWIDTH-16, 70, (mapsunfinished ? warningflags : 0), beststr);
if (mapsunfinished)
V_DrawRightAlignedString(BASEVIDWIDTH-16, 70, warningflags, va("(%d unfinished)", mapsunfinished));
V_DrawRightAlignedString(BASEVIDWIDTH-16, 78, warningflags, va("(%d unfinished)", mapsunfinished));
else
V_DrawRightAlignedString(BASEVIDWIDTH-16, 70, recommendedflags, "(complete)");
V_DrawRightAlignedString(BASEVIDWIDTH-16, 78, recommendedflags, "(complete)");
V_DrawString(32, 70, 0, va("x %d/%d", M_CountEmblems(), numemblems+numextraemblems));
V_DrawSmallScaledPatch(20, 70, 0, W_CachePatchName("GOTITA", PU_STATIC));
V_DrawString(32, 78, V_ALLOWLOWERCASE, va("x %d/%d", M_CountEmblems(), numemblems+numextraemblems));
V_DrawSmallScaledPatch(20, 78, 0, W_CachePatchName("GOTITA", PU_STATIC));
M_DrawStatsMaps(statsLocation);
}
@ -8261,6 +8299,7 @@ static void M_DrawConnectMenu(void)
UINT16 i;
const char *gt = "Unknown";
const char *spd = "";
const char *pwr = "----";
INT32 numPages = (serverlistcount+(SERVERS_PER_PAGE-1))/SERVERS_PER_PAGE;
for (i = FIRSTSERVERLINE; i < min(localservercount, SERVERS_PER_PAGE)+FIRSTSERVERLINE; i++)
@ -8295,33 +8334,38 @@ static void M_DrawConnectMenu(void)
V_DrawString(currentMenu->x, S_LINEY(i), globalflags, serverlist[slindex].info.servername);
// Don't use color flags intentionally, the global yellow color will auto override the text color code
if (serverlist[slindex].info.modifiedgame)
V_DrawSmallString(currentMenu->x+202, S_LINEY(i)+8, globalflags, "\x85" "Mod");
if (serverlist[slindex].info.cheatsenabled)
V_DrawSmallString(currentMenu->x+222, S_LINEY(i)+8, globalflags, "\x83" "Cheats");
if (serverlist[slindex].info.kartvars & SV_PASSWORD)
V_DrawFixedPatch((currentMenu->x - 9) << FRACBITS, (S_LINEY(i)) << FRACBITS, FRACUNIT, globalflags & (~V_ALLOWLOWERCASE), W_CachePatchName("SERVLOCK", PU_CACHE), NULL);
V_DrawSmallString(currentMenu->x, S_LINEY(i)+8, globalflags,
va("Ping: %u", (UINT32)LONG(serverlist[slindex].info.time)));
V_DrawSmallString(currentMenu->x+44,S_LINEY(i)+8, globalflags,
va("Players: %02d/%02d", serverlist[slindex].info.numberofplayer, serverlist[slindex].info.maxplayer));
gt = "Unknown";
if (serverlist[slindex].info.gametype < NUMGAMETYPES)
gt = Gametype_Names[serverlist[slindex].info.gametype];
V_DrawSmallString(currentMenu->x+46,S_LINEY(i)+8, globalflags,
va("Players: %02d/%02d", serverlist[slindex].info.numberofplayer, serverlist[slindex].info.maxplayer));
V_DrawSmallString(currentMenu->x+112, S_LINEY(i)+8, globalflags, gt);
V_DrawSmallString(currentMenu->x+108, S_LINEY(i)+8, globalflags, gt);
if (serverlist[slindex].info.gametype == GT_RACE)
{
spd = kartspeed_cons_t[serverlist[slindex].info.kartvars & SV_SPEEDMASK].strvalue;
V_DrawSmallString(currentMenu->x+132, S_LINEY(i)+8, globalflags, va("(%s Speed)", spd));
V_DrawSmallString(currentMenu->x+128, S_LINEY(i)+8, globalflags, va("(%s)", spd));
}
if (serverlist[slindex].info.kartvars & SV_PASSWORD)
V_DrawFixedPatch((currentMenu->x - 9) << FRACBITS, (S_LINEY(i)) << FRACBITS, FRACUNIT, globalflags & (~V_ALLOWLOWERCASE), W_CachePatchName("SERVLOCK", PU_CACHE), NULL);
pwr = "----";
if (serverlist[slindex].info.avgpwrlv == -1)
pwr = "Off";
else if (serverlist[slindex].info.avgpwrlv > 0)
pwr = va("%04d", serverlist[slindex].info.avgpwrlv);
V_DrawSmallString(currentMenu->x+171, S_LINEY(i)+8, globalflags, va("Power Level: %s", pwr));
// Don't use color flags intentionally, the global yellow color will auto override the text color code
if (serverlist[slindex].info.modifiedgame)
V_DrawSmallString(currentMenu->x+245, S_LINEY(i)+8, globalflags, "\x85" "Mod");
if (serverlist[slindex].info.cheatsenabled)
V_DrawSmallString(currentMenu->x+265, S_LINEY(i)+8, globalflags, "\x83" "Cheats");
MP_ConnectMenu[i+FIRSTSERVERLINE].status = IT_STRING | IT_CALL;
}
@ -8600,11 +8644,11 @@ static void M_StartServer(INT32 choice)
paused = false;
SV_StartSinglePlayerServer();
multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this...
D_MapChange(cv_nextmap.value, cv_newgametype.value, (boolean)cv_kartencore.value, 1, 1, false, false);
D_MapChange(cv_nextmap.value, cv_newgametype.value, (cv_kartencore.value == 1), 1, 1, false, false);
}
else
{
D_MapChange(cv_nextmap.value, cv_newgametype.value, (boolean)cv_kartencore.value, 1, 1, false, false);
D_MapChange(cv_nextmap.value, cv_newgametype.value, (cv_kartencore.value == 1), 1, 1, false, false);
COM_BufAddText("dummyconsvar 1\n");
}
@ -8641,7 +8685,7 @@ static void M_DrawLevelSelectOnly(boolean leftfade, boolean rightfade)
V_DrawFill(x-1, y-1, w+2, i+2, trans); // variable reuse...
if (!cv_kartencore.value || gamestate == GS_TIMEATTACK || cv_newgametype.value != GT_RACE)
if ((cv_kartencore.value != 1) || gamestate == GS_TIMEATTACK || cv_newgametype.value != GT_RACE)
V_DrawSmallScaledPatch(x, y, 0, PictureOfLevel);
else
{
@ -8774,6 +8818,7 @@ static void M_MapChange(INT32 choice)
M_SetupNextMenu(&MISC_ChangeLevelDef);
}
#ifndef TESTERS
static void M_StartOfflineServerMenu(INT32 choice)
{
(void)choice;
@ -8781,8 +8826,10 @@ static void M_StartOfflineServerMenu(INT32 choice)
M_PrepareLevelSelect();
M_SetupNextMenu(&MP_OfflineServerDef);
}
#endif
#ifndef NONET
#ifndef TESTERS
static void M_StartServerMenu(INT32 choice)
{
(void)choice;
@ -8792,6 +8839,7 @@ static void M_StartServerMenu(INT32 choice)
M_SetupNextMenu(&MP_ServerDef);
}
#endif
// ==============
// CONNECT VIA IP
@ -8811,7 +8859,7 @@ static void M_DrawMPMainMenu(void)
// use generic drawer for cursor, items and title
M_DrawGenericMenu();
#ifndef NONET
#ifndef NOMENUHOST
#if MAXPLAYERS != 16
Update the maxplayers label...
#endif
@ -8819,10 +8867,12 @@ Update the maxplayers label...
((itemOn == 4) ? highlightflags : 0), "(2-16 players)");
#endif
#ifndef TESTERS
V_DrawRightAlignedString(BASEVIDWIDTH-x, y+MP_MainMenu[5].alphaKey,
((itemOn == 5) ? highlightflags : 0),
"(2-4 players)"
);
#endif
#ifndef NONET
y += MP_MainMenu[8].alphaKey;
@ -9583,6 +9633,8 @@ static UINT8 erasecontext = 0;
static void M_EraseDataResponse(INT32 ch)
{
UINT8 i;
if (ch != 'y' && ch != KEY_ENTER)
return;
@ -9594,6 +9646,8 @@ static void M_EraseDataResponse(INT32 ch)
// SRB2Kart: This actually needs to be done FIRST, so that you don't immediately regain playtime/matches secrets
totalplaytime = 0;
matchesplayed = 0;
for (i = 0; i < PWRLV_NUMTYPES; i++)
vspowerlevel[i] = PWRLVRECORD_START;
F_StartIntro();
}
if (erasecontext != 1)

View file

@ -3559,8 +3559,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;
@ -3604,12 +3602,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++;
}
}
@ -3636,15 +3633,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++;
@ -8300,7 +8294,7 @@ void A_ItemPop(mobj_t *actor)
remains->flags = actor->flags; // Transfer flags
remains->flags2 = actor->flags2; // Transfer flags2
remains->fuse = actor->fuse; // Transfer respawn timer
remains->threshold = (actor->threshold == 69 ? 69 : 68);
remains->threshold = (actor->threshold == 70 ? 70 : (actor->threshold == 69 ? 69 : 68));
remains->skin = NULL;
remains->spawnpoint = actor->spawnpoint;
@ -8314,7 +8308,7 @@ void A_ItemPop(mobj_t *actor)
remains->flags2 &= ~MF2_AMBUSH;
if (G_BattleGametype() && actor->threshold != 69)
if (G_BattleGametype() && (actor->threshold != 69 && actor->threshold != 70))
numgotboxes++;
P_RemoveMobj(actor);

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") : ""
@ -1763,6 +1764,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
}
}
// Easily make it so that overtime works offline
//#define TESTOVERTIMEINFREEPLAY
/** Checks if the level timer is over the timelimit and the round should end,
* unless you are in overtime. In which case leveltime may stretch out beyond
* timelimitintics and overtime's status will be checked here each tick.
@ -1773,7 +1777,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
*/
void P_CheckTimeLimit(void)
{
INT32 i, k;
INT32 i;
if (!cv_timelimit.value)
return;
@ -1807,71 +1811,76 @@ void P_CheckTimeLimit(void)
}
}
}
else*/
//Optional tie-breaker for Match/CTF
else*/ if (cv_overtime.value)
if (cv_overtime.value)
{
INT32 playerarray[MAXPLAYERS];
INT32 tempplayer = 0;
INT32 spectators = 0;
INT32 playercount = 0;
//Figure out if we have enough participating players to care.
#ifndef TESTOVERTIMEINFREEPLAY
boolean foundone = false; // Overtime is used for closing off down to a specific item.
for (i = 0; i < MAXPLAYERS; i++)
{
if (players[i].exiting)
return;
if (playeringame[i] && players[i].spectator)
spectators++;
}
if ((D_NumPlayers() - spectators) > 1)
{
// Play the starpost sfx after the first second of overtime.
if (gamestate == GS_LEVEL && (leveltime == (timelimitintics + TICRATE)))
S_StartSound(NULL, sfx_strpst);
// Normal Match
if (!G_GametypeHasTeams())
if (!playeringame[i] || players[i].spectator)
continue;
if (foundone)
{
//Store the nodes of participating players in an array.
for (i = 0; i < MAXPLAYERS; i++)
#endif
// Initiate the kill zone
if (!battleovertime.enabled)
{
if (playeringame[i] && !players[i].spectator)
INT32 b = 0;
thinker_t *th;
mobj_t *item = NULL;
P_RespawnBattleBoxes(); // FORCE THESE TO BE RESPAWNED FOR THIS!!!!!!!
// Find us an item box to center on.
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
playerarray[playercount] = i;
playercount++;
mobj_t *thismo;
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
continue;
thismo = (mobj_t *)th;
if (thismo->type != MT_RANDOMITEM)
continue;
if (thismo->threshold == 69) // Disappears
continue;
b++;
// Only select items that are on the ground, ignore ones in the air. Ambush flag inverts this rule.
if ((!P_IsObjectOnGround(thismo)) != (thismo->flags2 & MF2_AMBUSH))
continue;
if (item == NULL || (b < nummapboxes && P_RandomChance(((nummapboxes-b)*FRACUNIT)/nummapboxes))) // This is to throw off the RNG some
item = thismo;
if (b >= nummapboxes) // end early if we've found them all already
break;
}
}
if (playercount > MAXPLAYERS)
playercount = MAXPLAYERS;
//Sort 'em.
for (i = 1; i < playercount; i++)
{
for (k = i; k < playercount; k++)
if (item == NULL) // no item found, could happen if every item is in the air or has ambush flag, or the map has none
{
if (players[playerarray[i-1]].marescore < players[playerarray[k]].marescore)
{
tempplayer = playerarray[i-1];
playerarray[i-1] = playerarray[k];
playerarray[k] = tempplayer;
}
CONS_Alert(CONS_WARNING, "No usuable items for Battle overtime!\n");
return;
}
}
//End the round if the top players aren't tied.
if (players[playerarray[0]].marescore == players[playerarray[1]].marescore)
return;
item->threshold = 70; // Set constant respawn
battleovertime.x = item->x;
battleovertime.y = item->y;
battleovertime.z = item->z;
battleovertime.radius = 4096*mapobjectscale;
battleovertime.minradius = (cv_overtime.value == 2 ? 40 : 512) * mapobjectscale;
battleovertime.enabled = 1;
S_StartSound(NULL, sfx_kc47);
}
return;
#ifndef TESTOVERTIMEINFREEPLAY
}
else
{
//In team match and CTF, determining a tie is much simpler. =P
if (redscore == bluescore)
return;
}
foundone = true;
}
#endif
}
for (i = 0; i < MAXPLAYERS; i++)
@ -1882,9 +1891,6 @@ void P_CheckTimeLimit(void)
return;
P_DoPlayerExit(&players[i]);
}
/*if (server)
SendNetXCmd(XD_EXITLEVEL, NULL, 0);*/
}
/** Checks if a player's score is over the pointlimit and the round should end.
@ -2030,7 +2036,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++)
@ -2047,57 +2054,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

@ -210,6 +210,7 @@ extern tic_t itemrespawntime[ITEMQUESIZE];
extern size_t iquehead, iquetail;
extern consvar_t cv_gravity/*, cv_viewheight*/;
void P_RespawnBattleBoxes(void);
void P_RespawnSpecials(void);
mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type);
@ -228,6 +229,7 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state);
void P_RunOverlays(void);
fixed_t P_CalculateShadowFloor(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player);
void P_RunShadows(void);
void P_RunBattleOvertime(void);
void P_MobjThinker(mobj_t *mobj);
boolean P_RailThinker(mobj_t *mobj);
void P_PushableThinker(mobj_t *mobj);

View file

@ -6486,6 +6486,216 @@ static void P_RemoveShadow(mobj_t *thing)
}
}
// SAL'S KART BATTLE MODE OVERTIME HANDLER
#define MAXPLANESPERSECTOR (MAXFFLOORS+1)*2
static void P_SpawnOvertimeParticles(fixed_t x, fixed_t y, fixed_t scale, mobjtype_t type, boolean ceiling)
{
UINT8 i;
fixed_t flatz[MAXPLANESPERSECTOR];
boolean flip[MAXPLANESPERSECTOR];
UINT8 numflats = 0;
mobj_t *mo;
subsector_t *ss = R_IsPointInSubsector(x, y);
sector_t *sec;
if (!ss)
return;
sec = ss->sector;
// convoluted stuff JUST to get all of the planes we need to draw orbs on :V
for (i = 0; i < MAXPLANESPERSECTOR; i++)
flip[i] = false;
if (sec->floorpic != skyflatnum)
{
#ifdef ESLOPE
flatz[numflats] = (sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : sec->floorheight);
#else
flatz[numflats] = (sec->floorheight);
#endif
numflats++;
}
if (sec->ceilingpic != skyflatnum && ceiling)
{
#ifdef ESLOPE
flatz[numflats] = (sec->c_slope ? P_GetZAt(sec->c_slope, x, y) : sec->ceilingheight) - FixedMul(mobjinfo[type].height, scale);
#else
flatz[numflats] = (sec->ceilingheight) - FixedMul(mobjinfo[type].height, scale);
#endif
flip[numflats] = true;
numflats++;
}
if (sec->ffloors)
{
ffloor_t *rover;
for (rover = sec->ffloors; rover; rover = rover->next)
{
if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER))
continue;
if (*rover->toppic != skyflatnum)
{
#ifdef ESLOPE
flatz[numflats] = (*rover->t_slope ? P_GetZAt(*rover->t_slope, x, y) : *rover->topheight);
#else
flatz[numflats] = (*rover->topheight);
#endif
numflats++;
}
if (*rover->bottompic != skyflatnum && ceiling)
{
#ifdef ESLOPE
flatz[numflats] = (*rover->b_slope ? P_GetZAt(*rover->b_slope, x, y) : *rover->bottomheight) - FixedMul(mobjinfo[type].height, scale);
#else
flatz[numflats] = (*rover->bottomheight) - FixedMul(mobjinfo[type].height, scale);
#endif
flip[numflats] = true;
numflats++;
}
}
}
if (numflats <= 0) // no flats
return;
for (i = 0; i < numflats; i++)
{
mo = P_SpawnMobj(x, y, flatz[i], type);
// Lastly, if this can see the skybox mobj, then... we just wasted our time :V
if (skyboxmo[0] && !P_MobjWasRemoved(skyboxmo[0]))
{
const fixed_t sbz = skyboxmo[0]->z;
fixed_t checkz = sec->floorheight;
while (checkz < sec->ceilingheight)
{
P_TeleportMove(skyboxmo[0], skyboxmo[0]->x, skyboxmo[0]->y, checkz);
if (P_CheckSight(skyboxmo[0], mo))
{
P_RemoveMobj(mo);
break;
}
else
checkz += 32*mapobjectscale;
}
P_TeleportMove(skyboxmo[0], skyboxmo[0]->x, skyboxmo[0]->y, sbz);
if (P_MobjWasRemoved(mo))
continue;
}
P_SetScale(mo, scale);
if (flip[i])
{
mo->flags2 |= MF2_OBJECTFLIP;
mo->eflags |= MFE_VERTICALFLIP;
}
switch(type)
{
case MT_OVERTIMEFOG:
mo->destscale = 8*mo->scale;
mo->momz = P_RandomRange(1,8)*mo->scale;
break;
case MT_OVERTIMEORB:
//mo->destscale = mo->scale/4;
mo->frame += ((leveltime/4) % 8);
/*if (battleovertime.enabled < 10*TICRATE)
mo->flags2 |= MF2_SHADOW;*/
mo->angle = R_PointToAngle2(mo->x, mo->y, battleovertime.x, battleovertime.y) + ANGLE_90;
mo->z += P_RandomRange(0,48) * mo->scale;
break;
default:
break;
}
}
}
#undef MAXPLANESPERSECTOR
void P_RunBattleOvertime(void)
{
UINT16 i, j;
if (battleovertime.enabled < 10*TICRATE)
{
battleovertime.enabled++;
if (battleovertime.enabled == TICRATE)
S_StartSound(NULL, sfx_bhurry);
if (battleovertime.enabled == 10*TICRATE)
S_StartSound(NULL, sfx_kc40);
}
else
{
if (battleovertime.radius > battleovertime.minradius)
battleovertime.radius -= mapobjectscale;
else
battleovertime.radius = battleovertime.minradius;
}
if (leveltime & 1)
{
UINT8 transparency = tr_trans50;
if (!splitscreen && players[displayplayers[0]].mo)
{
INT32 dist = P_AproxDistance(battleovertime.x-players[displayplayers[0]].mo->x, battleovertime.y-players[displayplayers[0]].mo->y);
transparency = max(0, NUMTRANSMAPS - ((256 + (dist>>FRACBITS)) / 256));
}
if (transparency < NUMTRANSMAPS)
{
mobj_t *beam = P_SpawnMobj(battleovertime.x, battleovertime.y, battleovertime.z + (mobjinfo[MT_RANDOMITEM].height/2), MT_OVERTIMEBEAM);
P_SetScale(beam, beam->scale*2);
if (transparency > 0)
beam->frame |= transparency<<FF_TRANSSHIFT;
}
}
// 16 orbs at the normal minimum size of 512
{
const fixed_t pi = (22<<FRACBITS) / 7; // loose approximation, this doesn't need to be incredibly precise
fixed_t scale = mapobjectscale + (battleovertime.radius/2048);
fixed_t sprwidth = 32*scale;
fixed_t circumference = FixedMul(pi, battleovertime.radius<<1);
UINT16 orbs = circumference / sprwidth;
angle_t angoff = ANGLE_MAX / orbs;
for (i = 0; i < orbs; i++)
{
angle_t ang = (i * angoff) + FixedAngle((leveltime/2)<<FRACBITS);
fixed_t x = battleovertime.x + P_ReturnThrustX(NULL, ang, battleovertime.radius - FixedMul(mobjinfo[MT_OVERTIMEORB].radius, scale));
fixed_t y = battleovertime.y + P_ReturnThrustY(NULL, ang, battleovertime.radius - FixedMul(mobjinfo[MT_OVERTIMEORB].radius, scale));
P_SpawnOvertimeParticles(x, y, scale, MT_OVERTIMEORB, false);
}
}
if (battleovertime.enabled < 10*TICRATE)
return;
/*if (!S_IdPlaying(sfx_s3kd4s)) // global ambience
S_StartSoundAtVolume(NULL, sfx_s3kd4s, min(255, ((4096*mapobjectscale) - battleovertime.radius)>>FRACBITS / 2));*/
for (i = 0; i < 16; i++)
{
j = 0;
while (j < 32) // max attempts
{
fixed_t x = battleovertime.x + ((P_RandomRange(-64,64) * 128)<<FRACBITS);
fixed_t y = battleovertime.y + ((P_RandomRange(-64,64) * 128)<<FRACBITS);
fixed_t closestdist = battleovertime.radius + (8*mobjinfo[MT_OVERTIMEFOG].radius);
j++;
if (P_AproxDistance(x-battleovertime.x, y-battleovertime.y) < closestdist)
continue;
P_SpawnOvertimeParticles(x, y, 4*mapobjectscale, MT_OVERTIMEFOG, false);
break;
}
}
}
void A_BossDeath(mobj_t *mo);
// AI for the Koopa boss.
static void P_KoopaThinker(mobj_t *koopa)
@ -7097,7 +7307,7 @@ void P_MobjThinker(mobj_t *mobj)
mobj->x = mobj->target->x;
mobj->y = mobj->target->y;
if (!splitscreen)
if (!splitscreen && players[displayplayers[0]].mo)
{
scale = mobj->target->scale + FixedMul(FixedDiv(abs(P_AproxDistance(players[displayplayers[0]].mo->x-mobj->target->x,
players[displayplayers[0]].mo->y-mobj->target->y)), RING_DIST), mobj->target->scale);
@ -8534,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
@ -9317,6 +9522,40 @@ void P_MobjThinker(mobj_t *mobj)
trail->color = mobj->color;
}
break;
case MT_RANDOMITEM:
if (G_BattleGametype() && mobj->threshold == 70)
{
mobj->color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1)));
mobj->colorized = true;
if (battleovertime.enabled)
{
angle_t ang = FixedAngle((leveltime % 360) << FRACBITS);
fixed_t z = battleovertime.z;
fixed_t dist;
mobj_t *ghost;
/*if (z < mobj->subsector->sector->floorheight)
z = mobj->subsector->sector->floorheight;*/
if (mobj->extravalue1 < 512)
mobj->extravalue1++;
dist = mobj->extravalue1 * mapobjectscale;
P_TeleportMove(mobj, battleovertime.x + P_ReturnThrustX(NULL, ang, dist),
battleovertime.y + P_ReturnThrustY(NULL, ang, dist), z);
ghost = P_SpawnGhostMobj(mobj);
ghost->fuse = 4;
ghost->frame |= FF_FULLBRIGHT;
}
}
else
{
mobj->color = SKINCOLOR_NONE;
mobj->colorized = false;
}
break;
//}
case MT_TURRET:
P_MobjCheckWater(mobj);
@ -9581,7 +9820,7 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s
P_RemoveMobj(mobj); // make sure they disappear
return;
case MT_RANDOMITEM:
if (G_BattleGametype())
if (G_BattleGametype() && (mobj->threshold != 70))
{
if (mobj->threshold != 69)
break;
@ -9597,8 +9836,11 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s
else
newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->type);
P_SpawnMobj(newmobj->x, newmobj->y, newmobj->z, MT_EXPLODE); // poof into existance
// Transfer flags2 (strongbox, objectflip)
newmobj->flags2 = mobj->flags2 & ~MF2_DONTDRAW;
if (mobj->threshold == 70)
newmobj->threshold = 70;
}
P_RemoveMobj(mobj); // make sure they disappear
return;
@ -10958,6 +11200,50 @@ void P_PrecipitationEffects(void)
}
}
void P_RespawnBattleBoxes(void)
{
thinker_t *th;
if (!G_BattleGametype())
return;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
mobj_t *box;
mobj_t *newmobj;
if (th->function.acp1 != (actionf_p1)P_MobjThinker) // not a mobj
continue;
box = (mobj_t *)th;
if (box->type != MT_RANDOMITEM || box->threshold != 68 || box->fuse) // only popped items
continue;
// Respawn from mapthing if you have one!
if (box->spawnpoint)
{
P_SpawnMapThing(box->spawnpoint);
newmobj = box->spawnpoint->mobj; // this is set to the new mobj in P_SpawnMapThing
P_SpawnMobj(box->spawnpoint->mobj->x, box->spawnpoint->mobj->y, box->spawnpoint->mobj->z, MT_EXPLODE); // poof into existance
}
else
{
newmobj = P_SpawnMobj(box->x, box->y, box->z, box->type);
P_SpawnMobj(newmobj->x, newmobj->y, newmobj->z, MT_EXPLODE); // poof into existance
}
// Transfer flags2 (strongbox, objectflip)
newmobj->flags2 = box->flags2;
P_RemoveMobj(box); // make sure they disappear
numgotboxes--; // you've restored a box, remove it from the count
//continue; -- irrelevant?
}
if (numgotboxes < 0)
numgotboxes = 0;
}
//
// P_RespawnSpecials
//
@ -10971,45 +11257,7 @@ void P_RespawnSpecials(void)
mapthing_t *mthing = NULL;
if (G_BattleGametype() && numgotboxes >= (4*nummapboxes/5)) // Battle Mode respawns all boxes in a different way
{
thinker_t *th;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
mobj_t *box;
mobj_t *newmobj;
if (th->function.acp1 != (actionf_p1)P_MobjThinker) // not a mobj
continue;
box = (mobj_t *)th;
if (box->type != MT_RANDOMITEM || box->threshold != 68 || box->fuse) // only popped items
continue;
// Respawn from mapthing if you have one!
if (box->spawnpoint)
{
P_SpawnMapThing(box->spawnpoint);
newmobj = box->spawnpoint->mobj; // this is set to the new mobj in P_SpawnMapThing
P_SpawnMobj(box->spawnpoint->mobj->x, box->spawnpoint->mobj->y, box->spawnpoint->mobj->z, MT_EXPLODE); // poof into existance
}
else
{
newmobj = P_SpawnMobj(box->x, box->y, box->z, box->type);
P_SpawnMobj(newmobj->x, newmobj->y, newmobj->z, MT_EXPLODE); // poof into existance
}
// Transfer flags2 (strongbox, objectflip)
newmobj->flags2 = box->flags2;
P_RemoveMobj(box); // make sure they disappear
numgotboxes--; // you've restored a box, remove it from the count
//continue; -- irrelevant?
}
if (numgotboxes < 0)
numgotboxes = 0;
}
P_RespawnBattleBoxes();
// wait time depends on player count
for (p = 0; p < MAXPLAYERS; p++)
@ -11021,8 +11269,16 @@ void P_RespawnSpecials(void)
if (pcount == 1) // No respawn when alone
return;
else if (pcount > 1)
{
time = (180 - (pcount * 10))*TICRATE;
// If the map is longer or shorter than 3 laps, then adjust ring respawn to account for this.
// 5 lap courses would have more retreaded ground, while 2 lap courses would have less.
if ((mapheaderinfo[gamemap-1]->numlaps != 3)
&& !(mapheaderinfo[gamemap-1]->levelflags & LF_SECTIONRACE))
time = (time * 3) / max(1, mapheaderinfo[gamemap-1]->numlaps);
}
// only respawn items when cv_itemrespawn is on
//if (!cv_itemrespawn.value) // TODO: remove this cvar
//return;

View file

@ -34,6 +34,9 @@
#include "p_slopes.h"
#endif
// SRB2Kart
#include "k_pwrlv.h"
savedata_t savedata;
UINT8 *save_p;
@ -953,8 +956,8 @@ typedef enum
MD2_EXTVAL2 = 1<<6,
MD2_HNEXT = 1<<7,
MD2_HPREV = 1<<8,
MD2_COLORIZED = 1<<9,
MD2_WAYPOINTCAP = 1<<10
MD2_COLORIZED = 1<<9,
MD2_WAYPOINTCAP = 1<<10
#ifdef ESLOPE
, MD2_SLOPE = 1<<11
#endif
@ -3305,11 +3308,22 @@ static void P_NetArchiveMisc(void)
for (i = 0; i < 4; i++)
WRITESINT8(save_p, battlewanted[i]);
// battleovertime_t
WRITEUINT16(save_p, battleovertime.enabled);
WRITEFIXED(save_p, battleovertime.radius);
WRITEFIXED(save_p, battleovertime.minradius);
WRITEFIXED(save_p, battleovertime.x);
WRITEFIXED(save_p, battleovertime.y);
WRITEFIXED(save_p, battleovertime.z);
WRITEUINT32(save_p, wantedcalcdelay);
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);
@ -3414,11 +3428,22 @@ static inline boolean P_NetUnArchiveMisc(void)
for (i = 0; i < 4; i++)
battlewanted[i] = READSINT8(save_p);
// battleovertime_t
battleovertime.enabled = READUINT16(save_p);
battleovertime.radius = READFIXED(save_p);
battleovertime.minradius = READFIXED(save_p);
battleovertime.x = READFIXED(save_p);
battleovertime.y = READFIXED(save_p);
battleovertime.z = READFIXED(save_p);
wantedcalcdelay = READUINT32(save_p);
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

@ -85,6 +85,7 @@
// SRB2Kart
#include "k_kart.h"
#include "k_waypoint.h"
#include "k_pwrlv.h"
//
// Map MD5, calculated on level load.
@ -2397,13 +2398,21 @@ 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;
}
for (i = 0; i < 4; i++)
battlewanted[i] = -1;
memset(&battleovertime, 0, sizeof(struct battleovertime));
speedscramble = encorescramble = -1;
}
//
@ -2781,7 +2790,7 @@ boolean P_SetupLevel(boolean skipprecip)
// use gamemap to get map number.
// 99% of the things already did, so.
// Map header should always be in place at this point
INT32 i, loadprecip = 1, ranspecialwipe = 0;
INT32 i, loadprecip = 1;
INT32 loademblems = 1;
INT32 fromnetsave = 0;
boolean loadedbm = false;
@ -2860,36 +2869,50 @@ boolean P_SetupLevel(boolean skipprecip)
S_StartSound(NULL, sfx_ruby1);
// Fade to an inverted screen, with a circle fade...
F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 209);
V_EncoreInvertScreen();
F_WipeEndScreen();
F_RunWipe(wipedefs[wipe_speclevel_towhite], false);
F_RunWipe(wipedefs[wipe_encore_toinvert], false, NULL, false, false);
// Hold on invert for extra effect.
// (This define might be useful for other areas of code? Not sure)
#define WAIT(timetowait) \
locstarttime = nowtime = lastwipetic; \
endtime = locstarttime + timetowait; \
while (nowtime < endtime) \
{ \
while (!((nowtime = I_GetTime()) - lastwipetic)) \
I_Sleep(); \
lastwipetic = nowtime; \
if (moviemode) \
M_SaveFrame(); \
NetKeepAlive(); \
} \
WAIT((3*TICRATE)/2);
S_StartSound(NULL, sfx_ruby2);
// Then fade to a white screen
F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 0);
F_WipeEndScreen();
F_RunWipe(wipedefs[wipe_level_final], false);
locstarttime = nowtime = lastwipetic;
endtime = locstarttime + (3*TICRATE)/2;
F_RunWipe(wipedefs[wipe_encore_towhite], false, "FADEMAP1", false, true); // wiggle the screen during this!
// Hold on white for extra effect.
while (nowtime < endtime)
{
// wait loop
while (!((nowtime = I_GetTime()) - lastwipetic))
I_Sleep();
lastwipetic = nowtime;
if (moviemode) // make sure we save frames for the white hold too
M_SaveFrame();
// THEN fade to a black screen.
F_WipeStartScreen();
// Keep the network alive
NetKeepAlive();
}
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
F_WipeEndScreen();
ranspecialwipe = 1;
F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
// Wait a bit longer.
WAIT((3*TICRATE)/4);
}
// Make sure all sounds are stopped before Z_FreeTags.
@ -2900,17 +2923,18 @@ boolean P_SetupLevel(boolean skipprecip)
// We should be fine starting it here.
S_Start();
levelfadecol = (encoremode && !ranspecialwipe ? 209 : 0);
levelfadecol = (encoremode ? 0 : 31);
// Let's fade to white here
// But only if we didn't do the encore startup wipe
if (rendermode != render_none && !ranspecialwipe && !demo.rewinding)
if (rendermode != render_none && !demo.rewinding)
{
F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol);
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol);
F_WipeEndScreen();
F_RunWipe(wipedefs[(encoremode ? wipe_level_final : wipe_level_toblack)], false);
F_RunWipe(wipedefs[wipe_level_toblack], false, ((levelfadecol == 0) ? "FADEMAP1" : "FADEMAP0"), false, false);
}
// Reset the palette now all fades have been done
@ -3285,7 +3309,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

@ -3788,10 +3788,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;

View file

@ -618,33 +618,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++)
@ -665,6 +642,8 @@ void P_Ticker(boolean run)
if (run)
{
P_RunThinkers();
if (G_BattleGametype() && battleovertime.enabled)
P_RunBattleOvertime();
// Run any "after all the other thinkers" stuff
for (i = 0; i < MAXPLAYERS; i++)
@ -760,11 +739,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();
}
@ -827,6 +801,8 @@ void P_PreTicker(INT32 frames)
}
P_RunThinkers();
if (G_BattleGametype() && battleovertime.enabled)
P_RunBattleOvertime();
// Run any "after all the other thinkers" stuff
for (i = 0; i < MAXPLAYERS; i++)

View file

@ -8055,8 +8055,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.
@ -8066,7 +8064,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

@ -267,7 +267,7 @@ static UINT8 *R_GenerateTexture(size_t texnum)
texturememory += blocksize;
block = Z_Malloc(blocksize+1, PU_STATIC, &texturecache[texnum]);
memset(block, 0xF7, blocksize+1); // Transparency hack
memset(block, 0xFF, blocksize+1); // TRANSPARENTPIXEL
// columns lookup table
colofs = (UINT32 *)(void *)block;
@ -1177,7 +1177,6 @@ void R_ClearColormaps(void)
//
static double deltas[256][3], map[256][3];
static UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b);
static int RoundUp(double number);
#ifdef HASINVERT
@ -1403,7 +1402,7 @@ INT32 R_CreateColormap(char *p1, char *p2, char *p3)
// Thanks to quake2 source!
// utils3/qdata/images.c
static UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b)
UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b)
{
int dr, dg, db;
int distortion, bestdistortion = 256 * 256 * 4, bestcolor = 0, i;

View file

@ -93,6 +93,7 @@ void R_ReInitColormaps(UINT16 num, lumpnum_t newencoremap);
void R_ClearColormaps(void);
INT32 R_ColormapNumForName(char *name);
INT32 R_CreateColormap(char *p1, char *p2, char *p3);
UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b);
#ifdef HASINVERT
void R_MakeInvertmap(void);
#endif

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.
@ -3177,11 +3183,14 @@ void I_Error(const char *error, ...)
#endif
G_SaveGameData(false); // Tails 12-08-2002
/* Prevent segmentation fault if testers go to Record Attack... */
#ifndef TESTERS
// Shutdown. Here might be other errors.
if (demo.recording)
G_CheckDemoStatus();
if (metalrecording)
G_StopMetalRecording();
#endif
D_QuitNetGame();
I_ShutdownMusic();

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

@ -815,6 +815,7 @@ sfxinfo_t S_sfx[NUMSFX] =
{"chain", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Mementos Reaper
{"mkuma", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Trigger Happy Havoc Monokuma
{"toada", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Arid Sands Toad scream
{"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
{"itfree", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // :shitsfree:
{"dbgsal", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Debug notification

View file

@ -890,6 +890,7 @@ typedef enum
sfx_chain,
sfx_mkuma,
sfx_toada,
sfx_bhurry,
sfx_bsnipe,
sfx_itfree,
sfx_dbgsal,

View file

@ -2134,12 +2134,7 @@ void ST_Drawer(void)
ST_MayonakaStatic();
}
// Draw a white fade on level opening
if (timeinmap < 15)
{
if (timeinmap <= 5)
V_DrawFill(0,0,BASEVIDWIDTH,BASEVIDHEIGHT,0); // Pure white on first few frames, to hide SRB2's awful level load artifacts
else
V_DrawFadeScreen(0, 15-timeinmap); // Then gradually fade out from there
}
// Draw a fade on level opening
if (timeinmap < 16)
V_DrawCustomFadeScreen(((levelfadecol == 0) ? "FADEMAP1" : "FADEMAP0"), 32-(timeinmap*2)); // Then gradually fade out from there
}

View file

@ -1287,28 +1287,75 @@ void V_DrawVhsEffect(boolean rewind)
void V_DrawFadeScreen(UINT16 color, UINT8 strength)
{
#ifdef HWRENDER
if (rendermode != render_soft && rendermode != render_none)
{
HWR_FadeScreenMenuBack(color, strength);
return;
}
if (rendermode != render_soft && rendermode != render_none)
{
HWR_FadeScreenMenuBack(color, strength);
return;
}
#endif
{
const UINT8 *fadetable =
{
const UINT8 *fadetable =
(color > 0xFFF0) // Grab a specific colormap palette?
? R_GetTranslationColormap(color | 0xFFFF0000, strength, GTC_CACHE)
: ((color & 0xFF00) // Color is not palette index?
? ((UINT8 *)colormaps + strength*256) // Do COLORMAP fade.
: ((UINT8 *)transtables + ((9-strength)<<FF_TRANSSHIFT) + color*256)); // Else, do TRANSMAP** fade.
const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
UINT8 *buf = screens[0];
const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
UINT8 *buf = screens[0];
// heavily simplified -- we don't need to know x or y
// position when we're doing a full screen fade
for (; buf < deststop; ++buf)
*buf = fadetable[*buf];
}
// heavily simplified -- we don't need to know x or y
// position when we're doing a full screen fade
for (; buf < deststop; ++buf)
*buf = fadetable[*buf];
}
}
//
// Fade the screen buffer, using a custom COLORMAP lump.
// Split from V_DrawFadeScreen, because that function has
// WAY too many options piled on top of it as is. :V
//
void V_DrawCustomFadeScreen(const char *lump, UINT8 strength)
{
#ifdef HWRENDER
if (rendermode != render_soft && rendermode != render_none)
{
//HWR_DrawCustomFadeScreen(color, strength);
return;
}
#endif
{
lumpnum_t lumpnum = LUMPERROR;
lighttable_t *clm = NULL;
if (lump != NULL)
lumpnum = W_GetNumForName(lump);
else
return;
if (lumpnum != LUMPERROR)
{
clm = Z_MallocAlign((256 * 32), PU_STATIC, NULL, 8);
W_ReadLump(lumpnum, clm);
if (clm != NULL)
{
const UINT8 *fadetable = ((UINT8 *)clm + strength*256);
const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
UINT8 *buf = screens[0];
// heavily simplified -- we don't need to know x or y
// position when we're doing a full screen fade
for (; buf < deststop; ++buf)
*buf = fadetable[*buf];
Z_Free(clm);
clm = NULL;
}
}
}
}
// Simple translucency with one color, over a set number of lines starting from the top.
@ -1332,6 +1379,34 @@ void V_DrawFadeConsBack(INT32 plines)
*buf = consolebgmap[*buf];
}
//
// Invert the entire screen, for Encore fades
//
void V_EncoreInvertScreen(void)
{
#ifdef HWRENDER
if (rendermode != render_soft && rendermode != render_none)
{
//HWR_EncoreInvertScreen();
return;
}
#endif
{
const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
UINT8 *buf = screens[0];
for (; buf < deststop; ++buf)
{
*buf = NearestColor(
256 - pLocalPalette[*buf].s.red,
256 - pLocalPalette[*buf].s.green,
256 - pLocalPalette[*buf].s.blue
);
}
}
}
// Gets string colormap, used for 0x80 color codes
//
UINT8 *V_GetStringColormap(INT32 colorflags)

View file

@ -158,8 +158,9 @@ void V_DrawVhsEffect(boolean rewind);
// fade down the screen buffer before drawing the menu over
void V_DrawFadeScreen(UINT16 color, UINT8 strength);
void V_DrawCustomFadeScreen(const char *lump, UINT8 strength);
void V_DrawFadeConsBack(INT32 plines);
void V_EncoreInvertScreen(void);
// draw a single character
void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed);

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 "console.h" // cons_menuhighlight
#include "lua_hook.h" // IntermissionThinker hook
@ -90,7 +91,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
@ -110,6 +111,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;
@ -193,11 +195,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;
}
@ -205,7 +209,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)
@ -257,14 +261,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++;
}
@ -276,8 +283,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])
@ -299,9 +304,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];
}
@ -431,7 +437,7 @@ void Y_IntermissionDrawer(void)
const char *timeheader;
if (data.match.rankingsmode)
timeheader = "RANK";
timeheader = "PWR.LV";
else
timeheader = (intertype == int_race ? "TIME" : "SCORE");
@ -493,21 +499,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
@ -591,9 +599,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));
//}
}
//
@ -676,13 +689,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)
@ -782,6 +828,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
//
@ -796,6 +989,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;
@ -872,6 +1078,9 @@ void Y_StartIntermission(void)
break;
}
if (powertype != -1)
K_UpdatePowerLevels();
//if (intertype == int_race || intertype == int_match)
{
//bgtile = W_CachePatchName("SRB2BACK", PU_STATIC);