mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-05-10 10:51:42 +00:00
Merge remote-tracking branch 'origin/master' into bot-grip
This commit is contained in:
commit
8c463d47b9
76 changed files with 2820 additions and 1154 deletions
|
|
@ -3589,13 +3589,15 @@ bool CallFunc_SetThingProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, A
|
|||
INT32 value = 0;
|
||||
|
||||
tag = argV[0];
|
||||
mobj = P_FindMobjFromTID(tag, mobj, info->mo);
|
||||
mobj_t *next = P_FindMobjFromTID(tag, mobj, info->mo);
|
||||
|
||||
property = argV[1];
|
||||
value = argV[2];
|
||||
|
||||
while (mobj != NULL)
|
||||
while ((mobj = next) != NULL)
|
||||
{
|
||||
// First in case of deletion. (Can't check for value == S_NULL because of A_ calls, etc)
|
||||
next = P_FindMobjFromTID(tag, mobj, info->mo);
|
||||
|
||||
#define PROP_READONLY(x, y) \
|
||||
case x: \
|
||||
|
|
@ -3830,8 +3832,6 @@ bool CallFunc_SetThingProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, A
|
|||
}
|
||||
}
|
||||
|
||||
mobj = P_FindMobjFromTID(tag, mobj, info->mo);
|
||||
|
||||
#undef PROP_FLAGS
|
||||
#undef PROP_SCALE
|
||||
#undef PROP_MOBJ
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ public:
|
|||
|
||||
Vector& operator=(const Vector& rhs)
|
||||
{
|
||||
clear();
|
||||
for (auto itr = rhs.begin(); itr != rhs.end(); itr++)
|
||||
{
|
||||
push_back(*itr);
|
||||
|
|
|
|||
|
|
@ -728,7 +728,7 @@ consvar_t cv_kartspeed = UnsavedNetVar("gamespeed", "Auto Gear").values(kartspee
|
|||
|
||||
consvar_t cv_teamplay = UnsavedNetVar("teamplay", "Off").on_off();
|
||||
|
||||
consvar_t cv_kartusepwrlv = UnsavedNetVar("usepwrlv", "Yes").yes_no();
|
||||
consvar_t cv_kartusepwrlv = UnsavedNetVar("mobiums", "Yes").yes_no();
|
||||
|
||||
void LiveStudioAudience_OnChange(void);
|
||||
#ifdef DEVELOP
|
||||
|
|
@ -783,6 +783,8 @@ consvar_t cv_timelimit = UnsavedNetVar("timelimit", "Default").min_max(1, 30*60,
|
|||
|
||||
consvar_t cv_votetime = UnsavedNetVar("votetime", "20").min_max(10, 3600);
|
||||
|
||||
consvar_t cv_dueltimelimit = UnsavedNetVar("dueltimelimit", "180").min_max(0, 3600);
|
||||
consvar_t cv_duelscorelimit = UnsavedNetVar("duelscorelimit", "4").min_max(1, 9);
|
||||
|
||||
//
|
||||
// Online cheats - synced in netgames.
|
||||
|
|
|
|||
|
|
@ -281,7 +281,7 @@ shouldsign_t ShouldSignChallenge(uint8_t *message)
|
|||
if ((max(now, then) - min(now, then)) > 60*15)
|
||||
return SIGN_BADTIME;
|
||||
|
||||
// ____ _____ ___ ____ _
|
||||
// ____ _____ ___ ____ _
|
||||
// / ___|_ _/ _ \| _ \| |
|
||||
// \___ \ | || | | | |_) | |
|
||||
// ___) || || |_| | __/|_|
|
||||
|
|
@ -2436,6 +2436,11 @@ static void CL_ConnectToServer(void)
|
|||
|
||||
joinedIP[0] = '\0'; // And empty this for good measure regardless of whether or not we actually used it.
|
||||
|
||||
// Enable sound input/microphone in netgames, activating the microphone device.
|
||||
if (netgame)
|
||||
{
|
||||
S_SoundInputSetEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
static void Command_connect(void)
|
||||
|
|
@ -2502,6 +2507,8 @@ static void Command_connect(void)
|
|||
{
|
||||
CONS_Alert(CONS_ERROR, M_GetText("There is no server identification with this network driver\n"));
|
||||
D_CloseConnection();
|
||||
|
||||
S_SoundInputSetEnabled(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -3659,6 +3666,7 @@ void D_QuitNetGame(void)
|
|||
K_ClearClientPowerLevels();
|
||||
G_ObliterateParties();
|
||||
K_ResetMidVote();
|
||||
S_SoundInputSetEnabled(false);
|
||||
|
||||
DEBFILE("===========================================================================\n"
|
||||
" Log finish\n"
|
||||
|
|
@ -7376,9 +7384,6 @@ void NetVoiceUpdate(void)
|
|||
return;
|
||||
}
|
||||
|
||||
// This necessarily runs every frame, not every tic
|
||||
S_SoundInputSetEnabled(true);
|
||||
|
||||
UINT32 bytes_dequed = 0;
|
||||
do
|
||||
{
|
||||
|
|
@ -7555,66 +7560,6 @@ tic_t GetLag(INT32 node)
|
|||
return gametic - nettics[node];
|
||||
}
|
||||
|
||||
#define REWIND_POINT_INTERVAL 4*TICRATE + 16
|
||||
rewind_t *rewindhead;
|
||||
|
||||
void CL_ClearRewinds(void)
|
||||
{
|
||||
rewind_t *head;
|
||||
while ((head = rewindhead))
|
||||
{
|
||||
rewindhead = rewindhead->next;
|
||||
free(head);
|
||||
}
|
||||
}
|
||||
|
||||
rewind_t *CL_SaveRewindPoint(size_t demopos)
|
||||
{
|
||||
savebuffer_t save = {0};
|
||||
rewind_t *rewind;
|
||||
|
||||
if (rewindhead && rewindhead->leveltime + REWIND_POINT_INTERVAL > leveltime)
|
||||
return NULL;
|
||||
|
||||
rewind = (rewind_t *)malloc(sizeof (rewind_t));
|
||||
if (!rewind)
|
||||
return NULL;
|
||||
|
||||
P_SaveBufferFromExisting(&save, rewind->savebuffer, NETSAVEGAMESIZE);
|
||||
P_SaveNetGame(&save, false);
|
||||
|
||||
rewind->leveltime = leveltime;
|
||||
rewind->next = rewindhead;
|
||||
rewind->demopos = demopos;
|
||||
rewindhead = rewind;
|
||||
|
||||
return rewind;
|
||||
}
|
||||
|
||||
rewind_t *CL_RewindToTime(tic_t time)
|
||||
{
|
||||
savebuffer_t save = {0};
|
||||
rewind_t *rewind;
|
||||
|
||||
while (rewindhead && rewindhead->leveltime > time)
|
||||
{
|
||||
rewind = rewindhead->next;
|
||||
free(rewindhead);
|
||||
rewindhead = rewind;
|
||||
}
|
||||
|
||||
if (!rewindhead)
|
||||
return NULL;
|
||||
|
||||
P_SaveBufferFromExisting(&save, rewindhead->savebuffer, NETSAVEGAMESIZE);
|
||||
P_LoadNetGame(&save, false);
|
||||
|
||||
wipegamestate = gamestate; // No fading back in!
|
||||
timeinmap = leveltime;
|
||||
|
||||
return rewindhead;
|
||||
}
|
||||
|
||||
void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest)
|
||||
{
|
||||
#ifdef NOMD5
|
||||
|
|
|
|||
|
|
@ -712,21 +712,6 @@ extern boolean hu_stopped;
|
|||
// SRB2Kart
|
||||
//
|
||||
|
||||
struct rewind_t {
|
||||
UINT8 savebuffer[NETSAVEGAMESIZE];
|
||||
tic_t leveltime;
|
||||
size_t demopos;
|
||||
|
||||
ticcmd_t oldcmd[MAXPLAYERS];
|
||||
mobj_t oldghost[MAXPLAYERS];
|
||||
|
||||
rewind_t *next;
|
||||
};
|
||||
|
||||
void CL_ClearRewinds(void);
|
||||
rewind_t *CL_SaveRewindPoint(size_t demopos);
|
||||
rewind_t *CL_RewindToTime(tic_t time);
|
||||
|
||||
void HandleSigfail(const char *string);
|
||||
|
||||
void DoSayPacket(SINT8 target, UINT8 flags, UINT8 source, char *message);
|
||||
|
|
|
|||
|
|
@ -742,9 +742,6 @@ static bool D_Display(bool world)
|
|||
if (forcerefresh && G_GamestateUsesLevel() == false)
|
||||
V_SetPalette(0);
|
||||
|
||||
if (demo.rewinding)
|
||||
V_DrawFadeScreen(TC_RAINBOW, (leveltime & 0x20) ? SKINCOLOR_PASTEL : SKINCOLOR_MOONSET);
|
||||
|
||||
// vid size change is now finished if it was on...
|
||||
vid.recalc = 0;
|
||||
|
||||
|
|
@ -936,7 +933,7 @@ void D_SRB2Loop(void)
|
|||
realtics = entertic - oldentertics;
|
||||
oldentertics = entertic;
|
||||
|
||||
if (demo.playback && gamestate == GS_LEVEL)
|
||||
if (demo.playback && gamestate == GS_LEVEL && demo.simplerewind == DEMO_REWIND_OFF)
|
||||
{
|
||||
// Nicer place to put this.
|
||||
realtics = realtics * cv_playbackspeed.value;
|
||||
|
|
@ -1216,6 +1213,7 @@ void D_ClearState(void)
|
|||
// Reset GP and roundqueue
|
||||
memset(&grandprixinfo, 0, sizeof(struct grandprixinfo));
|
||||
memset(&roundqueue, 0, sizeof(struct roundqueue));
|
||||
memset(&menuqueue, 0, sizeof(struct menuqueue));
|
||||
|
||||
// empty some other semi-important state
|
||||
maptol = 0;
|
||||
|
|
@ -1248,7 +1246,8 @@ void D_ClearState(void)
|
|||
if (gamedata && gamedata->deferredsave)
|
||||
G_SaveGameData();
|
||||
|
||||
K_UnsetDialogue();
|
||||
P_FreeLevelState();
|
||||
P_InvalidateThinkersWithoutInit();
|
||||
|
||||
G_SetGamestate(GS_NULL);
|
||||
wipegamestate = GS_NULL;
|
||||
|
|
|
|||
|
|
@ -2291,11 +2291,11 @@ void D_SetupVote(INT16 newgametype)
|
|||
|
||||
void D_ModifyClientVote(UINT8 player, SINT8 voted)
|
||||
{
|
||||
char buf[2];
|
||||
char buf[3];
|
||||
char *p = buf;
|
||||
UINT8 sendPlayer = consoleplayer;
|
||||
UINT8 sendPlayer = 0;
|
||||
|
||||
if (player == UINT8_MAX)
|
||||
if (player >= MAXPLAYERS)
|
||||
{
|
||||
// Special game vote (map anger, duel)
|
||||
if (!server)
|
||||
|
|
@ -2304,16 +2304,16 @@ void D_ModifyClientVote(UINT8 player, SINT8 voted)
|
|||
}
|
||||
}
|
||||
|
||||
if (player == UINT8_MAX)
|
||||
{
|
||||
// special vote
|
||||
WRITEUINT8(p, UINT8_MAX);
|
||||
}
|
||||
else
|
||||
{
|
||||
INT32 i = 0;
|
||||
WRITEUINT8(p, player);
|
||||
// Context value -- if context has changed, then discard vote update.
|
||||
// This is to prevent votes being registered from different vote types.
|
||||
// Currently used for Duel vs Normal votes.
|
||||
WRITEUINT8(p, Y_VoteContext());
|
||||
|
||||
WRITEUINT8(p, player);
|
||||
|
||||
if (player <= MAXPLAYERS)
|
||||
{
|
||||
INT32 i;
|
||||
for (i = 0; i <= splitscreen; i++)
|
||||
{
|
||||
if (g_localplayers[i] == player)
|
||||
|
|
@ -2529,7 +2529,7 @@ static void Command_Map_f(void)
|
|||
newforcespecialstage = COM_CheckParm("-forcespecialstage");
|
||||
|
||||
usingcheats = CV_CheatsEnabled();
|
||||
ischeating = (!(netgame || multiplayer)) || (!newresetplayers);
|
||||
ischeating = (!(netgame || multiplayer)) || (!newresetplayers) || (!K_CanChangeRules(false));
|
||||
|
||||
if (!( first_option = COM_FirstOption() ))
|
||||
first_option = COM_Argc();
|
||||
|
|
@ -2984,7 +2984,7 @@ static void Command_RestartLevel(void)
|
|||
D_MapChange(gamemap, g_lastgametype, newencore, false, 0, false, false);
|
||||
}
|
||||
|
||||
static void Handle_MapQueueSend(UINT16 newmapnum, UINT16 newgametype, boolean newencoremode)
|
||||
void Handle_MapQueueSend(UINT16 newmapnum, UINT16 newgametype, boolean newencoremode)
|
||||
{
|
||||
UINT8 flags = 0;
|
||||
|
||||
|
|
@ -5636,30 +5636,35 @@ static void Got_SetupVotecmd(const UINT8 **cp, INT32 playernum)
|
|||
|
||||
static void Got_ModifyVotecmd(const UINT8 **cp, INT32 playernum)
|
||||
{
|
||||
UINT8 context = READUINT8(*cp);
|
||||
UINT8 targetID = READUINT8(*cp);
|
||||
SINT8 vote = READSINT8(*cp);
|
||||
|
||||
if (targetID == UINT8_MAX)
|
||||
if (context != Y_VoteContext())
|
||||
{
|
||||
if (playernum != serverplayer) // server-only special vote
|
||||
// Silently discard. Server changed the
|
||||
// vote type as we were sending our vote.
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetID >= MAXPLAYERS)
|
||||
{
|
||||
// only the server is allowed to send these
|
||||
if (playernum != serverplayer)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
targetID = VOTE_SPECIAL;
|
||||
}
|
||||
else if (playeringame[targetID] == true && players[targetID].bot == true)
|
||||
{
|
||||
if (targetID >= MAXPLAYERS
|
||||
|| playernum != serverplayer)
|
||||
if (playernum != serverplayer)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (targetID >= MAXPLAYERS
|
||||
|| playernode[targetID] != playernode[playernum])
|
||||
if (playernode[targetID] != playernode[playernum])
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ extern consvar_t cv_joyscale[MAXSPLITSCREENPLAYERS];
|
|||
|
||||
extern consvar_t cv_pointlimit;
|
||||
extern consvar_t cv_timelimit;
|
||||
extern consvar_t cv_dueltimelimit, cv_duelscorelimit;
|
||||
extern consvar_t cv_numlaps;
|
||||
extern UINT32 timelimitintics, extratimeintics, secretextratime;
|
||||
extern UINT32 g_pointlimit;
|
||||
|
|
@ -205,6 +206,7 @@ size_t WeaponPref_Parse(const UINT8 *p, INT32 playernum);
|
|||
void D_SendPlayerConfig(UINT8 n);
|
||||
void Command_ExitGame_f(void);
|
||||
void Command_Retry_f(void);
|
||||
void Handle_MapQueueSend(UINT16 newmapnum, UINT16 newgametype, boolean newencoremode);
|
||||
boolean G_GamestateUsesExitLevel(void);
|
||||
void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore
|
||||
void D_MapChange(UINT16 pmapnum, INT32 pgametype, boolean pencoremode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pforcespecialstage);
|
||||
|
|
|
|||
|
|
@ -830,6 +830,8 @@ struct player_t
|
|||
UINT8 numsneakers; // Number of stacked sneaker effects
|
||||
UINT16 panelsneakertimer;
|
||||
UINT8 numpanelsneakers;
|
||||
UINT16 weaksneakertimer;
|
||||
UINT8 numweaksneakers;
|
||||
UINT8 floorboost; // (0 to 3) - Prevents Sneaker sounds for a brief duration when triggered by a floor panel
|
||||
|
||||
INT16 growshrinktimer; // > 0 = Big, < 0 = small
|
||||
|
|
@ -959,6 +961,8 @@ struct player_t
|
|||
INT32 cheatchecknum; // The number of the last cheatcheck you hit
|
||||
INT32 checkpointId; // Players respawn here, objects/checkpoint.cpp
|
||||
|
||||
INT16 duelscore;
|
||||
|
||||
UINT8 team; // 0 == Spectator, 1 == Red, 2 == Blue
|
||||
|
||||
UINT8 checkskip; // Skipping checkpoints? Oh no no no
|
||||
|
|
|
|||
|
|
@ -297,6 +297,7 @@ actionpointer_t actionpointers[] =
|
|||
{{A_MakeSSCandle}, "A_MAKESSCANDLE"},
|
||||
{{A_HologramRandomTranslucency}, "A_HOLOGRAMRANDOMTRANSLUCENCY"},
|
||||
{{A_SSChainShatter}, "A_SSCHAINSHATTER"},
|
||||
{{A_GenericBumper}, "A_GENERICBUMPER"},
|
||||
|
||||
{{NULL}, "NONE"},
|
||||
|
||||
|
|
|
|||
|
|
@ -745,7 +745,7 @@ extern int
|
|||
#define MAXAMPSCALINGDIST 18000
|
||||
|
||||
// Exp
|
||||
#define MINEXP 50 // The min value target
|
||||
#define MINEXP 25 // The min value target
|
||||
#define TARGETEXP 100 // The target value needed for A rank
|
||||
#define MAXEXP 125 // The max value displayed by the hud and in the tally screen and GP results screen
|
||||
|
||||
|
|
|
|||
|
|
@ -882,16 +882,24 @@ extern SINT8 spbplace;
|
|||
extern boolean rainbowstartavailable;
|
||||
extern tic_t linecrossed;
|
||||
extern boolean inDuel;
|
||||
extern UINT8 overtimecheckpoints;
|
||||
|
||||
extern tic_t bombflashtimer; // Used to avoid causing seizures if multiple mines explode close to you :)
|
||||
extern boolean legitimateexit;
|
||||
extern boolean comebackshowninfo;
|
||||
|
||||
#define VOTE_NUM_LEVELS (4)
|
||||
#define VOTE_NOT_PICKED (-1)
|
||||
#define VOTE_SPECIAL (MAXPLAYERS)
|
||||
#define VOTE_TOTAL (MAXPLAYERS+1)
|
||||
extern UINT16 g_voteLevels[4][2];
|
||||
|
||||
#define VOTE_TIMEOUT_LOSER (MAXPLAYERS+1) // not a real vote ID
|
||||
#define VOTE_TIMEOUT_WINNER (MAXPLAYERS+2) // ditto
|
||||
|
||||
extern UINT16 g_voteLevels[VOTE_NUM_LEVELS][2];
|
||||
extern SINT8 g_votes[VOTE_TOTAL];
|
||||
extern SINT8 g_pickedVote;
|
||||
extern boolean g_votes_striked[VOTE_NUM_LEVELS];
|
||||
|
||||
// ===========================
|
||||
// Internal parameters, fixed.
|
||||
|
|
|
|||
|
|
@ -404,7 +404,10 @@ class TiccmdBuilder
|
|||
map(gc_item, BT_ATTACK); // fire
|
||||
|
||||
map(gc_lookback, BT_LOOKBACK); // rear view
|
||||
map(gc_respawn, BT_RESPAWN | (freecam() ? 0 : BT_EBRAKEMASK)); // respawn
|
||||
if (!modeattacking)
|
||||
{
|
||||
map(gc_respawn, BT_RESPAWN | (freecam() ? 0 : BT_EBRAKEMASK)); // respawn
|
||||
}
|
||||
map(gc_vote, BT_VOTE); // mp general function button
|
||||
|
||||
// lua buttons a thru c
|
||||
|
|
|
|||
413
src/g_demo.cpp
413
src/g_demo.cpp
|
|
@ -270,22 +270,31 @@ static ticcmd_t oldcmd[MAXPLAYERS];
|
|||
|
||||
static mobj_t oldghost[MAXPLAYERS];
|
||||
|
||||
boolean G_ConsiderEndingDemoWrite(void)
|
||||
{
|
||||
// chill, we reserved extra memory so it's
|
||||
// "safe" to have written a bit past the end
|
||||
if (demobuf.p < demobuf.end)
|
||||
return false;
|
||||
|
||||
G_CheckDemoStatus();
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean G_ConsiderEndingDemoRead(void)
|
||||
{
|
||||
if (*demobuf.p != DEMOMARKER)
|
||||
return false;
|
||||
|
||||
G_CheckDemoStatus();
|
||||
return true;
|
||||
}
|
||||
|
||||
void G_ReadDemoExtraData(void)
|
||||
{
|
||||
INT32 p, extradata, i;
|
||||
char name[64];
|
||||
static_assert(sizeof name >= std::max({MAXPLAYERNAME+1u, SKINNAMESIZE+1u, MAXCOLORNAME+1u}));
|
||||
|
||||
if (leveltime > starttime)
|
||||
{
|
||||
rewind_t *rewind = CL_SaveRewindPoint(demobuf.p - demobuf.buffer);
|
||||
if (rewind)
|
||||
{
|
||||
memcpy(rewind->oldcmd, oldcmd, sizeof (oldcmd));
|
||||
memcpy(rewind->oldghost, oldghost, sizeof (oldghost));
|
||||
}
|
||||
}
|
||||
|
||||
memset(name, '\0', sizeof name);
|
||||
|
||||
p = READUINT8(demobuf.p);
|
||||
|
|
@ -460,13 +469,6 @@ void G_ReadDemoExtraData(void)
|
|||
|
||||
p = READUINT8(demobuf.p);
|
||||
}
|
||||
|
||||
if (!(demoflags & DF_GHOST) && *demobuf.p == DEMOMARKER)
|
||||
{
|
||||
// end of demo data stream
|
||||
G_CheckDemoStatus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void G_WriteDemoExtraData(void)
|
||||
|
|
@ -623,13 +625,6 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
|
|||
}
|
||||
|
||||
G_CopyTiccmd(cmd, &oldcmd[playernum], 1);
|
||||
|
||||
if (!(demoflags & DF_GHOST) && *demobuf.p == DEMOMARKER)
|
||||
{
|
||||
// end of demo data stream
|
||||
G_CheckDemoStatus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
|
||||
|
|
@ -739,14 +734,6 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
|
|||
|
||||
WRITEUINT16(botziptic_p, botziptic);
|
||||
}
|
||||
|
||||
// attention here for the ticcmd size!
|
||||
// latest demos with mouse aiming byte in ticcmd
|
||||
if (!(demoflags & DF_GHOST) && ziptic_p > demobuf.end - 9)
|
||||
{
|
||||
G_CheckDemoStatus(); // no more space
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void G_GhostAddFlip(INT32 playernum)
|
||||
|
|
@ -794,8 +781,11 @@ void G_GhostAddHit(INT32 playernum, mobj_t *victim)
|
|||
|
||||
void G_WriteAllGhostTics(void)
|
||||
{
|
||||
boolean toobig = false;
|
||||
INT32 i, counter = leveltime;
|
||||
|
||||
if (!demobuf.p || !(demoflags & DF_GHOST))
|
||||
return; // No ghost data to write.
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator)
|
||||
|
|
@ -811,22 +801,9 @@ void G_WriteAllGhostTics(void)
|
|||
|
||||
WRITEUINT8(demobuf.p, i);
|
||||
G_WriteGhostTic(players[i].mo, i);
|
||||
|
||||
// attention here for the ticcmd size!
|
||||
// latest demos with mouse aiming byte in ticcmd
|
||||
if (demobuf.p >= demobuf.end - (13 + 9 + 9))
|
||||
{
|
||||
toobig = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WRITEUINT8(demobuf.p, 0xFF);
|
||||
|
||||
if (toobig)
|
||||
{
|
||||
G_CheckDemoStatus(); // no more space
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void G_WriteGhostTic(mobj_t *ghost, INT32 playernum)
|
||||
|
|
@ -835,11 +812,6 @@ void G_WriteGhostTic(mobj_t *ghost, INT32 playernum)
|
|||
UINT8 *ziptic_p;
|
||||
UINT32 i;
|
||||
|
||||
if (!demobuf.p)
|
||||
return;
|
||||
if (!(demoflags & DF_GHOST))
|
||||
return; // No ghost data to write.
|
||||
|
||||
ziptic_p = demobuf.p++; // the ziptic, written at the end of this function
|
||||
|
||||
#define MAXMOM (0xFFFF<<8)
|
||||
|
|
@ -1061,7 +1033,7 @@ void G_ConsAllGhostTics(void)
|
|||
{
|
||||
UINT8 p;
|
||||
|
||||
if (!demobuf.p || !demo.deferstart)
|
||||
if (!demobuf.p || !(demoflags & DF_GHOST) || !demo.deferstart)
|
||||
return;
|
||||
|
||||
p = READUINT8(demobuf.p);
|
||||
|
|
@ -1071,13 +1043,6 @@ void G_ConsAllGhostTics(void)
|
|||
G_ConsGhostTic(p);
|
||||
p = READUINT8(demobuf.p);
|
||||
}
|
||||
|
||||
if (*demobuf.p == DEMOMARKER)
|
||||
{
|
||||
// end of demo data stream
|
||||
G_CheckDemoStatus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Uses ghost data to do consistency checks on your position.
|
||||
|
|
@ -1089,9 +1054,6 @@ void G_ConsGhostTic(INT32 playernum)
|
|||
mobj_t *testmo;
|
||||
UINT32 syncleeway;
|
||||
|
||||
if (!(demoflags & DF_GHOST))
|
||||
return; // No ghost data to use.
|
||||
|
||||
testmo = players[playernum].mo;
|
||||
|
||||
// Grab ghost data.
|
||||
|
|
@ -1282,13 +1244,6 @@ void G_ConsGhostTic(INT32 playernum)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (*demobuf.p == DEMOMARKER)
|
||||
{
|
||||
// end of demo data stream
|
||||
G_CheckDemoStatus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void G_GhostTicker(void)
|
||||
|
|
@ -1309,11 +1264,31 @@ void G_GhostTicker(void)
|
|||
continue;
|
||||
|
||||
readghosttic:
|
||||
#define follow g->mo->tracer
|
||||
|
||||
// Skip normal demo data.
|
||||
ziptic = READUINT8(g->p);
|
||||
xziptic = 0;
|
||||
|
||||
// Demo ends after ghost data.
|
||||
if (ziptic == DEMOMARKER)
|
||||
{
|
||||
fadeghost:
|
||||
g->mo->momx = g->mo->momy = g->mo->momz = 0;
|
||||
g->mo->fuse = TICRATE;
|
||||
if (follow)
|
||||
{
|
||||
follow->fuse = TICRATE;
|
||||
}
|
||||
|
||||
g->done = true;
|
||||
if (p)
|
||||
{
|
||||
p->next = g->next;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
while (ziptic != DW_END) // Get rid of extradata stuff
|
||||
{
|
||||
if (ziptic < MAXPLAYERS)
|
||||
|
|
@ -1414,9 +1389,11 @@ readghosttic:
|
|||
// Grab ghost data.
|
||||
ziptic = READUINT8(g->p);
|
||||
|
||||
if (ziptic == DEMOMARKER) // Had to end early for some reason
|
||||
goto fadeghost;
|
||||
if (ziptic == 0xFF)
|
||||
goto skippedghosttic; // Didn't write ghost info this frame
|
||||
else if (ziptic != 0)
|
||||
if (ziptic != 0)
|
||||
I_Error("Ghost is not a record attack ghost ZIPTIC"); //@TODO lmao don't blow up like this
|
||||
ziptic = READUINT8(g->p);
|
||||
|
||||
|
|
@ -1530,7 +1507,6 @@ readghosttic:
|
|||
g->mo->renderflags &= ~RF_DONTDRAW;
|
||||
}
|
||||
|
||||
#define follow g->mo->tracer
|
||||
if (ziptic & GZT_FOLLOW)
|
||||
{ // Even more...
|
||||
UINT8 followtic = READUINT8(g->p);
|
||||
|
|
@ -1620,28 +1596,6 @@ skippedghosttic:
|
|||
if (READUINT8(g->p) != 0xFF) // Make sure there isn't other ghost data here.
|
||||
I_Error("Ghost is not a record attack ghost GHOSTEND"); //@TODO lmao don't blow up like this
|
||||
|
||||
// Demo ends after ghost data.
|
||||
if (*g->p == DEMOMARKER)
|
||||
{
|
||||
g->mo->momx = g->mo->momy = g->mo->momz = 0;
|
||||
#if 0 // freeze frame (maybe more useful for time attackers) (2024-03-11: you leave it behind anyway!)
|
||||
g->mo->colorized = true;
|
||||
g->mo->fuse = 10*TICRATE;
|
||||
if (follow)
|
||||
follow->colorized = true;
|
||||
#else // dissapearing act
|
||||
g->mo->fuse = TICRATE;
|
||||
if (follow)
|
||||
follow->fuse = TICRATE;
|
||||
#endif
|
||||
g->done = true;
|
||||
if (p)
|
||||
{
|
||||
p->next = g->next;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the timer started, skip ahead until the ghost starts too.
|
||||
if (starttime <= leveltime && !g->linecrossed && G_TimeAttackStart())
|
||||
goto readghosttic;
|
||||
|
|
@ -1651,203 +1605,6 @@ skippedghosttic:
|
|||
}
|
||||
}
|
||||
|
||||
// Demo rewinding functions
|
||||
typedef struct rewindinfo_s {
|
||||
tic_t leveltime;
|
||||
|
||||
struct {
|
||||
boolean ingame;
|
||||
player_t player;
|
||||
mobj_t mobj;
|
||||
} playerinfo[MAXPLAYERS];
|
||||
|
||||
struct rewindinfo_s *prev;
|
||||
} rewindinfo_t;
|
||||
|
||||
static tic_t currentrewindnum;
|
||||
static rewindinfo_t *rewindhead = NULL; // Reverse chronological order
|
||||
|
||||
void G_InitDemoRewind(void)
|
||||
{
|
||||
CL_ClearRewinds();
|
||||
|
||||
while (rewindhead)
|
||||
{
|
||||
rewindinfo_t *p = rewindhead->prev;
|
||||
Z_Free(rewindhead);
|
||||
rewindhead = p;
|
||||
}
|
||||
|
||||
currentrewindnum = 0;
|
||||
}
|
||||
|
||||
void G_StoreRewindInfo(void)
|
||||
{
|
||||
static UINT8 timetolog = 8;
|
||||
rewindinfo_t *info;
|
||||
size_t i;
|
||||
|
||||
if (timetolog-- > 0)
|
||||
return;
|
||||
timetolog = 8;
|
||||
|
||||
info = static_cast<rewindinfo_t*>(Z_Calloc(sizeof(rewindinfo_t), PU_STATIC, NULL));
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator)
|
||||
{
|
||||
info->playerinfo[i].ingame = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
info->playerinfo[i].ingame = true;
|
||||
memcpy(&info->playerinfo[i].player, &players[i], sizeof(player_t));
|
||||
if (players[i].mo)
|
||||
memcpy(&info->playerinfo[i].mobj, players[i].mo, sizeof(mobj_t));
|
||||
}
|
||||
|
||||
info->leveltime = leveltime;
|
||||
info->prev = rewindhead;
|
||||
rewindhead = info;
|
||||
}
|
||||
|
||||
void G_PreviewRewind(tic_t previewtime)
|
||||
{
|
||||
SINT8 i;
|
||||
//size_t j;
|
||||
fixed_t tweenvalue = 0;
|
||||
rewindinfo_t *info = rewindhead, *next_info = rewindhead;
|
||||
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
while (info->leveltime > previewtime && info->prev)
|
||||
{
|
||||
next_info = info;
|
||||
info = info->prev;
|
||||
}
|
||||
if (info != next_info)
|
||||
tweenvalue = FixedDiv(previewtime - info->leveltime, next_info->leveltime - info->leveltime);
|
||||
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator)
|
||||
{
|
||||
if (info->playerinfo[i].player.mo)
|
||||
{
|
||||
//@TODO spawn temp object to act as a player display
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!info->playerinfo[i].ingame || !info->playerinfo[i].player.mo)
|
||||
{
|
||||
if (players[i].mo)
|
||||
players[i].mo->renderflags |= RF_DONTDRAW;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!players[i].mo)
|
||||
continue; //@TODO spawn temp object to act as a player display
|
||||
|
||||
players[i].mo->renderflags &= ~RF_DONTDRAW;
|
||||
|
||||
P_UnsetThingPosition(players[i].mo);
|
||||
#define TWEEN(pr) info->playerinfo[i].mobj.pr + FixedMul((INT32) (next_info->playerinfo[i].mobj.pr - info->playerinfo[i].mobj.pr), tweenvalue)
|
||||
players[i].mo->x = TWEEN(x);
|
||||
players[i].mo->y = TWEEN(y);
|
||||
players[i].mo->z = TWEEN(z);
|
||||
players[i].mo->angle = TWEEN(angle);
|
||||
#undef TWEEN
|
||||
P_SetThingPosition(players[i].mo);
|
||||
|
||||
players[i].drawangle = info->playerinfo[i].player.drawangle + FixedMul((INT32) (next_info->playerinfo[i].player.drawangle - info->playerinfo[i].player.drawangle), tweenvalue);
|
||||
|
||||
players[i].mo->sprite = info->playerinfo[i].mobj.sprite;
|
||||
players[i].mo->sprite2 = info->playerinfo[i].mobj.sprite2;
|
||||
players[i].mo->frame = info->playerinfo[i].mobj.frame;
|
||||
|
||||
players[i].mo->hitlag = info->playerinfo[i].mobj.hitlag;
|
||||
|
||||
players[i].realtime = info->playerinfo[i].player.realtime;
|
||||
// Genuinely CANNOT be fucked. I can redo lua and I can redo netsaves but I draw the line at this abysmal hack.
|
||||
/*for (j = 0; j < NUMKARTSTUFF; j++)
|
||||
players[i].kartstuff[j] = info->playerinfo[i].player.kartstuff[j];*/
|
||||
}
|
||||
|
||||
for (i = splitscreen; i >= 0; i--)
|
||||
P_ResetCamera(&players[displayplayers[i]], &camera[i]);
|
||||
}
|
||||
|
||||
void G_ConfirmRewind(tic_t rewindtime)
|
||||
{
|
||||
SINT8 i;
|
||||
tic_t j;
|
||||
boolean oldmenuactive = menuactive, oldsounddisabled = sound_disabled;
|
||||
|
||||
INT32 olddp1 = displayplayers[0], olddp2 = displayplayers[1], olddp3 = displayplayers[2], olddp4 = displayplayers[3];
|
||||
UINT8 oldss = splitscreen;
|
||||
|
||||
menuactive = false; // Prevent loops
|
||||
|
||||
CV_StealthSetValue(&cv_renderview, 0);
|
||||
|
||||
if (rewindtime <= starttime)
|
||||
{
|
||||
demo.rewinding = true; // this doesn't APPEAR to cause any misery, and it allows us to prevent running all the wipes again
|
||||
G_DoPlayDemo(NULL); // Restart the current demo
|
||||
}
|
||||
else
|
||||
{
|
||||
rewind_t *rewind;
|
||||
sound_disabled = true; // Prevent sound spam
|
||||
demo.rewinding = true;
|
||||
|
||||
rewind = CL_RewindToTime(rewindtime);
|
||||
|
||||
if (rewind)
|
||||
{
|
||||
demobuf.p = demobuf.buffer + rewind->demopos;
|
||||
memcpy(oldcmd, rewind->oldcmd, sizeof (oldcmd));
|
||||
memcpy(oldghost, rewind->oldghost, sizeof (oldghost));
|
||||
paused = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
demo.rewinding = true;
|
||||
G_DoPlayDemo(NULL); // Restart the current demo
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < rewindtime && leveltime < rewindtime; j++)
|
||||
{
|
||||
G_Ticker((j % NEWTICRATERATIO) == 0);
|
||||
}
|
||||
|
||||
demo.rewinding = false;
|
||||
menuactive = oldmenuactive; // Bring the menu back up
|
||||
sound_disabled = oldsounddisabled; // Re-enable SFX
|
||||
|
||||
wipegamestate = gamestate; // No fading back in!
|
||||
|
||||
COM_BufInsertText("renderview on\n");
|
||||
|
||||
splitscreen = oldss;
|
||||
displayplayers[0] = olddp1;
|
||||
displayplayers[1] = olddp2;
|
||||
displayplayers[2] = olddp3;
|
||||
displayplayers[3] = olddp4;
|
||||
R_ExecuteSetViewSize();
|
||||
G_ResetViews();
|
||||
|
||||
for (i = splitscreen; i >= 0; i--)
|
||||
P_ResetCamera(&players[displayplayers[i]], &camera[i]);
|
||||
}
|
||||
|
||||
//
|
||||
// G_RecordDemo
|
||||
//
|
||||
|
|
@ -1885,7 +1642,7 @@ void G_RecordDemo(const char *name)
|
|||
functions will check if they overran the buffer, but it
|
||||
should be safe enough because they'll think there's less
|
||||
memory than there actually is and stop early. */
|
||||
const size_t deadspace = 1024;
|
||||
const size_t deadspace = 2048;
|
||||
I_Assert(demobuf.size > deadspace);
|
||||
demobuf.size -= deadspace;
|
||||
demobuf.end -= deadspace;
|
||||
|
|
@ -2368,6 +2125,8 @@ void G_BeginRecording(void)
|
|||
WRITEUINT8(demobuf.p, player->skin);
|
||||
WRITEUINT8(demobuf.p, player->lastfakeskin);
|
||||
|
||||
WRITEUINT8(demobuf.p, player->team);
|
||||
|
||||
// Color
|
||||
demobuf.p += copy_fixed_buf(demobuf.p, skincolors[player->skincolor].name, g_buffer_sizes.color_name);
|
||||
|
||||
|
|
@ -2997,8 +2756,6 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum)
|
|||
boolean skiperrors = true;
|
||||
#endif
|
||||
|
||||
G_InitDemoRewind();
|
||||
|
||||
gtname[MAXGAMETYPELENGTH-1] = '\0';
|
||||
|
||||
if (deflumpnum != LUMPERROR)
|
||||
|
|
@ -3361,22 +3118,6 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum)
|
|||
// Load "mapmusrng" used for altmusic selection
|
||||
mapmusrng = READUINT8(demobuf.p);
|
||||
|
||||
// Sigh ... it's an empty demo.
|
||||
if (*demobuf.p == DEMOMARKER)
|
||||
{
|
||||
snprintf(msg, 1024, M_GetText("%s contains no data to be played.\n"), pdemoname);
|
||||
CONS_Alert(CONS_ERROR, "%s", msg);
|
||||
M_StartMessage("Demo Playback", msg, NULL, MM_NOTHING, NULL, "Return to Menu");
|
||||
Z_Free(demo.skinlist);
|
||||
demo.skinlist = NULL;
|
||||
Z_Free(pdemoname);
|
||||
Z_Free(demobuf.buffer);
|
||||
demo.playback = false;
|
||||
return;
|
||||
}
|
||||
|
||||
Z_Free(pdemoname);
|
||||
|
||||
memset(&oldcmd,0,sizeof(oldcmd));
|
||||
memset(&oldghost,0,sizeof(oldghost));
|
||||
memset(&ghostext,0,sizeof(ghostext));
|
||||
|
|
@ -3500,6 +3241,8 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum)
|
|||
demo.currentskinid[p] = 0;
|
||||
lastfakeskin[p] = READUINT8(demobuf.p);
|
||||
|
||||
players[p].team = READUINT8(demobuf.p);
|
||||
|
||||
// Color
|
||||
demobuf.p += copy_fixed_buf(color, demobuf.p, g_buffer_sizes.color_name);
|
||||
for (i = 0; i < numskincolors; i++)
|
||||
|
|
@ -3603,9 +3346,26 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum)
|
|||
players[p].lastfakeskin = lastfakeskin[p];
|
||||
}
|
||||
|
||||
// Sigh ... it's an empty demo.
|
||||
if (*demobuf.p == DEMOMARKER)
|
||||
{
|
||||
snprintf(msg, 1024, M_GetText("%s contains no data to be played.\n"), pdemoname);
|
||||
CONS_Alert(CONS_ERROR, "%s", msg);
|
||||
M_StartMessage("Demo Playback", msg, NULL, MM_NOTHING, NULL, "Return to Menu");
|
||||
Z_Free(demo.skinlist);
|
||||
demo.skinlist = NULL;
|
||||
Z_Free(pdemoname);
|
||||
Z_Free(demobuf.buffer);
|
||||
demo.playback = false;
|
||||
return;
|
||||
}
|
||||
|
||||
Z_Free(pdemoname);
|
||||
|
||||
demo.deferstart = true;
|
||||
|
||||
CV_StealthSetValue(&cv_playbackspeed, 1);
|
||||
if (demo.simplerewind == DEMO_REWIND_OFF)
|
||||
CV_StealthSetValue(&cv_playbackspeed, 1);
|
||||
}
|
||||
|
||||
void G_AddGhost(savebuffer_t *buffer, const char *defdemoname)
|
||||
|
|
@ -3752,14 +3512,6 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname)
|
|||
|
||||
p++; // mapmusrng
|
||||
|
||||
if (*p == DEMOMARKER)
|
||||
{
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Replay is empty.\n"), defdemoname);
|
||||
Z_Free(skinlist);
|
||||
P_SaveBufferFree(buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
p++; // player number - doesn't really need to be checked, TODO maybe support adding multiple players' ghosts at once
|
||||
|
||||
// any invalidating flags?
|
||||
|
|
@ -3783,6 +3535,8 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname)
|
|||
ghskin = &skins[skinlist[i].mapping];
|
||||
p++; // lastfakeskin
|
||||
|
||||
p++; // team
|
||||
|
||||
// Color
|
||||
p += copy_fixed_buf(color, p, ghostsizes.color_name);
|
||||
|
||||
|
|
@ -3805,6 +3559,14 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname)
|
|||
return;
|
||||
}
|
||||
|
||||
if (*p == DEMOMARKER)
|
||||
{
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Replay is empty.\n"), defdemoname);
|
||||
Z_Free(skinlist);
|
||||
P_SaveBufferFree(buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
gh = static_cast<demoghost*>(Z_Calloc(sizeof(demoghost), PU_LEVEL, NULL));
|
||||
gh->sizes = ghostsizes;
|
||||
|
|
@ -4167,7 +3929,7 @@ boolean G_CheckDemoStatus(void)
|
|||
// Keep the demo open and don't boot to intermission
|
||||
// YET, pause demo playback.
|
||||
if (!demo.waitingfortally && modeattacking && exitcountdown)
|
||||
demo.waitingfortally = true;
|
||||
;
|
||||
else if (!demo.attract)
|
||||
G_FinishExitLevel();
|
||||
else
|
||||
|
|
@ -4185,6 +3947,8 @@ boolean G_CheckDemoStatus(void)
|
|||
D_SetDeferredStartTitle(true);
|
||||
}
|
||||
|
||||
demo.waitingfortally = true; // if we've returned early for some reason...
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -4229,6 +3993,13 @@ void G_SaveDemo(void)
|
|||
if (currentMenu == &TitleEntryDef)
|
||||
M_ClearMenus(true);
|
||||
|
||||
if (!leveltime)
|
||||
{
|
||||
// Why would you save if nothing has been recorded
|
||||
G_ResetDemoRecording();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure extrainfo pointer is always available, even if no info is present.
|
||||
if (demoinfo_p && *(UINT32 *)demoinfo_p == 0)
|
||||
{
|
||||
|
|
|
|||
16
src/g_demo.h
16
src/g_demo.h
|
|
@ -84,7 +84,7 @@ struct demovars_s {
|
|||
boolean recording, playback, timing;
|
||||
UINT16 version; // Current file format of the demo being played
|
||||
UINT8 attract; // Attract demo can be cancelled by any key
|
||||
boolean rewinding; // Rewind in progress
|
||||
UINT8 simplerewind;
|
||||
|
||||
boolean loadfiles, ignorefiles; // Demo file loading options
|
||||
boolean quitafterplaying; // quit after playing a demo from cmdline
|
||||
|
|
@ -172,6 +172,8 @@ extern UINT8 demo_writerng;
|
|||
boolean G_CompatLevel(UINT16 level);
|
||||
|
||||
// Record/playback tics
|
||||
boolean G_ConsiderEndingDemoRead(void);
|
||||
boolean G_ConsiderEndingDemoWrite(void);
|
||||
void G_ReadDemoExtraData(void);
|
||||
void G_WriteDemoExtraData(void);
|
||||
void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum);
|
||||
|
|
@ -186,11 +188,6 @@ void G_ConsAllGhostTics(void);
|
|||
void G_ConsGhostTic(INT32 playernum);
|
||||
void G_GhostTicker(void);
|
||||
|
||||
void G_InitDemoRewind(void);
|
||||
void G_StoreRewindInfo(void);
|
||||
void G_PreviewRewind(tic_t previewtime);
|
||||
void G_ConfirmRewind(tic_t rewindtime);
|
||||
|
||||
struct DemoBufferSizes
|
||||
{
|
||||
size_t player_name;
|
||||
|
|
@ -250,6 +247,13 @@ typedef enum
|
|||
DEMO_ATTRACT_CREDITS
|
||||
} demoAttractMode_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DEMO_REWIND_OFF = 0,
|
||||
DEMO_REWIND_RESUME,
|
||||
DEMO_REWIND_PAUSE
|
||||
} demoRewindMode_t;
|
||||
|
||||
void G_SyncDemoParty(INT32 rem, INT32 newsplitscreen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
85
src/g_game.c
85
src/g_game.c
|
|
@ -297,9 +297,10 @@ boolean franticitems; // Frantic items currently enabled?
|
|||
boolean g_teamplay;
|
||||
|
||||
// Voting system
|
||||
UINT16 g_voteLevels[4][2]; // Levels that were rolled by the host
|
||||
UINT16 g_voteLevels[VOTE_NUM_LEVELS][2]; // Levels that were rolled by the host
|
||||
SINT8 g_votes[VOTE_TOTAL]; // Each player's vote
|
||||
SINT8 g_pickedVote; // What vote the host rolls
|
||||
boolean g_votes_striked[VOTE_NUM_LEVELS]; // Which levels were striked from votes?
|
||||
|
||||
// Server-sided, synched variables
|
||||
tic_t wantedcalcdelay; // Time before it recalculates WANTED
|
||||
|
|
@ -311,6 +312,7 @@ SINT8 spbplace; // SPB exists, give the person behind better items
|
|||
boolean rainbowstartavailable; // Boolean, keeps track of if the rainbow start was gotten
|
||||
tic_t linecrossed; // For Time Attack
|
||||
boolean inDuel; // Boolean, keeps track of if it is a 1v1
|
||||
UINT8 overtimecheckpoints; // Duel overtime speedups!
|
||||
|
||||
// Client-sided, unsynched variables (NEVER use in anything that needs to be synced with other players)
|
||||
tic_t bombflashtimer = 0; // Cooldown before another FlashPal can be intialized by a bomb exploding near a displayplayer. Avoids seizures.
|
||||
|
|
@ -1243,26 +1245,36 @@ void G_StartTitleCard(void)
|
|||
// prepare status bar
|
||||
ST_startTitleCard(); // <-- always must be called to init some variables
|
||||
|
||||
// The title card has been disabled for this map.
|
||||
// Oh well.
|
||||
if (demo.rewinding || !G_IsTitleCardAvailable())
|
||||
{
|
||||
WipeStageTitle = false;
|
||||
if (demo.simplerewind)
|
||||
return;
|
||||
|
||||
sfxenum_t kstart = 0;
|
||||
|
||||
if (K_CheckBossIntro() == true)
|
||||
{
|
||||
kstart = sfx_ssa021;
|
||||
}
|
||||
else if (encoremode)
|
||||
{
|
||||
kstart = sfx_ruby2;
|
||||
}
|
||||
|
||||
if (kstart)
|
||||
{
|
||||
// Play the guaranteed alt sounds
|
||||
S_StartSound(NULL, kstart);
|
||||
}
|
||||
|
||||
if (!G_IsTitleCardAvailable())
|
||||
return;
|
||||
|
||||
// start the title card
|
||||
WipeStageTitle = (gamestate == GS_LEVEL);
|
||||
|
||||
// play the sound
|
||||
if (WipeStageTitle)
|
||||
if (WipeStageTitle && !kstart)
|
||||
{
|
||||
sfxenum_t kstart = sfx_kstart;
|
||||
if (K_CheckBossIntro() == true)
|
||||
kstart = sfx_ssa021;
|
||||
else if (encoremode == true)
|
||||
kstart = sfx_ruby2;
|
||||
S_StartSound(NULL, kstart);
|
||||
// Play the standard titlecard sound
|
||||
S_StartSound(NULL, sfx_kstart);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1437,13 +1449,7 @@ boolean G_Responder(event_t *ev)
|
|||
{
|
||||
paused = !paused;
|
||||
|
||||
if (demo.rewinding)
|
||||
{
|
||||
G_ConfirmRewind(leveltime);
|
||||
paused = true;
|
||||
S_PauseAudio();
|
||||
}
|
||||
else if (paused)
|
||||
if (paused)
|
||||
S_PauseAudio();
|
||||
else
|
||||
S_ResumeAudio();
|
||||
|
|
@ -1554,7 +1560,7 @@ boolean G_CouldView(INT32 playernum)
|
|||
return false;
|
||||
|
||||
// SRB2Kart: we have no team-based modes, YET...
|
||||
if (G_GametypeHasTeams())
|
||||
if (G_GametypeHasTeams() && !demo.playback)
|
||||
{
|
||||
if (players[consoleplayer].spectator == false && player->team != players[consoleplayer].team)
|
||||
return false;
|
||||
|
|
@ -2148,6 +2154,14 @@ void G_Ticker(boolean run)
|
|||
|
||||
if (g_fast_forward == 0)
|
||||
{
|
||||
// Not "rewinding" anymore.
|
||||
if (demo.simplerewind == DEMO_REWIND_PAUSE)
|
||||
{
|
||||
paused = true;
|
||||
S_PauseAudio();
|
||||
}
|
||||
demo.simplerewind = DEMO_REWIND_OFF;
|
||||
|
||||
// Next fast-forward is unlimited.
|
||||
g_fast_forward_clock_stop = INFTICS;
|
||||
}
|
||||
|
|
@ -2262,6 +2276,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
INT32 kickstartaccel;
|
||||
INT32 checkpointId;
|
||||
boolean enteredGame;
|
||||
tic_t spectatewait;
|
||||
UINT8 lastsafelap;
|
||||
UINT8 lastsafecheatcheck;
|
||||
UINT16 bigwaypointgap;
|
||||
|
|
@ -2349,8 +2364,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
bot = players[player].bot;
|
||||
botdifficulty = players[player].botvars.difficulty;
|
||||
|
||||
cangrabitems = players[player].cangrabitems;
|
||||
|
||||
botdiffincrease = players[player].botvars.diffincrease;
|
||||
botrival = players[player].botvars.rival;
|
||||
|
||||
|
|
@ -2436,13 +2449,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
tallyactive = false;
|
||||
|
||||
cangrabitems = 0;
|
||||
if (gametyperules & GTR_SPHERES
|
||||
|| gametyperules & GTR_CATCHER
|
||||
|| G_TimeAttackStart()
|
||||
|| gametype == GT_TUTORIAL
|
||||
|| !M_NotFreePlay()
|
||||
|| K_GetNumWaypoints() == 0)
|
||||
cangrabitems = EARLY_ITEM_FLICKER;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -2498,6 +2504,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
{
|
||||
tally = players[player].tally;
|
||||
}
|
||||
|
||||
cangrabitems = players[player].cangrabitems;
|
||||
}
|
||||
|
||||
spectatorReentry = (betweenmaps ? 0 : players[player].spectatorReentry);
|
||||
|
|
@ -2551,6 +2559,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
checkpointId = players[player].checkpointId;
|
||||
|
||||
enteredGame = players[player].enteredGame;
|
||||
spectatewait = players[player].spectatewait;
|
||||
|
||||
p = &players[player];
|
||||
memset(p, 0, sizeof (*p));
|
||||
|
|
@ -2624,6 +2633,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
p->karthud[khud_fault] = khudfault;
|
||||
p->kickstartaccel = kickstartaccel;
|
||||
p->checkpointId = checkpointId;
|
||||
p->spectatewait = spectatewait;
|
||||
|
||||
p->ringvolume = 255;
|
||||
p->ringtransparency = 255;
|
||||
|
|
@ -3868,6 +3878,12 @@ tryAgain:
|
|||
continue;
|
||||
}
|
||||
|
||||
if (numPlayers == 2 && gametype == GT_RACE && ((mapheaderinfo[i]->levelflags & LF_SECTIONRACE) == LF_SECTIONRACE))
|
||||
{
|
||||
// Duel doesn't support sprints.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only care about restrictions if the host is a listen server.
|
||||
if (!dedicated)
|
||||
{
|
||||
|
|
@ -4090,6 +4106,7 @@ doremove:
|
|||
|
||||
// Next map apparatus
|
||||
struct roundqueue roundqueue;
|
||||
struct menuqueue menuqueue;
|
||||
|
||||
void G_MapSlipIntoRoundQueue(UINT8 position, UINT16 map, UINT8 setgametype, boolean setencore, boolean rankrestricted)
|
||||
{
|
||||
|
|
@ -5373,7 +5390,7 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr
|
|||
S_ResumeAudio();
|
||||
}
|
||||
|
||||
prevencoremode = ((!Playing()) ? false : encoremode);
|
||||
prevencoremode = encoremode;
|
||||
encoremode = pencoremode;
|
||||
|
||||
legitimateexit = false; // SRB2Kart
|
||||
|
|
@ -5415,6 +5432,8 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr
|
|||
players[i].xtralife = 0;
|
||||
players[i].totalring = 0;
|
||||
players[i].score = 0;
|
||||
if (roundqueue.position == 0) // Don't unassign teams in tournament play
|
||||
players[i].team = TEAM_UNASSIGNED;
|
||||
}
|
||||
|
||||
if (resetplayer || !(gametyperules & GTR_CHECKPOINTS && map == gamemap))
|
||||
|
|
@ -5823,7 +5842,7 @@ boolean G_GetExitGameFlag(void)
|
|||
// Same deal with retrying.
|
||||
void G_SetRetryFlag(void)
|
||||
{
|
||||
if (retrying == false)
|
||||
if (retrying == false && grandprixinfo.gp)
|
||||
{
|
||||
grandprixinfo.rank.continuesUsed++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,6 +75,15 @@ extern struct roundqueue
|
|||
roundentry_t entries[ROUNDQUEUE_MAX]; // Entries in the round queue
|
||||
} roundqueue;
|
||||
|
||||
extern struct menuqueue
|
||||
{
|
||||
// Degenerate version of roundqueue exclusively for menu use.
|
||||
UINT8 size;
|
||||
UINT8 sending;
|
||||
UINT8 anchor;
|
||||
roundentry_t entries[ROUNDQUEUE_MAX];
|
||||
} menuqueue;
|
||||
|
||||
void G_MapSlipIntoRoundQueue(UINT8 position, UINT16 map, UINT8 setgametype, boolean setencore, boolean rankrestricted);
|
||||
void G_MapIntoRoundQueue(UINT16 map, UINT8 setgametype, boolean setencore, boolean rankrestricted);
|
||||
void G_GPCupIntoRoundQueue(cupheader_t *cup, UINT8 setgametype, boolean setencore);
|
||||
|
|
|
|||
|
|
@ -3640,7 +3640,7 @@ state_t states[NUMSTATES] =
|
|||
{SPR_S_SP, FF_ANIMATE|FF_SEMIBRIGHT, -1, {NULL}, 3, 2, S_NULL}, // S_SLSTMACE
|
||||
|
||||
// MT_SEALEDSTAR_BUMPER
|
||||
{SPR_SBMP, 0|FF_FULLBRIGHT, -1, {NULL}, 2, 8, S_SEALEDSTAR_BUMPER}, // S_SEALEDSTAR_BUMPER
|
||||
{SPR_SBMP, 0|FF_FULLBRIGHT, -1, {A_GenericBumper}, 0, 56, S_SEALEDSTAR_BUMPER}, // S_SEALEDSTAR_BUMPER
|
||||
{SPR_SBMP, 1|FF_ANIMATE|FF_FULLBRIGHT, 8, {NULL}, 1, 2, S_SEALEDSTAR_BUMPER}, // S_SEALEDSTAR_BUMPERHIT
|
||||
|
||||
// MT_SSCHAIN_SPAWNER
|
||||
|
|
@ -13585,8 +13585,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
95*FRACUNIT, // radius
|
||||
95*FRACUNIT, // height
|
||||
108*FRACUNIT, // radius
|
||||
50*FRACUNIT, // height
|
||||
0, // display offset
|
||||
100, // mass
|
||||
0, // damage
|
||||
|
|
@ -22233,7 +22233,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
0, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_SPECIAL|MF_NOGRAVITY, // flags
|
||||
MF_NOGRAVITY|MF_SOLID, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
{ // MT_SSCHAIN_SPAWNER
|
||||
|
|
|
|||
|
|
@ -289,6 +289,7 @@ enum actionnum
|
|||
A_MAKESSCANDLE,
|
||||
A_HOLOGRAMRANDOMTRANSLUCENCY,
|
||||
A_SSCHAINSHATTER,
|
||||
A_GENERICBUMPER,
|
||||
NUMACTIONS
|
||||
};
|
||||
|
||||
|
|
@ -557,6 +558,7 @@ void A_BlendEyePuyoHack();
|
|||
void A_MakeSSCandle();
|
||||
void A_HologramRandomTranslucency();
|
||||
void A_SSChainShatter();
|
||||
void A_GenericBumper();
|
||||
|
||||
extern boolean actionsoverridden[NUMACTIONS];
|
||||
|
||||
|
|
|
|||
|
|
@ -2077,6 +2077,11 @@ void K_UpdateBotGameplayVars(player_t *player)
|
|||
if (cv_levelskull.value)
|
||||
player->botvars.difficulty = MAXBOTDIFFICULTY;
|
||||
|
||||
if (K_InRaceDuel())
|
||||
player->botvars.rival = true;
|
||||
else if (grandprixinfo.gp != true)
|
||||
player->botvars.rival = false;
|
||||
|
||||
player->botvars.rubberband = K_UpdateRubberband(player);
|
||||
|
||||
player->botvars.turnconfirm += player->cmd.bot.turnconfirm;
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2)
|
|||
if (t1->type == MT_BALLHOGBOOM && t2->type == MT_BALLHOGBOOM)
|
||||
return true; // Ballhogs don't collide with eachother
|
||||
|
||||
if (K_TryPickMeUp(t1, t2))
|
||||
if (K_TryPickMeUp(t1, t2, false))
|
||||
return true;
|
||||
|
||||
if (t2->player)
|
||||
|
|
@ -178,7 +178,7 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2)
|
|||
if (t1->health <= 0 || t2->health <= 0)
|
||||
return true;
|
||||
|
||||
if (K_TryPickMeUp(t1, t2))
|
||||
if (K_TryPickMeUp(t1, t2, false))
|
||||
return true;
|
||||
|
||||
if (!P_CanPickupItem(t2->player, PICKUP_EGGBOX))
|
||||
|
|
@ -434,7 +434,7 @@ boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2)
|
|||
if (t1->health <= 0 || t2->health <= 0)
|
||||
return true;
|
||||
|
||||
if (K_TryPickMeUp(t1, t2))
|
||||
if (K_TryPickMeUp(t1, t2, false))
|
||||
return true;
|
||||
|
||||
if (t2->player)
|
||||
|
|
@ -544,7 +544,7 @@ boolean K_DropTargetCollide(mobj_t *t1, mobj_t *t2)
|
|||
if (t2->player && (t2->player->hyudorotimer || t2->player->justbumped))
|
||||
return true;
|
||||
|
||||
if (K_TryPickMeUp(t1, t2))
|
||||
if (K_TryPickMeUp(t1, t2, false))
|
||||
return true;
|
||||
|
||||
if (draggeddroptarget && P_MobjWasRemoved(draggeddroptarget))
|
||||
|
|
@ -777,6 +777,12 @@ static inline BlockItReturn_t PIT_LightningShieldAttack(mobj_t *thing)
|
|||
return BMIT_CONTINUE;
|
||||
}
|
||||
|
||||
// see if it went over / under
|
||||
if (lightningSource->z - lightningDist > thing->z + thing->height)
|
||||
return BMIT_CONTINUE; // overhead
|
||||
if (lightningSource->z + lightningSource->height + lightningDist < thing->z)
|
||||
return BMIT_CONTINUE; // underneath
|
||||
|
||||
#if 0
|
||||
if (P_CheckSight(lightningSource, thing) == false)
|
||||
{
|
||||
|
|
@ -967,6 +973,7 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim)
|
|||
attackerPlayer->spindashboost = 0;
|
||||
attackerPlayer->sneakertimer = 0;
|
||||
attackerPlayer->panelsneakertimer = 0;
|
||||
attackerPlayer->weaksneakertimer = 0;
|
||||
attackerPlayer->instaWhipCharge = 0;
|
||||
attackerPlayer->flashing = 0;
|
||||
|
||||
|
|
@ -1036,7 +1043,14 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim)
|
|||
}
|
||||
else if (victim->type == MT_DROPTARGET || victim->type == MT_DROPTARGET_SHIELD)
|
||||
{
|
||||
K_DropTargetCollide(victim, shield);
|
||||
if (K_TryPickMeUp(attacker, victim, true))
|
||||
{
|
||||
shield->hitlag = attacker->hitlag; // players hitlag is handled in K_TryPickMeUp, and we need to set for the shield too
|
||||
}
|
||||
else
|
||||
{
|
||||
K_DropTargetCollide(victim, shield);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
|
@ -1053,8 +1067,13 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim)
|
|||
shield->extravalue1 = 1;
|
||||
}
|
||||
|
||||
if (P_DamageMobj(victim, shield, attacker, 1, DMG_NORMAL))
|
||||
if (K_TryPickMeUp(attacker, victim, true))
|
||||
{
|
||||
shield->hitlag = attacker->hitlag; // players hitlag is handled in K_TryPickMeUp, and we need to set for the shield too
|
||||
}
|
||||
else
|
||||
{
|
||||
P_DamageMobj(victim, shield, attacker, 1, DMG_NORMAL);
|
||||
K_AddHitLag(attacker, attackerHitlag, false);
|
||||
shield->hitlag = attacker->hitlag;
|
||||
}
|
||||
|
|
@ -1068,7 +1087,7 @@ boolean K_KitchenSinkCollide(mobj_t *t1, mobj_t *t2)
|
|||
if (((t1->target == t2) || (!(t2->flags & (MF_ENEMY|MF_BOSS)) && (t1->target == t2->target))) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0)))
|
||||
return true;
|
||||
|
||||
if (K_TryPickMeUp(t1, t2))
|
||||
if (K_TryPickMeUp(t1, t2, false))
|
||||
return true;
|
||||
|
||||
if (t2->player)
|
||||
|
|
@ -1193,7 +1212,7 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
|
|||
{
|
||||
auto shouldSteal = [](mobj_t *t1, mobj_t *t2)
|
||||
{
|
||||
return ((t1->player->sneakertimer > 0 || t1->player->panelsneakertimer > 0)
|
||||
return ((t1->player->sneakertimer > 0 || t1->player->panelsneakertimer > 0 || t1->player->weaksneakertimer > 0)
|
||||
&& !P_PlayerInPain(t1->player)
|
||||
&& (t1->player->flashing == 0));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -67,31 +67,6 @@ INT16 K_CalculateGPRankPoints(UINT16 exp, UINT8 position, UINT8 numplayers)
|
|||
|
||||
points = exp;
|
||||
|
||||
// Give bonus to high-ranking players, depending on player count
|
||||
// This rounds out the point gain when you get 1st every race,
|
||||
// and gives bots able to catch up in points if a player gets an early lead.
|
||||
// The maximum points you can get in a cup is: ((number of players - 1) + (max extra points)) * (number of races)
|
||||
// 8P: (7 + 5) * 5 = 60 maximum points
|
||||
// 12P: (11 + 5) * 5 = 80 maximum points
|
||||
// 16P: (15 + 5) * 5 = 100 maximum points
|
||||
switch (numplayers)
|
||||
{
|
||||
case 0: case 1: case 2: // 1v1
|
||||
break; // No bonus needed.
|
||||
case 3: case 4: // 3-4P
|
||||
if (position == 1) { points += 5; } // 1st gets +1 extra point
|
||||
break;
|
||||
case 5: case 6: // 5-6P
|
||||
if (position == 1) { points += 10; } // 1st gets +3 extra points
|
||||
// else if (position == 2) { points += 4; } // 2nd gets +1 extra point
|
||||
break;
|
||||
default: // Normal matches
|
||||
if (position == 1) { points += 10; } // 1st gets +5 extra points
|
||||
// else if (position == 2) { points += 5; } // 2nd gets +3 extra points
|
||||
// else if (position == 3) { points += 2; } // 3rd gets +1 extra point
|
||||
break;
|
||||
}
|
||||
|
||||
// somehow underflowed?
|
||||
if (points < 0)
|
||||
{
|
||||
|
|
|
|||
411
src/k_hud.cpp
411
src/k_hud.cpp
|
|
@ -235,6 +235,13 @@ static patch_t *kp_team_underlay[2][2];
|
|||
static patch_t *kp_team_minihead;
|
||||
static patch_t *kp_team_you;
|
||||
|
||||
static patch_t *kp_duel_foe;
|
||||
static patch_t *kp_duel_you;
|
||||
static patch_t *kp_duel_sticker;
|
||||
static patch_t *kp_duel_under;
|
||||
static patch_t *kp_duel_over;
|
||||
static patch_t *kp_duel_margin[24];
|
||||
|
||||
patch_t *kp_autoroulette;
|
||||
patch_t *kp_autoring;
|
||||
|
||||
|
|
@ -1061,6 +1068,20 @@ void K_LoadKartHUDGraphics(void)
|
|||
HU_UpdatePatch(&kp_team_underlay[1][1], "TEAM4UR");
|
||||
HU_UpdatePatch(&kp_team_minihead, "TEAM4H");
|
||||
HU_UpdatePatch(&kp_team_you, "TEAM_YOU");
|
||||
|
||||
HU_UpdatePatch(&kp_duel_foe, "DUEL_FOE");
|
||||
HU_UpdatePatch(&kp_duel_sticker, "DUEL_S");
|
||||
HU_UpdatePatch(&kp_duel_under, "DUEL_B");
|
||||
HU_UpdatePatch(&kp_duel_over, "DUEL_B2");
|
||||
HU_UpdatePatch(&kp_duel_you, "DUEL_YOU");
|
||||
|
||||
sprintf(buffer, "DUELMBxx");
|
||||
for (i = 0; i < MARGINLEVELS; i++)
|
||||
{
|
||||
buffer[6] = '0'+(i/10);
|
||||
buffer[7] = '0'+(i%10);
|
||||
HU_UpdatePatch(&kp_duel_margin[i], "%s", buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// For the item toggle menu
|
||||
|
|
@ -1515,6 +1536,46 @@ void K_DrawLikeMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, p
|
|||
);
|
||||
}
|
||||
|
||||
void K_DrawMapAsFace(INT32 x, INT32 y, UINT32 flags, UINT16 map, const UINT8 *colormap)
|
||||
{
|
||||
const fixed_t iconHeight = (14 << FRACBITS);
|
||||
const fixed_t iconWidth = (iconHeight * 320) / 200;
|
||||
INT32 unit = 1;
|
||||
fixed_t mul = FRACUNIT;
|
||||
if (flags & V_NOSCALESTART)
|
||||
{
|
||||
unit = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy);
|
||||
mul = 1;
|
||||
}
|
||||
|
||||
V_DrawFill(
|
||||
x,
|
||||
y,
|
||||
16 * unit,
|
||||
16 * unit,
|
||||
(flags & ~V_FLIP)
|
||||
);
|
||||
|
||||
V_SetClipRect(
|
||||
(x + unit) * mul,
|
||||
(y + unit) * mul,
|
||||
(14 * unit) * mul,
|
||||
(14 * unit) * mul,
|
||||
(flags & ~V_FLIP)
|
||||
);
|
||||
|
||||
K_DrawMapThumbnail(
|
||||
((x + unit) * FRACUNIT) - (iconWidth - iconHeight)/2,
|
||||
((y + unit) * FRACUNIT),
|
||||
iconWidth,
|
||||
flags,
|
||||
map,
|
||||
colormap
|
||||
);
|
||||
|
||||
V_ClearClipRect();
|
||||
}
|
||||
|
||||
// see also K_DrawNameTagItemSpy
|
||||
static void K_drawKartItem(void)
|
||||
{
|
||||
|
|
@ -2970,6 +3031,9 @@ static boolean K_drawKartPositionFaces(void)
|
|||
if (!LUA_HudEnabled(hud_minirankings))
|
||||
return false; // Don't proceed but still return true for free play above if HUD is disabled.
|
||||
|
||||
if (K_InRaceDuel())
|
||||
return false;
|
||||
|
||||
switch (r_splitscreen)
|
||||
{
|
||||
case 0:
|
||||
|
|
@ -3229,11 +3293,292 @@ INT32 K_GetTransFlagFromFixed(fixed_t value)
|
|||
}
|
||||
}
|
||||
|
||||
static tic_t duel_lastleveltime = 0;
|
||||
static INT32 duel_marginanim = 0;
|
||||
static INT32 duel_lastmargin = 0;
|
||||
static INT32 youheight = 0;
|
||||
|
||||
static void K_drawKartDuelScores(void)
|
||||
{
|
||||
if (!K_InRaceDuel())
|
||||
return;
|
||||
|
||||
using srb2::Draw;
|
||||
|
||||
player_t *foe = K_DuelOpponent(stplyr);
|
||||
|
||||
INT32 basex = 0;
|
||||
INT32 basey = 40;
|
||||
INT32 flags = V_SNAPTOLEFT|V_HUDTRANS|V_SLIDEIN;
|
||||
|
||||
// score bars, here barheight is the size of bars at tied score
|
||||
INT32 barx = 8;
|
||||
INT32 bary = 61;
|
||||
INT32 barheight = 48;
|
||||
INT32 barwidth = 6;
|
||||
|
||||
// portraits
|
||||
INT32 foex = 16;
|
||||
INT32 foey = 21;
|
||||
INT32 youx = 16;
|
||||
INT32 youy = 85;
|
||||
|
||||
// scores
|
||||
INT32 foescorex = 16;
|
||||
INT32 foescorey = 38;
|
||||
INT32 youscorex = 16;
|
||||
INT32 youscorey = 69;
|
||||
|
||||
Draw::Font scorefont = Draw::Font::kThinTimer;
|
||||
|
||||
UINT8 ri = 6;
|
||||
INT32 youfill = skincolors[stplyr->skincolor].ramp[ri];
|
||||
INT32 foefill = skincolors[foe->skincolor].ramp[ri];
|
||||
|
||||
V_DrawScaledPatch(basex, basey, flags, kp_duel_sticker);
|
||||
|
||||
INT32 scoredelta = stplyr->duelscore - foe->duelscore;
|
||||
INT32 clutchscore = DUELWINNINGSCORE - 1; // we want the bar to be full when NEXT checkpoint wins...
|
||||
INT32 savemargin = 3; // ...minus a little bit.
|
||||
|
||||
if (leveltime/(TICRATE/2) % 2)
|
||||
savemargin += ((leveltime/2)%2);
|
||||
|
||||
if (clutchscore == 0)
|
||||
clutchscore = 1; // Fuck it, just don't crash
|
||||
|
||||
INT32 targetyouheight = barheight*abs(clutchscore+scoredelta)/clutchscore;
|
||||
|
||||
if (targetyouheight == 0)
|
||||
{
|
||||
targetyouheight = savemargin;
|
||||
}
|
||||
else if (targetyouheight >= 2*barheight)
|
||||
{
|
||||
targetyouheight = 2*barheight - savemargin;
|
||||
}
|
||||
|
||||
if (leveltime != duel_lastleveltime)
|
||||
{
|
||||
INT32 slide = std::max(1, abs(targetyouheight - youheight)/3);
|
||||
if (targetyouheight > youheight)
|
||||
youheight += slide;
|
||||
else if (targetyouheight < youheight)
|
||||
youheight -= slide;
|
||||
}
|
||||
|
||||
INT32 foeheight = 2*barheight-youheight; // barheight is a single tied bar, so total height of the full gauge is 2x barheight
|
||||
|
||||
V_DrawFill(basex+barx, basey+bary-barheight, barwidth, foeheight, foefill|flags);
|
||||
V_DrawFill(basex+barx, basey+bary-barheight+foeheight, barwidth, youheight, youfill|flags);
|
||||
|
||||
V_DrawScaledPatch(basex, basey, flags, kp_duel_under);
|
||||
V_DrawScaledPatch(basex, basey-barheight+foeheight, flags, kp_duel_over);
|
||||
V_DrawScaledPatch(basex, basey, flags, kp_duel_foe);
|
||||
V_DrawScaledPatch(basex, basey, flags, kp_duel_you);
|
||||
|
||||
Draw foenum = Draw(basex+foescorex, basey+foescorey).flags(flags).font(scorefont).align(Draw::Align::kLeft);
|
||||
Draw younum = Draw(basex+youscorex, basey+youscorey).flags(flags).font(scorefont).align(Draw::Align::kLeft);
|
||||
|
||||
if (abs(scoredelta) == clutchscore && ((leveltime % 2) || cv_reducevfx.value))
|
||||
{
|
||||
if (foe->duelscore > stplyr->duelscore)
|
||||
foenum = foenum.colorize(SKINCOLOR_GOLD);
|
||||
else
|
||||
younum = younum.colorize(SKINCOLOR_GOLD);
|
||||
}
|
||||
|
||||
foenum.text("{}", foe->duelscore);
|
||||
younum.text("{}", stplyr->duelscore);
|
||||
|
||||
// minirankings shamelessly copypasted because i know that shit works already
|
||||
// and SURELY we will never need to use this somewhere else, right?
|
||||
|
||||
UINT8 workingskin;
|
||||
UINT8 *colormap;
|
||||
INT32 xoff, yoff, flipflag, skinflags;
|
||||
|
||||
for (UINT8 draw = 0; draw < 2; draw++)
|
||||
{
|
||||
UINT8 drawme = draw ? (stplyr - players) : (foe - players);
|
||||
UINT8 drawx = basex + (draw ? youx : foex);
|
||||
UINT8 drawy = basey + (draw ? youy : foey);
|
||||
|
||||
if (!playeringame[drawme] || players[drawme].spectator)
|
||||
continue;
|
||||
|
||||
if (!players[drawme].mo || P_MobjWasRemoved(players[drawme].mo))
|
||||
continue;
|
||||
|
||||
skinflags = (demo.playback)
|
||||
? demo.skinlist[demo.currentskinid[drawme]].flags
|
||||
: skins[players[drawme].skin].flags;
|
||||
|
||||
// Flip SF_IRONMAN portraits, but only if they're transformed
|
||||
if (skinflags & SF_IRONMAN
|
||||
&& !(players[drawme].charflags & SF_IRONMAN) )
|
||||
{
|
||||
flipflag = V_FLIP|V_VFLIP; // blonic flip
|
||||
xoff = yoff = 16;
|
||||
} else
|
||||
{
|
||||
flipflag = 0;
|
||||
xoff = yoff = 0;
|
||||
}
|
||||
|
||||
if ((skin_t*)players[drawme].mo->skin)
|
||||
workingskin = (skin_t*)players[drawme].mo->skin - skins;
|
||||
else
|
||||
workingskin = players[drawme].skin;
|
||||
|
||||
colormap = R_GetTranslationColormap(workingskin, static_cast<skincolornum_t>(players[drawme].mo->color), GTC_CACHE);
|
||||
if (players[drawme].mo->colorized)
|
||||
colormap = R_GetTranslationColormap(TC_RAINBOW, static_cast<skincolornum_t>(players[drawme].mo->color), GTC_CACHE);
|
||||
else
|
||||
colormap = R_GetTranslationColormap(workingskin, static_cast<skincolornum_t>(players[drawme].mo->color), GTC_CACHE);
|
||||
|
||||
V_DrawMappedPatch(drawx+xoff, drawy+yoff, flags|flipflag, faceprefix[workingskin][FACE_RANK], colormap);
|
||||
}
|
||||
|
||||
// Dogshit. Should have just figured out how to do log base 5 in C++.
|
||||
// However, I think this works anyway.
|
||||
// I did my best to comment this but the algorithm is honestly just bad and hard to
|
||||
// reason about. Please don't try to maintain this, just yell at me if it needs any
|
||||
// adjustments. -Tyron 2025-05-29
|
||||
|
||||
// DESIGN INTENT: Create realistic-looking Puyo garbage stacks, while using the
|
||||
// leading garbage symbol as an indicator of the current Margin Boost value.
|
||||
|
||||
INT32 rawmargin = overtimecheckpoints; // The actual Margin Boost value.
|
||||
INT32 boostspersymbol = 3; // How many boosts should it take to see a new symbol?
|
||||
// rawmargin = (leveltime/10)%(3*boostspersymbol);
|
||||
|
||||
if (duel_lastleveltime != leveltime) // Trigger the "slide" animation when rawmargin changes.
|
||||
{
|
||||
duel_marginanim = std::min(duel_marginanim + 1, 100); // not magic just arbitrary
|
||||
if (duel_lastmargin != rawmargin)
|
||||
{
|
||||
duel_marginanim = 0;
|
||||
duel_lastmargin = rawmargin;
|
||||
}
|
||||
}
|
||||
|
||||
duel_lastleveltime = leveltime;
|
||||
|
||||
// CONS_Printf("=== RAWMARGIN %d\n", rawmargin);
|
||||
|
||||
if (rawmargin == 0)
|
||||
return;
|
||||
|
||||
rawmargin--; // Start at 0, idiot
|
||||
|
||||
// We're invoking the RNG to get a slightly chaotic symbol distribution,
|
||||
// but we're a HUD hook, so we need to keep the results of the call consistent.
|
||||
P_SetRandSeed(PR_NUISANCE, 69 + rawmargin);
|
||||
|
||||
INT32 highsymbol = rawmargin/boostspersymbol + 1; // Highest symbol that should appear.
|
||||
INT32 symbolsperupgrade = 5; // What is each symbol worth relative to each other? Like, 5 Stars = 1 Moon, etc.
|
||||
|
||||
// Okay, so we would LOVE to do this in a way that isn't a big clusterfuck, like just
|
||||
// doing rawmargin^3 and then subtracting powers of 5 out of that. Unfortunately, UINT64
|
||||
// is too small for the values that feel intuitively right here, so we have to do some of
|
||||
// the math on a limited set of symbols, then shift up. This is the concept of "symbol
|
||||
// headroom" that's in use here.
|
||||
//
|
||||
// (Note that Puyo~n uses a super inconsistent symbol table, probably to avoid this problem,
|
||||
// but we're assholes and want things to feel logically consistent I guess?
|
||||
// I dunno. I sort of feel like I should have just directly used the Puyo~n garbage table and
|
||||
// avoided most of this, LOL)
|
||||
|
||||
INT32 symbolheadroom = 5; // Maximum # symbols we can "step down".
|
||||
INT32 frac = rawmargin % boostspersymbol; // Used in intermediate calculations.
|
||||
INT32 minsymbol = std::max(1, highsymbol - symbolheadroom); // The lowest symbol that should appear.
|
||||
INT32 symbolheadroominuse = highsymbol - minsymbol; // The # of symbols we are stepping down.
|
||||
INT32 minscore = std::pow(symbolsperupgrade, symbolheadroominuse+1);
|
||||
INT32 maxscore = std::pow(symbolsperupgrade, symbolheadroominuse+2) - 1;
|
||||
|
||||
// CONS_Printf("min %d max %d\n", minscore, maxscore);
|
||||
|
||||
// We show the player successive combos with the same leading symbol, but we
|
||||
// waht them to feel intuitively like they're increasing each time.
|
||||
// Maxscore and minscore have been mapped to the correct power-of-N, so any
|
||||
// point we pick between them will lead with the correct symbol once we adjust
|
||||
// for symbol headroom. Pick a point that's appropriate for how "far" into the
|
||||
// current symbol we are.
|
||||
fixed_t lobound = FRACUNIT * frac / boostspersymbol;
|
||||
fixed_t hibound = FRACUNIT * (frac+1) / boostspersymbol;
|
||||
fixed_t roll = P_RandomRange(PR_NUISANCE, lobound, hibound);
|
||||
|
||||
INT32 margin = Easing_Linear(roll, minscore, maxscore); // The score we're trying to draw a garbage stack for.
|
||||
|
||||
INT32 margindigits[5];
|
||||
memset(margindigits, -1, sizeof(margindigits));
|
||||
|
||||
INT32 nummargindigits = 0;
|
||||
|
||||
// CONS_Printf("margin %d min %d max %d roll %d shiu %d ms %d\n", margin, minscore, maxscore, roll, symbolheadroominuse, minsymbol);
|
||||
|
||||
if (rawmargin/boostspersymbol >= (MARGINLEVELS-1))
|
||||
{
|
||||
// Capped out. Show 5 Chaos.
|
||||
nummargindigits = 5;
|
||||
for(UINT8 i = 0; i < nummargindigits; i++)
|
||||
{
|
||||
margindigits[i] = MARGINLEVELS-1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subtract powers of N from our chosen score to create a decent-enough-looking
|
||||
// garbage stack, then queue up the right patches to be drawn, shifting all the math
|
||||
// up by "minsymbol"—remember, once maxsymbol goes above symbolheadroom, we are doing
|
||||
// a low-precision version of the math that ignores low enough symbols.
|
||||
while (margin > 0)
|
||||
{
|
||||
INT32 significant_margin = 0;
|
||||
for (UINT8 i = symbolheadroominuse+1; i >= 0; i--)
|
||||
{
|
||||
INT32 test = std::pow(symbolsperupgrade, i);
|
||||
// CONS_Printf("testing %d (%d)\n", i, test);
|
||||
if (margin >= test)
|
||||
{
|
||||
significant_margin = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
INT32 index = significant_margin;
|
||||
|
||||
margindigits[nummargindigits] = index + minsymbol - 1;
|
||||
// CONS_Printf("digit %d %d\n", nummargindigits, margindigits[nummargindigits]);
|
||||
|
||||
nummargindigits++;
|
||||
|
||||
// CONS_Printf("margin was %d ", margin);
|
||||
margin -= std::pow(symbolsperupgrade, index);
|
||||
// CONS_Printf("is %d\n", margin);
|
||||
|
||||
if (nummargindigits >= 3 + frac)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
INT32 marginspacing = std::min(6, duel_marginanim);
|
||||
INT32 marginx = ((nummargindigits-1) * marginspacing)/2;
|
||||
|
||||
for (INT32 i = nummargindigits - 1; i >= 0; i--)
|
||||
{
|
||||
// CONS_Printf("draw %d - %d\n", i, margindigits[i]);
|
||||
V_DrawScaledPatch(basex + marginx, basey, flags, kp_duel_margin[margindigits[i]]);
|
||||
marginx -= marginspacing;
|
||||
}
|
||||
}
|
||||
|
||||
static INT32 easedallyscore = 0;
|
||||
static tic_t scorechangecooldown = 0;
|
||||
// Mildly ugly. Don't want to export this to khud when it's so nicely handled here,
|
||||
// but HUD hooks run at variable timing based on your actual framerate.
|
||||
static tic_t lastleveltime = 0;
|
||||
static tic_t teams_lastleveltime = 0;
|
||||
|
||||
static void K_drawKartTeamScores(void)
|
||||
{
|
||||
|
|
@ -3309,6 +3654,9 @@ static void K_drawKartTeamScores(void)
|
|||
UINT16 enemyscore = g_teamscores[enemies];
|
||||
UINT16 totalscore = allyscore + enemyscore;
|
||||
|
||||
if (totalscore == 0)
|
||||
return;
|
||||
|
||||
using srb2::Draw;
|
||||
srb2::Draw::Font scorefont = Draw::Font::kTimer;
|
||||
|
||||
|
|
@ -3360,11 +3708,11 @@ static void K_drawKartTeamScores(void)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (lastleveltime != leveltime) // Timing consistency
|
||||
if (teams_lastleveltime != leveltime) // Timing consistency
|
||||
{
|
||||
INT32 delta = abs(easedallyscore - allyscore); // how wrong is display score?
|
||||
|
||||
if (scorechangecooldown == 0)
|
||||
if (scorechangecooldown == 0 && delta)
|
||||
{
|
||||
if (allyscore > easedallyscore)
|
||||
{
|
||||
|
|
@ -3387,7 +3735,7 @@ static void K_drawKartTeamScores(void)
|
|||
enemyscore = totalscore - allyscore;
|
||||
}
|
||||
|
||||
lastleveltime = leveltime;
|
||||
teams_lastleveltime = leveltime;
|
||||
|
||||
fixed_t enemypercent = FixedDiv(enemyscore*FRACUNIT, totalscore*FRACUNIT);
|
||||
// fixed_t allypercent = FixedDiv(allyscore*FRACUNIT, totalscore*FRACUNIT);
|
||||
|
|
@ -3531,6 +3879,11 @@ static void K_drawKartTeamScores(void)
|
|||
*/
|
||||
}
|
||||
|
||||
static boolean K_DrawingLaps()
|
||||
{
|
||||
return (numlaps != 1 && !K_InRaceDuel() && (UINT16)stplyr->exp != UINT16_MAX);
|
||||
}
|
||||
|
||||
static boolean K_drawKartLaps(void)
|
||||
{
|
||||
INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN;
|
||||
|
|
@ -3543,7 +3896,9 @@ static boolean K_drawKartLaps(void)
|
|||
// I do not understand the way this system of offsets is laid out at all,
|
||||
// so it's probably going to be pretty bad to maintain. Sorry.
|
||||
|
||||
if (numlaps != 1 && displayEXP != UINT16_MAX)
|
||||
boolean drawinglaps = (numlaps != 1 && !K_InRaceDuel() && displayEXP != UINT16_MAX);
|
||||
|
||||
if (drawinglaps)
|
||||
{
|
||||
if (r_splitscreen > 1)
|
||||
bump = 27;
|
||||
|
|
@ -3551,7 +3906,7 @@ static boolean K_drawKartLaps(void)
|
|||
bump = 40;
|
||||
}
|
||||
|
||||
if (numlaps != 1)
|
||||
if (drawinglaps)
|
||||
{
|
||||
if (r_splitscreen > 1)
|
||||
{
|
||||
|
|
@ -3968,7 +4323,16 @@ static void K_drawKartAccessibilityIcons(boolean gametypeinfoshown, INT32 fx)
|
|||
}
|
||||
else
|
||||
{
|
||||
fx = LAPS_X+44;
|
||||
fx = LAPS_X + 33;
|
||||
|
||||
if ((gametyperules & (GTR_BUMPERS|GTR_CIRCUIT)) == GTR_BUMPERS)
|
||||
fx -= 14;
|
||||
|
||||
if (K_DrawingLaps())
|
||||
{
|
||||
fx += 30;
|
||||
}
|
||||
|
||||
fy = LAPS_Y;
|
||||
if (R_GetViewNumber() & 1) // If we are not P1 or P3...
|
||||
{
|
||||
|
|
@ -4862,7 +5226,7 @@ playertagtype_t K_WhichPlayerTag(player_t *p)
|
|||
}
|
||||
else if (p->bot)
|
||||
{
|
||||
if (p->botvars.rival == true || cv_levelskull.value)
|
||||
if ((p->botvars.rival == true || cv_levelskull.value) && (!K_InRaceDuel()))
|
||||
{
|
||||
return PLAYERTAG_RIVAL;
|
||||
}
|
||||
|
|
@ -6717,10 +7081,8 @@ static void K_DrawGPRankDebugger(void)
|
|||
|
||||
V_DrawThinString(0, 0, V_SNAPTOTOP|V_SNAPTOLEFT,
|
||||
va("POS: %d / %d", grandprixinfo.rank.position, RANK_NEUTRAL_POSITION));
|
||||
V_DrawThinString(0, 10, V_SNAPTOTOP|V_SNAPTOLEFT,
|
||||
va("PTS: %d / %d", grandprixinfo.rank.winPoints, grandprixinfo.rank.totalPoints));
|
||||
V_DrawThinString(0, 20, V_SNAPTOTOP|V_SNAPTOLEFT,
|
||||
va("LAPS: %d / %d", grandprixinfo.rank.exp, grandprixinfo.rank.totalExp));
|
||||
va("EXP: %d / %d", grandprixinfo.rank.exp, grandprixinfo.rank.totalExp));
|
||||
V_DrawThinString(0, 30, V_SNAPTOTOP|V_SNAPTOLEFT,
|
||||
va("CONTINUES: %d", grandprixinfo.rank.continuesUsed));
|
||||
V_DrawThinString(0, 40, V_SNAPTOTOP|V_SNAPTOLEFT,
|
||||
|
|
@ -6944,35 +7306,39 @@ static void K_DrawMessageFeed(void)
|
|||
|
||||
text.font(Draw::Font::kMenu);
|
||||
|
||||
UINT8 x = BASEVIDWIDTH/2;
|
||||
UINT8 y = 10;
|
||||
UINT32 vw = vid.width / vid.dupx;
|
||||
UINT32 vh = vid.height / vid.dupy;
|
||||
|
||||
UINT32 x = vw / 2;
|
||||
UINT32 y = 10;
|
||||
|
||||
SINT8 shift = 0;
|
||||
if (r_splitscreen >= 2)
|
||||
{
|
||||
text.font(Draw::Font::kThin);
|
||||
shift = -2;
|
||||
|
||||
x = BASEVIDWIDTH/4;
|
||||
x = vw/4;
|
||||
y = 5;
|
||||
|
||||
if (i % 2)
|
||||
x += BASEVIDWIDTH/2;
|
||||
x += vw / 2;
|
||||
|
||||
if (i >= 2)
|
||||
y += BASEVIDHEIGHT / 2;
|
||||
y += vh / 2;
|
||||
}
|
||||
else if (r_splitscreen >= 1)
|
||||
{
|
||||
y = 5;
|
||||
|
||||
if (i >= 1)
|
||||
y += BASEVIDHEIGHT / 2;
|
||||
y += vh / 2;
|
||||
}
|
||||
|
||||
UINT16 sw = text.width();
|
||||
|
||||
K_DrawSticker(x - sw/2, y, sw, 0, true);
|
||||
Draw(x, y+shift).align(Draw::Align::kCenter).text(text);
|
||||
K_DrawSticker(x - sw/2, y, sw, V_SNAPTOTOP|V_SNAPTOLEFT, true);
|
||||
Draw(x, y+shift).align(Draw::Align::kCenter).flags(V_SNAPTOTOP|V_SNAPTOLEFT).text(text);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -7253,9 +7619,14 @@ void K_drawKartHUD(void)
|
|||
K_drawKartTeamScores();
|
||||
}
|
||||
|
||||
if (K_InRaceDuel())
|
||||
{
|
||||
K_drawKartDuelScores();
|
||||
}
|
||||
|
||||
if (LUA_HudEnabled(hud_gametypeinfo))
|
||||
{
|
||||
if (gametyperules & GTR_CIRCUIT)
|
||||
if (gametyperules & GTR_CIRCUIT && !K_InRaceDuel())
|
||||
{
|
||||
K_drawKartLaps();
|
||||
gametypeinfoshown = true;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ extern "C" {
|
|||
|
||||
#define POS_DELAY_TIME 10
|
||||
|
||||
#define MARGINLEVELS 24
|
||||
|
||||
extern INT32 MINI_X, MINI_Y;
|
||||
|
||||
struct trackingResult_t
|
||||
|
|
@ -58,6 +60,7 @@ void K_drawKart4PTimestamp(void);
|
|||
void K_drawEmeraldWin(boolean overlay);
|
||||
void K_DrawMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, UINT16 map, const UINT8 *colormap);
|
||||
void K_DrawLikeMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, patch_t *patch, const UINT8 *colormap);
|
||||
void K_DrawMapAsFace(INT32 x, INT32 y, UINT32 flags, UINT16 map, const UINT8 *colormap);
|
||||
void K_drawTargetHUD(const vector3_t *origin, player_t *player);
|
||||
void K_drawButton(fixed_t x, fixed_t y, INT32 flags, patch_t *button[2], boolean pressed);
|
||||
void K_drawButtonAnim(INT32 x, INT32 y, INT32 flags, patch_t *button[2], tic_t animtic);
|
||||
|
|
|
|||
|
|
@ -143,6 +143,8 @@ struct TargetTracking
|
|||
return false;
|
||||
|
||||
default:
|
||||
if (K_IsPickMeUpItem(mobj->type))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -277,28 +279,17 @@ private:
|
|||
{{6, 2, {kp_spraycantarget_far[1]}, V_ADD}}, // 4P
|
||||
}},
|
||||
};
|
||||
|
||||
case MT_JAWZ:
|
||||
case MT_JAWZ_SHIELD:
|
||||
case MT_ORBINAUT:
|
||||
case MT_ORBINAUT_SHIELD:
|
||||
case MT_DROPTARGET:
|
||||
case MT_DROPTARGET_SHIELD:
|
||||
case MT_LANDMINE:
|
||||
case MT_BANANA:
|
||||
case MT_BANANA_SHIELD:
|
||||
case MT_GACHABOM:
|
||||
case MT_EGGMANITEM:
|
||||
case MT_EGGMANITEM_SHIELD:
|
||||
case MT_BUBBLESHIELDTRAP:
|
||||
return {
|
||||
{ // Near
|
||||
{2, TICRATE/2, {kp_pickmeup}, 0}, // 1P
|
||||
{{2, TICRATE/2, {kp_pickmeup}, 0}}, // 4P
|
||||
},
|
||||
};
|
||||
|
||||
default:
|
||||
if (K_IsPickMeUpItem(mobj->type))
|
||||
{
|
||||
return {
|
||||
{ // Near
|
||||
{2, TICRATE/2, {kp_pickmeup}, 0}, // 1P
|
||||
{{2, TICRATE/2, {kp_pickmeup}, 0}}, // 4P
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
{ // Near
|
||||
{8, 2, {kp_capsuletarget_near[0]}}, // 1P
|
||||
|
|
@ -902,32 +893,17 @@ void K_drawTargetHUD(const vector3_t* origin, player_t* player)
|
|||
if (tracking)
|
||||
{
|
||||
fixed_t itemOffset = 36*mobj->scale;
|
||||
switch (mobj->type)
|
||||
|
||||
if (K_IsPickMeUpItem(mobj->type))
|
||||
{
|
||||
case MT_JAWZ:
|
||||
case MT_JAWZ_SHIELD:
|
||||
case MT_ORBINAUT:
|
||||
case MT_ORBINAUT_SHIELD:
|
||||
case MT_DROPTARGET:
|
||||
case MT_DROPTARGET_SHIELD:
|
||||
case MT_LANDMINE:
|
||||
case MT_BANANA:
|
||||
case MT_BANANA_SHIELD:
|
||||
case MT_GACHABOM:
|
||||
case MT_BUBBLESHIELDTRAP:
|
||||
case MT_EGGMANITEM:
|
||||
case MT_EGGMANITEM_SHIELD:
|
||||
if (stplyr->mo->eflags & MFE_VERTICALFLIP)
|
||||
{
|
||||
pos.z -= itemOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos.z += itemOffset;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (stplyr->mo->eflags & MFE_VERTICALFLIP)
|
||||
{
|
||||
pos.z -= itemOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos.z += itemOffset;
|
||||
}
|
||||
}
|
||||
|
||||
K_ObjectTracking(&tr.result, &pos, false);
|
||||
|
|
|
|||
384
src/k_kart.c
384
src/k_kart.c
|
|
@ -119,6 +119,27 @@ boolean K_DuelItemAlwaysSpawns(mapthing_t *mt)
|
|||
return !!(mt->thing_args[0]);
|
||||
}
|
||||
|
||||
boolean K_InRaceDuel(void)
|
||||
{
|
||||
return (inDuel && (gametyperules & GTR_CIRCUIT) && !(mapheaderinfo[gamemap-1]->levelflags & LF_SECTIONRACE)) && !specialstageinfo.valid;
|
||||
}
|
||||
|
||||
player_t *K_DuelOpponent(player_t *player)
|
||||
{
|
||||
if (!K_InRaceDuel())
|
||||
return player; // ????
|
||||
else
|
||||
{
|
||||
for (UINT8 i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] && !players[i].spectator && player - players != i)
|
||||
return &players[i];
|
||||
}
|
||||
}
|
||||
|
||||
return player; // ????????????
|
||||
}
|
||||
|
||||
static void K_SpawnDuelOnlyItems(void)
|
||||
{
|
||||
mapthing_t *mt = NULL;
|
||||
|
|
@ -145,6 +166,7 @@ void K_TimerReset(void)
|
|||
numbulbs = 1;
|
||||
inDuel = rainbowstartavailable = false;
|
||||
linecrossed = 0;
|
||||
overtimecheckpoints = 0;
|
||||
timelimitintics = extratimeintics = secretextratime = 0;
|
||||
g_pointlimit = 0;
|
||||
}
|
||||
|
|
@ -273,6 +295,9 @@ void K_TimerInit(void)
|
|||
introtime = (108) + 5; // 108 for rotation, + 5 for white fade
|
||||
numbulbs += (numPlayers-2); // Extra POSITION!! time
|
||||
}
|
||||
|
||||
if (K_InRaceDuel())
|
||||
numlaps = 99;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -446,6 +471,9 @@ boolean K_IsPlayerScamming(player_t *player)
|
|||
if (!M_NotFreePlay())
|
||||
return false;
|
||||
|
||||
if (!(gametyperules & GTR_CIRCUIT))
|
||||
return false;
|
||||
|
||||
// "Why 8?" Consistency
|
||||
// "Why 2000?" Vibes
|
||||
return (K_GetItemRouletteDistance(player, 8) < SCAMDIST);
|
||||
|
|
@ -468,7 +496,10 @@ fixed_t K_GetKartGameSpeedScalar(SINT8 value)
|
|||
if (cv_4thgear.value && !netgame && (!demo.playback || !demo.netgame) && !modeattacking)
|
||||
value = 3;
|
||||
|
||||
return ((13 + (3*value)) << FRACBITS) / 16;
|
||||
fixed_t base = ((13 + (3*value)) << FRACBITS) / 16;
|
||||
fixed_t duel = overtimecheckpoints*(1<<FRACBITS)/32;
|
||||
|
||||
return base + duel;
|
||||
}
|
||||
|
||||
// Array of states to pick the starting point of the animation, based on the actual time left for invincibility.
|
||||
|
|
@ -2981,7 +3012,7 @@ void K_MomentumToFacing(player_t *player)
|
|||
|
||||
boolean K_ApplyOffroad(const player_t *player)
|
||||
{
|
||||
if (player->invincibilitytimer || player->hyudorotimer || player->sneakertimer || player->panelsneakertimer)
|
||||
if (player->invincibilitytimer || player->hyudorotimer || player->sneakertimer || player->panelsneakertimer|| player->weaksneakertimer)
|
||||
return false;
|
||||
if (K_IsRidingFloatingTop(player))
|
||||
return false;
|
||||
|
|
@ -2990,7 +3021,7 @@ boolean K_ApplyOffroad(const player_t *player)
|
|||
|
||||
boolean K_SlopeResistance(const player_t *player)
|
||||
{
|
||||
if (player->invincibilitytimer || player->sneakertimer || player->panelsneakertimer || player->tiregrease || player->flamedash)
|
||||
if (player->invincibilitytimer || player->sneakertimer || player->panelsneakertimer || player->weaksneakertimer || player->tiregrease || player->flamedash)
|
||||
return true;
|
||||
if (player->curshield == KSHIELD_TOP)
|
||||
return true;
|
||||
|
|
@ -3131,6 +3162,7 @@ boolean K_WaterRun(mobj_t *mobj)
|
|||
if (mobj->player->invincibilitytimer
|
||||
|| mobj->player->sneakertimer
|
||||
|| mobj->player->panelsneakertimer
|
||||
|| mobj->player->weaksneakertimer
|
||||
|| mobj->player->tiregrease
|
||||
|| mobj->player->flamedash
|
||||
|| mobj->player->speed > minspeed)
|
||||
|
|
@ -3495,19 +3527,29 @@ static void K_GetKartBoostPower(player_t *player)
|
|||
UINT8 i;
|
||||
for (i = 0; i < player->numsneakers; i++)
|
||||
{
|
||||
ADDBOOST(FRACUNIT*85/100, 8*FRACUNIT, HANDLESCALING+HANDLESCALING/3); // + 50% top speed, + 800% acceleration, +50% handling
|
||||
ADDBOOST(FRACUNIT, 8*FRACUNIT, HANDLESCALING+HANDLESCALING/3); // + 100% top speed, + 800% acceleration, +50%(???) handling
|
||||
}
|
||||
}
|
||||
|
||||
if (player->panelsneakertimer) // Sneaker
|
||||
if (player->panelsneakertimer) // Sneaker panel
|
||||
{
|
||||
UINT8 i;
|
||||
for (i = 0; i < player->numpanelsneakers; i++)
|
||||
{
|
||||
ADDBOOST(FRACUNIT/2, 8*FRACUNIT, HANDLESCALING); // + 50% top speed, + 800% acceleration, +50% handling
|
||||
ADDBOOST(FRACUNIT/2, 8*FRACUNIT, HANDLESCALING); // + 50% top speed, + 800% acceleration, +50%(???) handling
|
||||
}
|
||||
}
|
||||
|
||||
if (player->weaksneakertimer) // Rocket sneaker boost
|
||||
{
|
||||
UINT8 i;
|
||||
for (i = 0; i < player->numweaksneakers; i++)
|
||||
{
|
||||
ADDBOOST((FRACUNIT*85)/100, 8*FRACUNIT, HANDLESCALING+HANDLESCALING/3); // + 85% top speed, + 800% acceleration, +50%(???) handling
|
||||
}
|
||||
}
|
||||
//NOTE: The various sneaker booth strengths are also defined in K_DoSneaker()!
|
||||
|
||||
if (player->invincibilitytimer) // Invincibility
|
||||
{
|
||||
// S-Monitor: no extra %
|
||||
|
|
@ -3564,7 +3606,7 @@ static void K_GetKartBoostPower(player_t *player)
|
|||
Easing_InCubic(
|
||||
player->overdrivepower,
|
||||
0,
|
||||
5*FRACUNIT/10
|
||||
75*FRACUNIT/100
|
||||
),
|
||||
Easing_InSine(
|
||||
player->overdrivepower,
|
||||
|
|
@ -3640,7 +3682,7 @@ static void K_GetKartBoostPower(player_t *player)
|
|||
{
|
||||
fixed_t ringboost_base = FRACUNIT/4;
|
||||
if (player->overdrive)
|
||||
ringboost_base += FRACUNIT/2;
|
||||
ringboost_base += FRACUNIT/4;
|
||||
// This one's a little special: we add extra top speed per tic of ringboost stored up, to allow for Ring Box to really rocket away.
|
||||
// (We compensate when decrementing ringboost to avoid runaway exponential scaling hell.)
|
||||
fixed_t rb = FixedDiv(player->ringboost * FRACUNIT, max(FRACUNIT, K_RingDurationBoost(player)));
|
||||
|
|
@ -3971,7 +4013,7 @@ SINT8 K_GetForwardMove(const player_t *player)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (player->sneakertimer || player->panelsneakertimer || player->spindashboost
|
||||
if (player->sneakertimer || player->panelsneakertimer || player->weaksneakertimer || player->spindashboost
|
||||
|| (((gametyperules & (GTR_ROLLINGSTART|GTR_CIRCUIT)) == (GTR_ROLLINGSTART|GTR_CIRCUIT)) && (leveltime < TICRATE/2)))
|
||||
{
|
||||
return MAXPLMOVE;
|
||||
|
|
@ -4134,8 +4176,8 @@ void K_SpawnAmps(player_t *player, UINT8 amps, mobj_t *impact)
|
|||
if (amps == 0)
|
||||
return;
|
||||
|
||||
UINT32 itemdistance = max(0, min( FRACUNIT, K_GetItemRouletteDistance(player, D_NumPlayersInRace()))); // cap this to FRACUNIT, so it doesn't wrap when turning it into fixed_t
|
||||
fixed_t itemdistmult = FRACUNIT + max( 0, min(FixedMul(FixedDiv(itemdistance<<FRACBITS, MAXAMPSCALINGDIST<<FRACBITS),FRACUNIT), FRACUNIT));
|
||||
UINT32 itemdistance = max(0, min( FRACUNIT-1, K_GetItemRouletteDistance(player, D_NumPlayersInRace()))); // cap this to FRACUNIT-1, so it doesn't wrap when turning it into fixed_t
|
||||
fixed_t itemdistmult = FRACUNIT + max( 0, min( FRACUNIT, (itemdistance<<FRACBITS) / MAXAMPSCALINGDIST));
|
||||
UINT16 scaledamps = min(amps, amps * (10 + (9-player->kartspeed) - (9-player->kartweight)) / 10);
|
||||
// Debug print for scaledamps calculation
|
||||
// CONS_Printf("K_SpawnAmps: player=%s, amps=%d, kartspeed=%d, kartweight=%d, itemdistance=%d, itemdistmult=%0.2f, statscaledamps=%d, distscaledamps=%d\n",
|
||||
|
|
@ -4239,7 +4281,7 @@ void K_CheckpointCrossAward(player_t *player)
|
|||
if (gametype != GT_RACE)
|
||||
return;
|
||||
|
||||
player->gradingfactor += K_GetGradingMultAdjustment(player);
|
||||
player->gradingfactor += K_GetGradingFactorAdjustment(player);
|
||||
player->gradingpointnum++;
|
||||
player->exp = K_GetEXP(player);
|
||||
//CONS_Printf("player: %s factor: %.2f exp: %d\n", player_names[player-players], FIXED_TO_FLOAT(player->gradingfactor), player->exp);
|
||||
|
|
@ -4247,6 +4289,92 @@ void K_CheckpointCrossAward(player_t *player)
|
|||
player->cangrabitems = 1;
|
||||
|
||||
K_AwardPlayerRings(player, (player->bot ? 20 : 10), true);
|
||||
|
||||
// Update Duel scoring.
|
||||
if (K_InRaceDuel() && player->position == 1)
|
||||
{
|
||||
player->duelscore += 1;
|
||||
|
||||
if (leveltime > (tic_t)(TICRATE*DUELOVERTIME))
|
||||
{
|
||||
overtimecheckpoints++;
|
||||
if (overtimecheckpoints > 1)
|
||||
{
|
||||
K_AddMessage(va("Margin Boost x%d!", overtimecheckpoints), true, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
K_AddMessage("Margin Boost!", true, false);
|
||||
g_darkness.start = leveltime;
|
||||
g_darkness.end = INT32_MAX;
|
||||
for (UINT8 i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
||||
{
|
||||
g_darkness.value[i] = FRACUNIT;
|
||||
}
|
||||
}
|
||||
|
||||
S_StartSound(NULL, sfx_gsha6);
|
||||
}
|
||||
|
||||
player_t *opp = K_DuelOpponent(player);
|
||||
boolean clutch = (player->duelscore - opp->duelscore == (DUELWINNINGSCORE-1));
|
||||
boolean win = (player->duelscore - opp->duelscore == DUELWINNINGSCORE);
|
||||
|
||||
if (!win)
|
||||
{
|
||||
for (UINT8 i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
||||
{
|
||||
player_t *check = &players[displayplayers[i]];
|
||||
if (check == player)
|
||||
{
|
||||
S_StartSound(NULL, sfx_mbs45);
|
||||
if (clutch)
|
||||
S_StartSoundAtVolume(NULL, sfx_s3k9c, 170);
|
||||
}
|
||||
|
||||
else if (check == opp)
|
||||
{
|
||||
S_StartSound(NULL, sfx_mbs60);
|
||||
if (clutch)
|
||||
S_StartSoundAtVolume(NULL, sfx_kc4b, 150);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (player->duelscore - opp->duelscore == DUELWINNINGSCORE)
|
||||
{
|
||||
opp->position = 2;
|
||||
player->position = 1;
|
||||
|
||||
if (opp->distancetofinish - player->distancetofinish < 200) // Setting player.exiting changes distance reporting, check these first!
|
||||
{
|
||||
K_StartRoundWinCamera(
|
||||
player->mo,
|
||||
player->angleturn + ANGLE_180,
|
||||
400*mapobjectscale,
|
||||
6*TICRATE,
|
||||
FRACUNIT/16
|
||||
);
|
||||
}
|
||||
|
||||
S_StartSound(NULL, sfx_s3k6a);
|
||||
P_DoPlayerExit(player, 0);
|
||||
P_DoAllPlayersExit(PF_NOCONTEST, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Doing this here because duel exit is a weird path, and we don't want to transform for endcam.
|
||||
UINT32 skinflags = (demo.playback)
|
||||
? demo.skinlist[demo.currentskinid[(player-players)]].flags
|
||||
: skins[player->skin].flags;
|
||||
if (skinflags & SF_IRONMAN)
|
||||
{
|
||||
SetRandomFakePlayerSkin(player, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
boolean K_Overdrive(player_t *player)
|
||||
|
|
@ -4948,7 +5076,7 @@ static boolean K_IsLosingWavedash(player_t *player)
|
|||
if (!K_Sliptiding(player) && player->wavedash < MIN_WAVEDASH_CHARGE)
|
||||
return true;
|
||||
if (!K_Sliptiding(player) && player->drift == 0
|
||||
&& P_IsObjectOnGround(player->mo) && player->sneakertimer == 0 && player->panelsneakertimer == 0
|
||||
&& P_IsObjectOnGround(player->mo) && player->sneakertimer == 0 && player->panelsneakertimer == 0 && player->weaksneakertimer == 0
|
||||
&& player->driftboost == 0)
|
||||
return true;
|
||||
return false;
|
||||
|
|
@ -7087,8 +7215,14 @@ void K_DoSneaker(player_t *player, INT32 type)
|
|||
|
||||
fixed_t intendedboost = FRACUNIT/2;
|
||||
|
||||
// If you've already got an item sneaker type boost, panel sneakers will instead turn into item sneaker boosts
|
||||
if (player->numsneakers && type == 0)
|
||||
// If you've already got an rocket sneaker type boost, panel sneakers will instead turn into rocket sneaker boosts
|
||||
if (player->numweaksneakers && type == 0)
|
||||
{
|
||||
type = 2;
|
||||
}
|
||||
|
||||
// If you've already got an item sneaker type boost, other sneakers will instead turn into item sneaker boosts
|
||||
if (player->numsneakers && type != 1)
|
||||
{
|
||||
type = 1;
|
||||
}
|
||||
|
|
@ -7099,10 +7233,13 @@ void K_DoSneaker(player_t *player, INT32 type)
|
|||
intendedboost = FRACUNIT/2;
|
||||
break;
|
||||
case 1: // Single item sneaker
|
||||
case 2: // Rocket sneaker
|
||||
intendedboost = 100*FRACUNIT/100;
|
||||
break;
|
||||
case 2: // Rocket sneaker (aka. weaksneaker)
|
||||
intendedboost = 85*FRACUNIT/100;
|
||||
break;
|
||||
}
|
||||
//NOTE: The various sneaker booth strengths are also defined in K_GetKartBoostPower()!
|
||||
|
||||
if (player->roundconditions.touched_sneakerpanel == false
|
||||
&& !(player->exiting || (player->pflags & PF_NOCONTEST))
|
||||
|
|
@ -7118,7 +7255,7 @@ void K_DoSneaker(player_t *player, INT32 type)
|
|||
const sfxenum_t smallsfx = sfx_cdfm40;
|
||||
sfxenum_t sfx = normalsfx;
|
||||
|
||||
if (player->numsneakers || player->numpanelsneakers)
|
||||
if (player->numsneakers || player->numpanelsneakers || player->numweaksneakers)
|
||||
{
|
||||
// Use a less annoying sound when stacking sneakers.
|
||||
sfx = smallsfx;
|
||||
|
|
@ -7134,17 +7271,19 @@ void K_DoSneaker(player_t *player, INT32 type)
|
|||
|
||||
switch (type)
|
||||
{
|
||||
case 0:
|
||||
case 0: // Panel sneaker
|
||||
player->numpanelsneakers++;
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
case 1: // Single item sneaker
|
||||
player->numsneakers++;
|
||||
break;
|
||||
case 2: // Rocket sneaker (aka. weaksneaker)
|
||||
player->numweaksneakers++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (player->sneakertimer == 0 && player->panelsneakertimer == 0)
|
||||
if (player->sneakertimer == 0 && player->panelsneakertimer == 0 && player->weaksneakertimer == 0)
|
||||
{
|
||||
if (type == 2)
|
||||
{
|
||||
|
|
@ -7176,17 +7315,19 @@ void K_DoSneaker(player_t *player, INT32 type)
|
|||
|
||||
switch (type)
|
||||
{
|
||||
case 0:
|
||||
case 0: // Panel sneaker
|
||||
player->panelsneakertimer = sneakertime;
|
||||
player->overshield += 1;
|
||||
if (player->overshield > 0) {
|
||||
player->overshield = min( player->overshield + TICRATE/3, max( TICRATE, player->overshield ));
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
case 1: // Single item sneaker
|
||||
player->sneakertimer = sneakertime;
|
||||
player->overshield += TICRATE/2;
|
||||
player->overshield = max( player->overshield, 25 );
|
||||
break;
|
||||
case 2:
|
||||
player->sneakertimer = 3*sneakertime/4;
|
||||
player->overshield += TICRATE/2;
|
||||
case 2: // Rocket sneaker (aka. weaksneaker)
|
||||
player->weaksneakertimer = 3*sneakertime/4;
|
||||
player->overshield = max( player->overshield, TICRATE/2 );
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -7321,7 +7462,7 @@ void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound)
|
|||
}
|
||||
}
|
||||
|
||||
if (mo->player->sneakertimer || mo->player->panelsneakertimer || mo->player->invincibilitytimer)
|
||||
if (mo->player->sneakertimer || mo->player->panelsneakertimer || mo->player->weaksneakertimer || mo->player->invincibilitytimer)
|
||||
{
|
||||
thrust = FixedMul(thrust, (3*FRACUNIT)/2);
|
||||
}
|
||||
|
|
@ -8925,6 +9066,7 @@ static void K_UpdateTripwire(player_t *player)
|
|||
boolean goodSpeed = (player->speed >= speedThreshold);
|
||||
boolean boostExists = (player->tripwireLeniency > 0); // can't be checked later because of subtractions...
|
||||
tripwirepass_t triplevel = K_TripwirePassConditions(player);
|
||||
boolean mightplaysound = false;
|
||||
|
||||
if (triplevel != TRIPWIRE_NONE)
|
||||
{
|
||||
|
|
@ -8933,12 +9075,7 @@ static void K_UpdateTripwire(player_t *player)
|
|||
mobj_t *front = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_TRIPWIREBOOST);
|
||||
mobj_t *back = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_TRIPWIREBOOST);
|
||||
|
||||
if (P_IsDisplayPlayer(player))
|
||||
{
|
||||
S_StartSound(player->mo, sfx_s3k40);
|
||||
S_StopSoundByID(player->mo, sfx_gshaf);
|
||||
}
|
||||
|
||||
mightplaysound = true;
|
||||
|
||||
P_SetTarget(&front->target, player->mo);
|
||||
P_SetTarget(&back->target, player->mo);
|
||||
|
|
@ -8957,6 +9094,12 @@ static void K_UpdateTripwire(player_t *player)
|
|||
|
||||
if (triplevel != TRIPWIRE_CONSUME)
|
||||
player->tripwireLeniency = max(player->tripwireLeniency, TRIPWIRETIME);
|
||||
|
||||
if (P_IsDisplayPlayer(player) && player->tripwireLeniency && mightplaysound)
|
||||
{
|
||||
S_StartSound(player->mo, TRIPWIRE_OK_SOUND);
|
||||
S_StopSoundByID(player->mo, TRIPWIRE_NG_SOUND);
|
||||
}
|
||||
}
|
||||
|
||||
// TRIPWIRE_CONSUME is only applied in very specific cases (currently, riding Garden Top)
|
||||
|
|
@ -9081,7 +9224,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
if (player->speed > 0)
|
||||
{
|
||||
// Speed lines
|
||||
if (player->sneakertimer || player->panelsneakertimer || player->ringboost
|
||||
if (player->sneakertimer || player->panelsneakertimer || player->weaksneakertimer || player->ringboost
|
||||
|| player->driftboost || player->startboost
|
||||
|| player->eggmanexplode || player->trickboost
|
||||
|| player->gateBoost || player->wavedashboost)
|
||||
|
|
@ -9322,7 +9465,8 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
if (((P_IsObjectOnGround(player->mo)
|
||||
|| ( player->spinouttype & KSPIN_AIRTIMER ))
|
||||
&& (!player->sneakertimer)
|
||||
&& (!player->panelsneakertimer))
|
||||
&& (!player->panelsneakertimer)
|
||||
&& (!player->weaksneakertimer))
|
||||
|| (player->respawn.state != RESPAWNST_NONE
|
||||
&& player->spinouttimer > 1
|
||||
&& (leveltime & 1)))
|
||||
|
|
@ -9518,6 +9662,16 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
}
|
||||
}
|
||||
|
||||
if (player->weaksneakertimer)
|
||||
{
|
||||
player->weaksneakertimer--;
|
||||
|
||||
if (player->weaksneakertimer <= 0)
|
||||
{
|
||||
player->numweaksneakers = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (player->trickboost)
|
||||
player->trickboost--;
|
||||
|
||||
|
|
@ -9537,7 +9691,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
if (player->counterdash)
|
||||
player->counterdash--;
|
||||
|
||||
if ((player->sneakertimer || player->panelsneakertimer) && player->wipeoutslow > 0 && player->wipeoutslow < wipeoutslowtime+1)
|
||||
if ((player->sneakertimer || player->panelsneakertimer || player->weaksneakertimer) && player->wipeoutslow > 0 && player->wipeoutslow < wipeoutslowtime+1)
|
||||
player->wipeoutslow = wipeoutslowtime+1;
|
||||
|
||||
if (player->floorboost > 0)
|
||||
|
|
@ -9662,6 +9816,26 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
player->invincibilitytimer--;
|
||||
if (player->invincibilitytimer && K_IsPlayerScamming(player))
|
||||
player->invincibilitytimer--;
|
||||
|
||||
// Extra tripwire leniency for the end of invincibility
|
||||
if (player->invincibilitytimer <= 0) {
|
||||
player->tripwireLeniency = max( player->tripwireLeniency, TICRATE );
|
||||
}
|
||||
}
|
||||
|
||||
// The precise ordering of start-of-level made me want to cut my head off,
|
||||
// so let's try this instead. Whatever!
|
||||
if (leveltime <= starttime || player->gradingpointnum == 0)
|
||||
{
|
||||
if ((gametyperules & GTR_SPHERES)
|
||||
|| (gametyperules & GTR_CATCHER)
|
||||
|| G_TimeAttackStart()
|
||||
|| (gametype == GT_TUTORIAL)
|
||||
|| !M_NotFreePlay()
|
||||
|| (K_GetNumWaypoints() == 0))
|
||||
player->cangrabitems = EARLY_ITEM_FLICKER;
|
||||
else
|
||||
player->cangrabitems = 0;
|
||||
}
|
||||
|
||||
if (player->cangrabitems && player->cangrabitems <= EARLY_ITEM_FLICKER)
|
||||
|
|
@ -9826,6 +10000,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
player->tiregrease = 0;
|
||||
player->sneakertimer = 0;
|
||||
player->panelsneakertimer = 0;
|
||||
player->weaksneakertimer = 0;
|
||||
player->spindashboost = 0;
|
||||
player->flashing = TICRATE/2;
|
||||
player->ringboost = 0;
|
||||
|
|
@ -10993,7 +11168,7 @@ static void K_UpdateDistanceFromFinishLine(player_t *const player)
|
|||
const mapheader_t *mapheader = mapheaderinfo[gamemap - 1];
|
||||
if ((mapheader->levelflags & LF_SECTIONRACE) == 0U)
|
||||
{
|
||||
const UINT8 numfulllapsleft = ((UINT8)numlaps - player->laps) / mapheader->lapspersection;
|
||||
UINT8 numfulllapsleft = ((UINT8)numlaps - player->laps) / mapheader->lapspersection;
|
||||
player->distancetofinish += numfulllapsleft * K_GetCircuitLength();
|
||||
}
|
||||
}
|
||||
|
|
@ -11041,6 +11216,7 @@ static void K_UpdatePlayerWaypoints(player_t *const player)
|
|||
player->respawn.state == RESPAWNST_NONE && // Respawning should be a full reset.
|
||||
old_currentwaypoint != NULL && // So should touching the first waypoint ever.
|
||||
player->laps != 0 && // POSITION rooms may have unorthodox waypoints to guide bots.
|
||||
player->exiting == 0 && // What the fuck? Why do duels antiskip the bot?
|
||||
!(player->pflags & PF_TRUSTWAYPOINTS)) // Special exception.
|
||||
{
|
||||
extern consvar_t cv_debuglapcheat;
|
||||
|
|
@ -11337,6 +11513,14 @@ INT16 K_GetKartTurnValue(const player_t *player, INT16 turnvalue)
|
|||
turnfixed = FixedMul(turnfixed, 2*FRACUNIT); // Base increase to turning
|
||||
}
|
||||
|
||||
/*
|
||||
if (overtimecheckpoints)
|
||||
{
|
||||
fixed_t assistpercent = FRACUNIT * overtimecheckpoints / 32;
|
||||
turnfixed += FixedMul(Easing_Linear(assistpercent, 0, FRACUNIT/2), turnfixed);
|
||||
}
|
||||
*/
|
||||
|
||||
if (player->drift != 0 && P_IsObjectOnGround(player->mo))
|
||||
{
|
||||
if (G_CompatLevel(0x000A))
|
||||
|
|
@ -11393,7 +11577,7 @@ INT16 K_GetKartTurnValue(const player_t *player, INT16 turnvalue)
|
|||
// If you're sliptiding, don't interact with handling boosts.
|
||||
// You need turning power proportional to your speed, no matter what!
|
||||
fixed_t topspeed = K_GetKartSpeed(player, false, false);
|
||||
if (K_Sliptiding(player))
|
||||
if (K_Sliptiding(player) || player->flamedash)
|
||||
{
|
||||
fixed_t sliptide_handle;
|
||||
|
||||
|
|
@ -12040,6 +12224,11 @@ void K_KartUpdatePosition(player_t *player)
|
|||
realplayers++;
|
||||
}
|
||||
}
|
||||
else if (K_InRaceDuel() && player->exiting)
|
||||
{
|
||||
// Positions directly set in K_CheckpointCrossAward, don't touch.
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
|
|
@ -12146,7 +12335,9 @@ void K_KartUpdatePosition(player_t *player)
|
|||
/* except in FREE PLAY */
|
||||
if (player->curshield == KSHIELD_TOP &&
|
||||
(gametyperules & GTR_CIRCUIT) &&
|
||||
realplayers > 1)
|
||||
realplayers > 1 &&
|
||||
!specialstageinfo.valid
|
||||
&& !K_Cooperative())
|
||||
{
|
||||
/* grace period so you don't fall off INSTANTLY */
|
||||
if (K_GetItemRouletteDistance(player, 8) < 2000 && player->topinfirst < 2*TICRATE) // "Why 8?" Literally no reason, but since we intend for constant-ish distance we choose a fake fixed playercount.
|
||||
|
|
@ -13708,7 +13899,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
{
|
||||
UINT32 behind = K_GetItemRouletteDistance(player, player->itemRoulette.playing);
|
||||
UINT32 behindScaled = behind * TICRATE / 4500;
|
||||
behindScaled = min(behindScaled, 10*TICRATE);
|
||||
behindScaled = min(behindScaled, 15*TICRATE);
|
||||
|
||||
K_DoInvincibility(player,
|
||||
max(7u * TICRATE + behindScaled, player->invincibilitytimer + 5u*TICRATE));
|
||||
|
|
@ -14261,7 +14452,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
{
|
||||
P_Thrust(
|
||||
player->mo, player->mo->angle,
|
||||
FixedMul((50*player->mo->scale), K_GetKartGameSpeedScalar(gamespeed))
|
||||
FixedMul((80*player->mo->scale), K_GetKartGameSpeedScalar(gamespeed))
|
||||
);
|
||||
|
||||
player->wavedashboost += TICRATE;
|
||||
|
|
@ -15277,8 +15468,17 @@ void K_EggmanTransfer(player_t *source, player_t *victim)
|
|||
if (victim->eggmanexplode)
|
||||
return;
|
||||
|
||||
boolean prank = false;
|
||||
|
||||
if (victim->itemRoulette.eggman)
|
||||
{
|
||||
K_StopRoulette(&source->itemRoulette);
|
||||
prank = true; // Give the transferring player the victim's eggbox roulette?!
|
||||
}
|
||||
|
||||
K_AddHitLag(victim->mo, 5, false);
|
||||
K_DropItems(victim);
|
||||
|
||||
victim->eggmanexplode = 6*TICRATE;
|
||||
victim->eggmanblame = (source - players);
|
||||
K_StopRoulette(&victim->itemRoulette);
|
||||
|
|
@ -15287,9 +15487,20 @@ void K_EggmanTransfer(player_t *source, player_t *victim)
|
|||
S_StartSound(NULL, sfx_itrole);
|
||||
|
||||
K_AddHitLag(source->mo, 5, false);
|
||||
source->eggmanexplode = 0;
|
||||
source->eggmanblame = -1;
|
||||
K_StopRoulette(&source->itemRoulette);
|
||||
|
||||
if (prank)
|
||||
{
|
||||
source->eggmanexplode = 0;
|
||||
source->eggmanblame = (victim - players);
|
||||
K_StartEggmanRoulette(source);
|
||||
S_StartSound(source->mo, sfx_s223);
|
||||
}
|
||||
else
|
||||
{
|
||||
source->eggmanexplode = 0;
|
||||
source->eggmanblame = -1;
|
||||
K_StopRoulette(&source->itemRoulette);
|
||||
}
|
||||
|
||||
source->eggmanTransferDelay = 25;
|
||||
victim->eggmanTransferDelay = 15;
|
||||
|
|
@ -15517,7 +15728,7 @@ boolean K_PlayerCanUseItem(player_t *player)
|
|||
return (player->mo->health > 0 && !player->spectator && !P_PlayerInPain(player) && !mapreset && leveltime > introtime);
|
||||
}
|
||||
|
||||
fixed_t K_GetGradingMultAdjustment(player_t *player)
|
||||
fixed_t K_GetGradingFactorAdjustment(player_t *player)
|
||||
{
|
||||
fixed_t power = 3*FRACUNIT/100; // adjust to change overall xp volatility
|
||||
fixed_t stablerate = 3*FRACUNIT/10; // how low is your placement before losing XP? 4*FRACUNIT/10 = top 40% of race will gain
|
||||
|
|
@ -15571,13 +15782,57 @@ fixed_t K_GetGradingMultAdjustment(player_t *player)
|
|||
return result;
|
||||
}
|
||||
|
||||
fixed_t K_GetGradingFactorMinMax(UINT32 gradingpointnum, boolean max)
|
||||
{
|
||||
// Create a dummy player structure for the theoretical last-place player
|
||||
player_t dummy_player;
|
||||
memset(&dummy_player, 0, sizeof(player_t));
|
||||
dummy_player.gradingfactor = FRACUNIT; // Start at 1.0
|
||||
|
||||
if (G_GametypeHasTeams())
|
||||
{
|
||||
const UINT8 orange_count = G_CountTeam(TEAM_ORANGE);
|
||||
const UINT8 blue_count = G_CountTeam(TEAM_BLUE);
|
||||
if (orange_count <= blue_count)
|
||||
{
|
||||
dummy_player.team = TEAM_ORANGE;
|
||||
}
|
||||
else
|
||||
{
|
||||
dummy_player.team = TEAM_BLUE;
|
||||
}
|
||||
dummy_player.position = max ? 0 : D_NumPlayersInRace() + 1; // Ensures that all enemy players are counted, and our dummy won't overlap
|
||||
}
|
||||
else
|
||||
{
|
||||
dummy_player.position = max ? 1 : D_NumPlayersInRace();
|
||||
}
|
||||
|
||||
// Apply the adjustment for each grading point
|
||||
for (UINT32 i = 0; i < gradingpointnum; i++)
|
||||
{
|
||||
dummy_player.gradingfactor += K_GetGradingFactorAdjustment(&dummy_player);
|
||||
}
|
||||
return dummy_player.gradingfactor;
|
||||
}
|
||||
|
||||
UINT16 K_GetEXP(player_t *player)
|
||||
{
|
||||
UINT32 numgradingpoints = K_GetNumGradingPoints();
|
||||
// target is where you should be if you're doing good and at a 1.0 mult
|
||||
fixed_t clampedmult = max(FRACUNIT/2, min(FRACUNIT*5/4, player->gradingfactor)); // clamp between 0.5 and 1.25
|
||||
fixed_t targetexp = (TARGETEXP*player->gradingpointnum/max(1,numgradingpoints))<<FRACBITS;
|
||||
UINT16 exp = FixedMul(clampedmult, targetexp)>>FRACBITS;
|
||||
UINT16 targetminexp = (MINEXP*player->gradingpointnum/max(1,numgradingpoints)); // about what a last place player should be at this stage of the race
|
||||
UINT16 targetexp = (MAXEXP*player->gradingpointnum/max(1,numgradingpoints)); // about what a 1.0 factor should be at this stage of the race
|
||||
fixed_t factormin = K_GetGradingFactorMinMax(player->gradingpointnum, false);
|
||||
fixed_t factormax = K_GetGradingFactorMinMax(player->gradingpointnum, true);
|
||||
fixed_t clampedfactor = max(factormin, min(factormax, player->gradingfactor));
|
||||
fixed_t range = factormax - factormin;
|
||||
fixed_t normalizedfactor = FixedDiv(clampedfactor - factormin, range);
|
||||
fixed_t easedexp = Easing_Linear(normalizedfactor, targetminexp, targetexp);
|
||||
// fixed_t easedexp = Easing_Linear(normalizedfactor, MINEXP*FRACUNIT, MAXEXP*FRACUNIT);
|
||||
UINT16 exp = easedexp;
|
||||
// CONS_Printf("Player %s numgradingpoints=%d targetminexp=%d targetexp=%d factormin=%.2f factormax=%.2f clampedfactor=%.2f normalizedfactor=%.2f easedexp=%d\n",
|
||||
// player_names[player - players], numgradingpoints, targetminexp, targetexp, FIXED_TO_FLOAT(factormin), FIXED_TO_FLOAT(factormax),
|
||||
// FIXED_TO_FLOAT(clampedfactor), FIXED_TO_FLOAT(normalizedfactor), easedexp);
|
||||
// UINT16 exp = (player->gradingfactor*100)>>FRACBITS;
|
||||
return exp;
|
||||
}
|
||||
|
||||
|
|
@ -15598,6 +15853,29 @@ void K_BotHitPenalty(player_t *player)
|
|||
}
|
||||
}
|
||||
|
||||
boolean K_IsPickMeUpItem(mobjtype_t type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MT_JAWZ:
|
||||
case MT_JAWZ_SHIELD:
|
||||
case MT_ORBINAUT:
|
||||
case MT_ORBINAUT_SHIELD:
|
||||
case MT_DROPTARGET:
|
||||
case MT_DROPTARGET_SHIELD:
|
||||
case MT_LANDMINE:
|
||||
case MT_BANANA:
|
||||
case MT_BANANA_SHIELD:
|
||||
case MT_GACHABOM:
|
||||
case MT_EGGMANITEM:
|
||||
case MT_EGGMANITEM_SHIELD:
|
||||
case MT_BUBBLESHIELDTRAP:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean K_PickUp(player_t *player, mobj_t *picked)
|
||||
{
|
||||
SINT8 type = -1;
|
||||
|
|
@ -15688,7 +15966,7 @@ static boolean K_PickUp(player_t *player, mobj_t *picked)
|
|||
}
|
||||
|
||||
// ACHTUNG this destroys items when returning true, make sure to bail out
|
||||
boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2)
|
||||
boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2, boolean allowHostile)
|
||||
{
|
||||
if (!m1 || P_MobjWasRemoved(m1))
|
||||
return false;
|
||||
|
|
@ -15723,7 +16001,7 @@ boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2)
|
|||
if (inflictor->target->player && G_SameTeam(inflictor->target->player, victim->player))
|
||||
allied = true;
|
||||
|
||||
if (!allied)
|
||||
if (!allied && !allowHostile)
|
||||
return false;
|
||||
|
||||
// CONS_Printf("target check passed\n");
|
||||
|
|
|
|||
16
src/k_kart.h
16
src/k_kart.h
|
|
@ -64,6 +64,9 @@ Make sure this matches the actual number of states
|
|||
|
||||
#define EARLY_ITEM_FLICKER (NUMTRANSMAPS)
|
||||
|
||||
#define TRIPWIRE_OK_SOUND (sfx_s3k40)
|
||||
#define TRIPWIRE_NG_SOUND (sfx_gshaf)
|
||||
|
||||
// 2023-08-26 +ang20 to Sal's OG values to make them friendlier - Tyron
|
||||
#define STUMBLE_STEEP_VAL (ANG60 + ANG20)
|
||||
#define STUMBLE_STEEP_VAL_AIR (ANG30 + ANG10 + ANG20)
|
||||
|
|
@ -80,6 +83,9 @@ Make sure this matches the actual number of states
|
|||
#define RINGVOLUMEREGEN 1
|
||||
#define RINGTRANSPARENCYREGEN 3
|
||||
|
||||
#define DUELOVERTIME (cv_dueltimelimit.value)
|
||||
#define DUELWINNINGSCORE (cv_duelscorelimit.value)
|
||||
|
||||
#define MIN_WAVEDASH_CHARGE ((11*TICRATE/16)*9)
|
||||
|
||||
#define MAXTOPACCEL (12*FRACUNIT)
|
||||
|
|
@ -105,6 +111,8 @@ angle_t K_ReflectAngle(angle_t angle, angle_t against, fixed_t maxspeed, fixed_t
|
|||
|
||||
boolean K_IsDuelItem(mobjtype_t type);
|
||||
boolean K_DuelItemAlwaysSpawns(mapthing_t *mt);
|
||||
boolean K_InRaceDuel(void);
|
||||
player_t *K_DuelOpponent(player_t *player);
|
||||
|
||||
void K_TimerReset(void);
|
||||
void K_TimerInit(void);
|
||||
|
|
@ -310,8 +318,8 @@ boolean K_ThunderDome(void);
|
|||
|
||||
boolean K_PlayerCanUseItem(player_t *player);
|
||||
|
||||
fixed_t K_GetGradingMultAdjustment(player_t *player);
|
||||
|
||||
fixed_t K_GetGradingFactorAdjustment(player_t *player);
|
||||
fixed_t K_GetGradingFactorMinMax(UINT32 gradingpointnum, boolean max);
|
||||
UINT16 K_GetEXP(player_t *player);
|
||||
|
||||
UINT32 K_GetNumGradingPoints(void);
|
||||
|
|
@ -320,7 +328,9 @@ boolean K_LegacyRingboost(player_t *player);
|
|||
|
||||
void K_BotHitPenalty(player_t *player);
|
||||
|
||||
boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2);
|
||||
boolean K_IsPickMeUpItem(mobjtype_t type);
|
||||
|
||||
boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2, boolean allowHostile);
|
||||
|
||||
fixed_t K_TeamComebackMultiplier(player_t *player);
|
||||
|
||||
|
|
|
|||
|
|
@ -593,10 +593,10 @@ extern menu_t PAUSE_PlaybackMenuDef;
|
|||
typedef enum
|
||||
{
|
||||
playback_hide,
|
||||
playback_restart,
|
||||
playback_rewind,
|
||||
playback_pause,
|
||||
playback_fastforward,
|
||||
playback_backframe,
|
||||
playback_resume,
|
||||
playback_advanceframe,
|
||||
playback_viewcount,
|
||||
|
|
@ -923,6 +923,7 @@ typedef struct levellist_s {
|
|||
UINT8 guessgt;
|
||||
levelsearch_t levelsearch;
|
||||
boolean netgame; // Start the game in an actual server
|
||||
boolean canqueue;
|
||||
menu_t *backMenu;
|
||||
} levellist_t;
|
||||
|
||||
|
|
@ -944,6 +945,8 @@ void M_CupSelectTick(void);
|
|||
void M_LevelSelectHandler(INT32 choice);
|
||||
void M_LevelSelectTick(void);
|
||||
|
||||
INT16 M_LevelFromScrolledList(INT16 add);
|
||||
void M_MenuToLevelPreamble(UINT8 ssplayers, boolean nowipe);
|
||||
void M_LevelSelected(INT16 add, boolean menuupdate);
|
||||
boolean M_LevelSelectCupSwitch(boolean next, boolean skipones);
|
||||
|
||||
|
|
|
|||
|
|
@ -755,33 +755,40 @@ static void M_DrawMenuTyping(void)
|
|||
|
||||
}
|
||||
|
||||
// Largely replaced by boxed drawing mode in K_DrawGameControl and rich text
|
||||
/*
|
||||
static void M_DrawMediocreKeyboardKey(const char *text, INT32 *workx, INT32 worky, boolean push, boolean rightaligned)
|
||||
static void M_DrawPauseRoundQueue(INT16 offset, boolean canqueue)
|
||||
{
|
||||
INT32 buttonwidth = V_StringWidth(text, 0) + 2;
|
||||
y_data_t standings;
|
||||
memset(&standings, 0, sizeof (standings));
|
||||
|
||||
if (rightaligned)
|
||||
if (gamestate == GS_MENU)
|
||||
{
|
||||
(*workx) -= buttonwidth;
|
||||
}
|
||||
|
||||
if (push)
|
||||
{
|
||||
worky += 2;
|
||||
standings.mainplayer = MAXPLAYERS;
|
||||
}
|
||||
else
|
||||
{
|
||||
V_DrawFill((*workx)-1, worky+10, buttonwidth, 2, 24);
|
||||
standings.mainplayer = (demo.playback ? displayplayers[0] : consoleplayer);
|
||||
}
|
||||
|
||||
V_DrawFill((*workx)-1, worky, buttonwidth, 10, 16);
|
||||
V_DrawString(
|
||||
(*workx), worky + 1,
|
||||
0, text
|
||||
);
|
||||
// See also G_GetNextMap, Y_CalculateMatchData
|
||||
if (
|
||||
canqueue == false
|
||||
&& grandprixinfo.gp == true
|
||||
&& netgame == false // TODO netgame Special Mode support
|
||||
&& grandprixinfo.gamespeed >= KARTSPEED_NORMAL
|
||||
&& roundqueue.size > 1
|
||||
&& roundqueue.entries[roundqueue.size - 1].rankrestricted == true
|
||||
&& (
|
||||
gamedata->everseenspecial == true
|
||||
|| roundqueue.position == roundqueue.size
|
||||
)
|
||||
)
|
||||
{
|
||||
// Additional cases in which it should always be shown.
|
||||
standings.showrank = true;
|
||||
}
|
||||
|
||||
Y_RoundQueueDrawer(&standings, offset, false, false, canqueue);
|
||||
}
|
||||
*/
|
||||
|
||||
// Draw the message popup submenu
|
||||
void M_DrawMenuMessage(void)
|
||||
|
|
@ -991,6 +998,16 @@ void M_Drawer(void)
|
|||
|
||||
// Draw message overlay when needed
|
||||
M_DrawMenuMessage();
|
||||
|
||||
if (
|
||||
(
|
||||
currentMenu == &PLAY_LevelSelectDef
|
||||
|| currentMenu == &PLAY_CupSelectDef
|
||||
) && levellist.canqueue
|
||||
)
|
||||
{
|
||||
M_DrawPauseRoundQueue(0, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (menuwipe)
|
||||
|
|
@ -6288,30 +6305,11 @@ void M_DrawPause(void)
|
|||
V_DrawCenteredLSTitleLowString(220 + offset*2, 103, mainflags, word2);
|
||||
}
|
||||
|
||||
const boolean rulescheck = (K_CanChangeRules(false) && (server || IsPlayerAdmin(consoleplayer)));
|
||||
boolean drawqueue = (rulescheck && (menuqueue.size > 0));
|
||||
|
||||
if (gamestate != GS_INTERMISSION && roundqueue.size > 0)
|
||||
{
|
||||
y_data_t standings;
|
||||
memset(&standings, 0, sizeof (standings));
|
||||
|
||||
standings.mainplayer = (demo.playback ? displayplayers[0] : consoleplayer);
|
||||
|
||||
// See also G_GetNextMap, Y_CalculateMatchData
|
||||
if (
|
||||
grandprixinfo.gp == true
|
||||
&& netgame == false // TODO netgame Special Mode support
|
||||
&& grandprixinfo.gamespeed >= KARTSPEED_NORMAL
|
||||
&& roundqueue.size > 1
|
||||
&& roundqueue.entries[roundqueue.size - 1].rankrestricted == true
|
||||
&& (
|
||||
gamedata->everseenspecial == true
|
||||
|| roundqueue.position == roundqueue.size
|
||||
)
|
||||
)
|
||||
{
|
||||
// Additional cases in which it should always be shown.
|
||||
standings.showrank = true;
|
||||
}
|
||||
|
||||
if (roundqueue.position > 0 && roundqueue.position <= roundqueue.size)
|
||||
{
|
||||
patch_t *smallroundpatch = ST_getRoundPicture(true);
|
||||
|
|
@ -6328,7 +6326,7 @@ void M_DrawPause(void)
|
|||
|
||||
V_DrawCenteredMenuString(24, 167 + offset/2, V_YELLOWMAP, M_GetGameplayMode());
|
||||
|
||||
Y_RoundQueueDrawer(&standings, offset/2, false, false);
|
||||
drawqueue = true;
|
||||
}
|
||||
else if (gametype == GT_TUTORIAL)
|
||||
{
|
||||
|
|
@ -6347,6 +6345,11 @@ void M_DrawPause(void)
|
|||
{
|
||||
V_DrawMenuString(4, 188 + offset/2, V_YELLOWMAP, M_GetGameplayMode());
|
||||
}
|
||||
|
||||
if (drawqueue)
|
||||
{
|
||||
M_DrawPauseRoundQueue(offset/2, rulescheck);
|
||||
}
|
||||
}
|
||||
|
||||
void M_DrawKickHandler(void)
|
||||
|
|
@ -6485,7 +6488,7 @@ void M_DrawPlaybackMenu(void)
|
|||
else if (currentMenu->menuitems[i].patch && W_CheckNumForName(currentMenu->menuitems[i].patch) != LUMPERROR)
|
||||
icon = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE);
|
||||
|
||||
if ((i == playback_fastforward && cv_playbackspeed.value > 1) || (i == playback_rewind && demo.rewinding))
|
||||
if ((i == playback_fastforward && cv_playbackspeed.value > 1))
|
||||
V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].mvar1, currentMenu->y, V_SNAPTOTOP, icon, R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_JAWZ, GTC_MENUCACHE));
|
||||
else
|
||||
V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].mvar1, currentMenu->y, V_SNAPTOTOP, icon, (i == itemOn) ? activemap : inactivemap);
|
||||
|
|
|
|||
|
|
@ -466,7 +466,7 @@ boolean M_Responder(event_t *ev)
|
|||
if (Playing() && !demo.playback)
|
||||
{
|
||||
// Quick Retry (Y in modeattacking)
|
||||
if (modeattacking && G_PlayerInputDown(0, gc_y, splitscreen + 1) == true)
|
||||
if (modeattacking && G_PlayerInputDown(0, gc_respawn, splitscreen + 1) == true)
|
||||
{
|
||||
M_TryAgain(0);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -411,7 +411,6 @@ void Obj_SSGobletMobjThink(mobj_t* mo);
|
|||
void Obj_SSLampMapThingSpawn(mobj_t* mo, mapthing_t* mt);
|
||||
void Obj_SSWindowMapThingSpawn(mobj_t* mo, mapthing_t* mt);
|
||||
void Obj_SLSTMaceMobjThink(mobj_t* mo);
|
||||
void Obj_SSBumperTouchSpecial(mobj_t* special, mobj_t* toucher);
|
||||
void Obj_SSBumperMobjSpawn(mobj_t* mo);
|
||||
void Obj_SSChainMobjThink(mobj_t* mo);
|
||||
void Obj_SSGachaTargetMobjSpawn(mobj_t* mo);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include "k_grandprix.h"
|
||||
#include "k_profiles.h"
|
||||
#include "k_serverstats.h"
|
||||
#include "k_kart.h" // K_InRaceDuel
|
||||
|
||||
// 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.
|
||||
|
|
@ -213,6 +214,10 @@ void K_UpdatePowerLevels(player_t *player, UINT8 lap, boolean forfeit)
|
|||
CONS_Debug(DBG_PWRLV, "========\n");
|
||||
|
||||
yourPower = clientpowerlevels[playerNum][powerType];
|
||||
|
||||
if (K_InRaceDuel())
|
||||
yourPower += clientPowerAdd[playerNum];
|
||||
|
||||
if (yourPower == 0)
|
||||
{
|
||||
// Guests don't record power level changes.
|
||||
|
|
@ -225,6 +230,8 @@ void K_UpdatePowerLevels(player_t *player, UINT8 lap, boolean forfeit)
|
|||
CONS_Debug(DBG_PWRLV, "%s's gametype score: %d\n", player_names[playerNum], yourScore);
|
||||
|
||||
CONS_Debug(DBG_PWRLV, "========\n");
|
||||
|
||||
boolean dueling = K_InRaceDuel();
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
UINT16 theirScore = 0;
|
||||
|
|
@ -254,6 +261,9 @@ void K_UpdatePowerLevels(player_t *player, UINT8 lap, boolean forfeit)
|
|||
CONS_Debug(DBG_PWRLV, "%s VS %s:\n", player_names[playerNum], player_names[i]);
|
||||
|
||||
theirPower = clientpowerlevels[i][powerType];
|
||||
if (K_InRaceDuel())
|
||||
theirPower += clientPowerAdd[i];
|
||||
|
||||
if (theirPower == 0)
|
||||
{
|
||||
// No power level (splitscreen guests, bots)
|
||||
|
|
@ -295,11 +305,13 @@ void K_UpdatePowerLevels(player_t *player, UINT8 lap, boolean forfeit)
|
|||
}
|
||||
}
|
||||
|
||||
if (exitBonus == false)
|
||||
if (dueling)
|
||||
{
|
||||
INT16 prevInc = inc;
|
||||
|
||||
inc /= max(numlaps-1, 1);
|
||||
// INT32 winnerscore = (yourScore > theirScore) ? player->duelscore : players[i].duelscore;
|
||||
INT32 multiplier = 2;
|
||||
inc *= multiplier;
|
||||
|
||||
if (inc == 0)
|
||||
{
|
||||
|
|
@ -313,7 +325,32 @@ void K_UpdatePowerLevels(player_t *player, UINT8 lap, boolean forfeit)
|
|||
}
|
||||
}
|
||||
|
||||
CONS_Debug(DBG_PWRLV, "Reduced (%d / %d = %d) because it's not the end of the race\n", prevInc, numlaps, inc);
|
||||
// CONS_Printf("%s PWR UPDATE: %d\n", player_names[player - players], inc);
|
||||
|
||||
CONS_Debug(DBG_PWRLV, "DUELING: Boosted (%d * %d = %d)\n", prevInc, multiplier, inc);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (exitBonus == false)
|
||||
{
|
||||
INT16 prevInc = inc;
|
||||
|
||||
inc /= max(numlaps-1, 1);
|
||||
|
||||
if (inc == 0)
|
||||
{
|
||||
if (prevInc > 0)
|
||||
{
|
||||
inc = 1;
|
||||
}
|
||||
else if (prevInc < 0)
|
||||
{
|
||||
inc = -1;
|
||||
}
|
||||
}
|
||||
|
||||
CONS_Debug(DBG_PWRLV, "Reduced (%d / %d = %d) because it's not the end of the race\n", prevInc, numlaps, inc);
|
||||
}
|
||||
}
|
||||
|
||||
CONS_Debug(DBG_PWRLV, "========\n");
|
||||
|
|
@ -346,6 +383,10 @@ void K_UpdatePowerLevelsFinalize(player_t *player, boolean onForfeit)
|
|||
INT16 lapsLeft = 0;
|
||||
UINT8 i;
|
||||
|
||||
// No remaining laps in Duel.
|
||||
if (K_InRaceDuel())
|
||||
return;
|
||||
|
||||
lapsLeft = (numlaps - player->latestlap) + 1;
|
||||
|
||||
if (lapsLeft <= 0)
|
||||
|
|
@ -394,7 +435,7 @@ INT16 K_FinalPowerIncrement(player_t *player, INT16 yourPower, INT16 baseInc)
|
|||
|
||||
if (inc <= 0)
|
||||
{
|
||||
if (player->position == 1 && numPlayers > 1)
|
||||
if (player->position == 1 && numPlayers > 1 && !(K_InRaceDuel()))
|
||||
{
|
||||
// Won the whole match?
|
||||
// Get at least one point.
|
||||
|
|
|
|||
|
|
@ -46,6 +46,9 @@ boolean level_tally_t::UseBonuses(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (K_InRaceDuel())
|
||||
return false;
|
||||
|
||||
// No bonuses / ranking in FREE PLAY or Time Attack
|
||||
return (grandprixinfo.gp == true || K_TimeAttackRules() == false);
|
||||
}
|
||||
|
|
@ -218,7 +221,7 @@ INT32 level_tally_t::CalculateGrade(void)
|
|||
}
|
||||
}
|
||||
|
||||
const INT32 positionWeight = (position > 0 && numPlayers > 2) ? 50 : 0;
|
||||
const INT32 positionWeight = (position > 0 && numPlayers > 2) ? 20 : 0;
|
||||
const INT32 total = positionWeight + bonusWeights[0] + bonusWeights[1];
|
||||
|
||||
INT32 ours = 0;
|
||||
|
|
@ -242,10 +245,7 @@ INT32 level_tally_t::CalculateGrade(void)
|
|||
}
|
||||
case TALLY_BONUS_EXP:
|
||||
{
|
||||
// Use a special curve for this.
|
||||
// Low Exp amounts are guaranteed, higher than half is where skill expression starts
|
||||
// Magic numbers here are to reduce the range from 50-125 to 0-75 and compare with a max of 58, 85% of which is 49.3, which should put an even 100 or higher exp at A rank
|
||||
const fixed_t frac = std::min(FRACUNIT, ((exp-50) * FRACUNIT) / std::max(1, static_cast<int>(totalExp-42)));
|
||||
const fixed_t frac = std::min(FRACUNIT, ((exp-15) * FRACUNIT) / std::max(1, static_cast<int>(totalExp)));
|
||||
ours += Easing_Linear(frac, 0, bonusWeights[i]);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
697
src/k_vote.c
697
src/k_vote.c
|
|
@ -109,6 +109,7 @@
|
|||
// Give time for the animations to finish before finalizing the vote stages.
|
||||
#define SELECT_DELAY_TIME (TICRATE*4)
|
||||
#define PICK_DELAY_TIME (TICRATE/2)
|
||||
#define STRIKE_DELAY_TIME (TICRATE/3)
|
||||
|
||||
#define MAP_ANGER_MAX (2)
|
||||
|
||||
|
|
@ -179,12 +180,21 @@ typedef struct
|
|||
{
|
||||
INT32 timer;
|
||||
INT32 tic, endtic;
|
||||
INT32 selectFinalize, pickFinalize;
|
||||
INT32 selectFinalize, pickFinalize, strikeFinalize;
|
||||
boolean notYetPicked;
|
||||
boolean loaded;
|
||||
SINT8 deferredLevel;
|
||||
y_vote_player players[MAXSPLITSCREENPLAYERS];
|
||||
y_vote_roulette roulette;
|
||||
|
||||
// If both of these players are valid,
|
||||
// and they're the only players in the server,
|
||||
// then we want stage striking!
|
||||
player_t *strike_loser;
|
||||
player_t *strike_winner;
|
||||
boolean strike_turn;
|
||||
boolean strike_time_out;
|
||||
boolean stage_striking;
|
||||
} y_vote_data;
|
||||
|
||||
// Voting level drawing
|
||||
|
|
@ -209,6 +219,8 @@ typedef struct
|
|||
patch_t *ruby_icon;
|
||||
fixed_t ruby_height;
|
||||
|
||||
patch_t *strike_icon;
|
||||
|
||||
patch_t *bg_planet[PLANET_FRAMES];
|
||||
patch_t *bg_checker;
|
||||
patch_t *bg_levelText;
|
||||
|
|
@ -230,13 +242,54 @@ typedef struct
|
|||
static y_vote_data vote = {0};
|
||||
static y_vote_draw vote_draw = {0};
|
||||
|
||||
static void Y_SetVoteTimer(void)
|
||||
{
|
||||
vote.timer = cv_votetime.value * TICRATE;
|
||||
|
||||
if (vote.stage_striking == true)
|
||||
{
|
||||
vote.timer /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
static UINT8 Y_CountStriked(void)
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
UINT8 num_striked = 0;
|
||||
for (i = 0; i < VOTE_NUM_LEVELS; i++)
|
||||
{
|
||||
if (g_votes_striked[i] == true)
|
||||
{
|
||||
num_striked++;
|
||||
}
|
||||
}
|
||||
|
||||
return num_striked;
|
||||
}
|
||||
|
||||
static boolean Y_VoteIDIsSpecial(const UINT8 playerId)
|
||||
{
|
||||
switch (playerId)
|
||||
{
|
||||
case VOTE_SPECIAL:
|
||||
case VOTE_TIMEOUT_LOSER:
|
||||
case VOTE_TIMEOUT_WINNER:
|
||||
{
|
||||
// Special vote spot, always allow
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean Y_PlayerIDCanVote(const UINT8 playerId)
|
||||
{
|
||||
player_t *player = NULL;
|
||||
|
||||
if (playerId == VOTE_SPECIAL)
|
||||
if (Y_VoteIDIsSpecial(playerId) == true)
|
||||
{
|
||||
// Special vote spot, always allow
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -245,7 +298,7 @@ boolean Y_PlayerIDCanVote(const UINT8 playerId)
|
|||
return false;
|
||||
}
|
||||
|
||||
player = &players[playerId];
|
||||
const player_t *player = &players[playerId];
|
||||
if (player->spectator == true)
|
||||
{
|
||||
return false;
|
||||
|
|
@ -260,8 +313,48 @@ boolean Y_PlayerIDCanVote(const UINT8 playerId)
|
|||
return true;
|
||||
}
|
||||
|
||||
static boolean Y_IsPlayersTurn(const UINT8 playerId)
|
||||
{
|
||||
if (Y_VoteIDIsSpecial(playerId) == true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (vote.stage_striking == false)
|
||||
{
|
||||
// Not stage striking -- we can always vote.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Is it our turn to strike a stage?
|
||||
const player_t *player = &players[playerId];
|
||||
if (vote.strike_turn == true)
|
||||
{
|
||||
return (player == vote.strike_winner);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (player == vote.strike_loser);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean Y_PlayerIDCanVoteRightNow(const UINT8 playerId)
|
||||
{
|
||||
if (Y_IsPlayersTurn(playerId) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Y_PlayerIDCanVote(playerId);
|
||||
}
|
||||
|
||||
static boolean Y_PlayerCanSelect(const UINT8 localId)
|
||||
{
|
||||
if (localId > splitscreen)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const UINT8 p = g_localplayers[localId];
|
||||
|
||||
if (g_pickedVote != VOTE_NOT_PICKED)
|
||||
|
|
@ -280,7 +373,7 @@ static boolean Y_PlayerCanSelect(const UINT8 localId)
|
|||
return false;
|
||||
}
|
||||
|
||||
return Y_PlayerIDCanVote(p);
|
||||
return Y_PlayerIDCanVoteRightNow(p);
|
||||
}
|
||||
|
||||
static void Y_SortPile(void)
|
||||
|
|
@ -381,21 +474,73 @@ static void Y_SortPile(void)
|
|||
}
|
||||
}
|
||||
|
||||
void Y_SetPlayersVote(const UINT8 playerId, SINT8 newVote)
|
||||
void Y_SetPlayersVote(const UINT8 inputPlayerId, SINT8 newVote)
|
||||
{
|
||||
y_vote_pile *const pile = &vote.roulette.pile[playerId];
|
||||
y_vote_catcher *const catcher = &pile->catcher;
|
||||
INT32 i;
|
||||
|
||||
if (gamestate != GS_VOTING)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UINT8 playerId = inputPlayerId;
|
||||
|
||||
// Manually overwrite these players for timed out votes.
|
||||
// Loser/winner is encoded in the vote ID to prevent race
|
||||
// race conditions with real votes causing problems.
|
||||
if (inputPlayerId == VOTE_TIMEOUT_LOSER)
|
||||
{
|
||||
playerId = (vote.strike_loser - players);
|
||||
}
|
||||
else if (inputPlayerId == VOTE_TIMEOUT_WINNER)
|
||||
{
|
||||
playerId = (vote.strike_winner - players);
|
||||
}
|
||||
|
||||
if (newVote < 0 || newVote >= VOTE_NUM_LEVELS)
|
||||
{
|
||||
newVote = VOTE_NOT_PICKED;
|
||||
}
|
||||
|
||||
if (playerId < MAXPLAYERS)
|
||||
{
|
||||
if (Y_PlayerIDCanVoteRightNow(playerId) == false)
|
||||
{
|
||||
// Not your turn, dude!
|
||||
return;
|
||||
}
|
||||
|
||||
if (vote.stage_striking == true)
|
||||
{
|
||||
if (newVote != VOTE_NOT_PICKED
|
||||
&& g_votes_striked[newVote] == false
|
||||
&& Y_CountStriked() < VOTE_NUM_LEVELS-1)
|
||||
{
|
||||
// Strike a stage, instead of voting.
|
||||
g_votes_striked[newVote] = true;
|
||||
|
||||
// Change turn.
|
||||
vote.strike_turn = !vote.strike_turn;
|
||||
|
||||
// Reset variables.
|
||||
Y_SetVoteTimer();
|
||||
for (i = 0; i <= splitscreen; i++)
|
||||
{
|
||||
vote.players[i].sentTimeOutVote = false;
|
||||
vote.players[i].delay = NEWTICRATE/7;
|
||||
}
|
||||
vote.strike_time_out = false;
|
||||
|
||||
// TODO: striking animation
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
y_vote_pile *const pile = &vote.roulette.pile[playerId];
|
||||
y_vote_catcher *const catcher = &pile->catcher;
|
||||
|
||||
g_votes[playerId] = newVote;
|
||||
|
||||
Y_SortPile();
|
||||
|
|
@ -416,12 +561,12 @@ void Y_SetPlayersVote(const UINT8 playerId, SINT8 newVote)
|
|||
if (vote.timer == -1)
|
||||
{
|
||||
// Someone has voted, so start the timer now.
|
||||
vote.timer = cv_votetime.value * TICRATE;
|
||||
Y_SetVoteTimer();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void Y_DrawVoteThumbnail(fixed_t center_x, fixed_t center_y, fixed_t width, INT32 flags, SINT8 v, boolean dim, SINT8 playerID)
|
||||
static void Y_DrawVoteThumbnail(fixed_t center_x, fixed_t center_y, fixed_t width, INT32 flags, SINT8 v, boolean dim, SINT8 playerID, boolean from_selection)
|
||||
{
|
||||
const boolean encore = vote_draw.levels[v].encore;
|
||||
const fixed_t height = (width * BASEVIDHEIGHT) / BASEVIDWIDTH;
|
||||
|
|
@ -468,29 +613,64 @@ static void Y_DrawVoteThumbnail(fixed_t center_x, fixed_t center_y, fixed_t widt
|
|||
|
||||
V_AdjustXYWithSnap(&fx, &fy, flags, dupx, dupy);
|
||||
|
||||
boolean striked = false;
|
||||
if (from_selection == true)
|
||||
{
|
||||
striked = g_votes_striked[v];
|
||||
}
|
||||
|
||||
V_DrawFill(
|
||||
fx - dupx, fy - dupy,
|
||||
fw + (dupx << 1), fh + (dupy << 1),
|
||||
0|flags|V_NOSCALESTART
|
||||
);
|
||||
|
||||
K_DrawMapThumbnail(
|
||||
x, y,
|
||||
width, flags | ((encore == true) ? V_FLIP : 0),
|
||||
g_voteLevels[v][0],
|
||||
(dim == true ? R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_MENUCACHE) : NULL)
|
||||
);
|
||||
|
||||
if (encore == true)
|
||||
if (striked == true)
|
||||
{
|
||||
const fixed_t rubyScale = width / 72;
|
||||
const fixed_t strikeScale = width / 32;
|
||||
V_DrawFixedPatch(
|
||||
center_x, center_y - FixedMul(vote_draw.ruby_height << 1, rubyScale),
|
||||
rubyScale, flags,
|
||||
vote_draw.ruby_icon,
|
||||
center_x - (strikeScale * 25 / 2), center_y - (strikeScale * 22 / 2),
|
||||
strikeScale, flags,
|
||||
vote_draw.strike_icon,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
K_DrawMapThumbnail(
|
||||
x, y,
|
||||
width, flags | ((encore == true) ? V_FLIP : 0),
|
||||
g_voteLevels[v][0],
|
||||
(dim == true ? R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_MENUCACHE) : NULL)
|
||||
);
|
||||
|
||||
if (encore == true)
|
||||
{
|
||||
const fixed_t rubyScale = width / 72;
|
||||
V_DrawFixedPatch(
|
||||
center_x, center_y - FixedMul(vote_draw.ruby_height << 1, rubyScale),
|
||||
rubyScale, flags,
|
||||
vote_draw.ruby_icon,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
if (vote.stage_striking == true
|
||||
&& from_selection == true
|
||||
&& dim == false)
|
||||
{
|
||||
if (Y_CountStriked() < VOTE_NUM_LEVELS-1)
|
||||
{
|
||||
const fixed_t strikeScale = width / 32;
|
||||
V_DrawFixedPatch(
|
||||
center_x - (strikeScale * 25 / 2), center_y - (strikeScale * 22 / 2),
|
||||
strikeScale, flags /*| V_TRANSLUCENT*/,
|
||||
vote_draw.strike_icon,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dim == true)
|
||||
{
|
||||
|
|
@ -506,7 +686,7 @@ static void Y_DrawVoteThumbnail(fixed_t center_x, fixed_t center_y, fixed_t widt
|
|||
{
|
||||
const INT32 whiteSq = 16 * dupx;
|
||||
|
||||
if (playerID < MAXPLAYERS)
|
||||
if (playerID < MAXPLAYERS) // Player vote
|
||||
{
|
||||
UINT8 *playerMap = R_GetTranslationColormap(players[playerID].skin, players[playerID].skincolor, GTC_CACHE);
|
||||
patch_t *playerPatch = faceprefix[players[playerID].skin][FACE_RANK];
|
||||
|
|
@ -518,37 +698,15 @@ static void Y_DrawVoteThumbnail(fixed_t center_x, fixed_t center_y, fixed_t widt
|
|||
playerPatch, playerMap
|
||||
);
|
||||
}
|
||||
else
|
||||
else if (vote.stage_striking == false) // Angry map
|
||||
{
|
||||
const fixed_t iconHeight = (14 << FRACBITS);
|
||||
const fixed_t iconWidth = (iconHeight * 320) / 200;
|
||||
|
||||
V_DrawFill(
|
||||
K_DrawMapAsFace(
|
||||
fx + fw - whiteSq + dupx,
|
||||
fy + fh - whiteSq + dupy,
|
||||
whiteSq,
|
||||
whiteSq,
|
||||
0|flags|V_NOSCALESTART
|
||||
);
|
||||
|
||||
V_SetClipRect(
|
||||
fx + fw - whiteSq + (2 * dupx),
|
||||
fy + fh - whiteSq + (2 * dupy),
|
||||
whiteSq - (2 * dupx),
|
||||
whiteSq - (2 * dupy),
|
||||
flags|V_NOSCALESTART
|
||||
);
|
||||
|
||||
K_DrawMapThumbnail(
|
||||
((fx + fw - whiteSq + (2 * dupx)) * FRACUNIT) - (iconWidth - iconHeight),
|
||||
(fy + fh - whiteSq + (2 * dupy)) * FRACUNIT,
|
||||
iconWidth,
|
||||
flags | V_NOSCALESTART | ((encore == true) ? V_FLIP : 0),
|
||||
g_voteLevels[v][0],
|
||||
NULL
|
||||
);
|
||||
|
||||
V_ClearClipRect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -618,7 +776,7 @@ static void Y_DrawCatcher(y_vote_catcher *catcher)
|
|||
baseX, catcher->y,
|
||||
((catcher->small == true) ? PILE_WIDTH : SELECTION_WIDTH), 0,
|
||||
catcher->level, false,
|
||||
catcher->player
|
||||
catcher->player, false
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -813,7 +971,7 @@ static void Y_DrawVoteSelection(fixed_t offset)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (g_votes[p] != VOTE_NOT_PICKED || Y_PlayerIDCanVote(p) == false)
|
||||
if (g_votes[p] != VOTE_NOT_PICKED || Y_PlayerIDCanVoteRightNow(p) == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -881,12 +1039,41 @@ static void Y_DrawVoteSelection(fixed_t offset)
|
|||
x, y - vote_draw.levels[i].hop,
|
||||
SELECTION_WIDTH, 0,
|
||||
i, (selected == false),
|
||||
-1
|
||||
-1, true
|
||||
);
|
||||
|
||||
x += SELECTION_SPACING_W;
|
||||
}
|
||||
|
||||
if (vote.stage_striking == true && Y_CountStriked() < VOTE_NUM_LEVELS-1)
|
||||
{
|
||||
UINT8 current_strike_player = (
|
||||
(vote.strike_turn == true)
|
||||
? (vote.strike_winner - players)
|
||||
: (vote.strike_loser - players)
|
||||
);
|
||||
|
||||
for (i = 0; i <= splitscreen; i++)
|
||||
{
|
||||
if (g_localplayers[i] == current_strike_player)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > splitscreen)
|
||||
{
|
||||
const char *wait_str = va("Waiting for %s...", player_names[current_strike_player]);
|
||||
|
||||
V_DrawThinString(
|
||||
BASEVIDWIDTH / 2 - (V_ThinStringWidth(wait_str, 0) / 2),
|
||||
180,
|
||||
0,
|
||||
wait_str
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Draw our catchers
|
||||
//
|
||||
|
|
@ -944,7 +1131,7 @@ static void Y_DrawVotePile(void)
|
|||
PILE_WIDTH, 0,
|
||||
g_votes[i],
|
||||
(i != vote.roulette.anim || g_pickedVote == VOTE_NOT_PICKED),
|
||||
i
|
||||
i, false
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1060,8 +1247,30 @@ static void Y_VoteStops(SINT8 pick, SINT8 level)
|
|||
}
|
||||
}
|
||||
|
||||
static void Y_PlayerSendStrike(const UINT8 localPlayer)
|
||||
{
|
||||
y_vote_player *const player = &vote.players[localPlayer];
|
||||
y_vote_catcher *const catcher = &player->catcher;
|
||||
|
||||
if (g_votes_striked[player->selection] == true)
|
||||
{
|
||||
// TODO: "Can't select" animation
|
||||
return;
|
||||
}
|
||||
|
||||
D_ModifyClientVote(g_localplayers[localPlayer], player->selection);
|
||||
catcher->action = CATCHER_NA;
|
||||
catcher->delay = 5;
|
||||
}
|
||||
|
||||
static void Y_PlayerSendVote(const UINT8 localPlayer)
|
||||
{
|
||||
if (vote.stage_striking == true)
|
||||
{
|
||||
Y_PlayerSendStrike(localPlayer);
|
||||
return;
|
||||
}
|
||||
|
||||
y_vote_player *const player = &vote.players[localPlayer];
|
||||
y_vote_catcher *const catcher = &player->catcher;
|
||||
|
||||
|
|
@ -1188,7 +1397,11 @@ static void Y_TickPlayerCatcher(const UINT8 localPlayer)
|
|||
{
|
||||
if (catcher->x == catcher->destX && catcher->y == catcher->destY)
|
||||
{
|
||||
D_ModifyClientVote(g_localplayers[localPlayer], vote.players[localPlayer].selection);
|
||||
if (vote.stage_striking == false)
|
||||
{
|
||||
D_ModifyClientVote(g_localplayers[localPlayer], vote.players[localPlayer].selection);
|
||||
}
|
||||
|
||||
catcher->action = CATCHER_NA;
|
||||
catcher->delay = 5;
|
||||
S_StopSoundByNum(sfx_kc37);
|
||||
|
|
@ -1434,6 +1647,201 @@ static SINT8 Y_TryMapAngerVote(void)
|
|||
return angryMaps[pick];
|
||||
}
|
||||
|
||||
static void Y_ExitStageStrike(void)
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
vote.stage_striking = false;
|
||||
|
||||
for (i = 0; i < VOTE_NUM_LEVELS; i++)
|
||||
{
|
||||
g_votes_striked[i] = false;
|
||||
}
|
||||
|
||||
vote.strike_loser = NULL;
|
||||
vote.strike_winner = NULL;
|
||||
vote.strike_turn = false;
|
||||
vote.strike_time_out = false;
|
||||
|
||||
Y_SetVoteTimer();
|
||||
}
|
||||
|
||||
static boolean Y_CheckStageStrikeStatus(void)
|
||||
{
|
||||
INT32 i;
|
||||
UINT8 num_voters = 0;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (Y_PlayerIDCanVote(i) == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
num_voters++;
|
||||
if (num_voters > 2)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_voters != 2)
|
||||
{
|
||||
// Someone joined or left. Stage striking is broken!
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vote.strike_loser == NULL || Y_PlayerIDCanVote(vote.strike_loser - players) == false)
|
||||
{
|
||||
// Loser is invalidated!
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vote.strike_winner == NULL || Y_PlayerIDCanVote(vote.strike_winner - players) == false)
|
||||
{
|
||||
// Winner is invalidated!
|
||||
return false;
|
||||
}
|
||||
|
||||
// Looks good, we can tick stage striking.
|
||||
return true;
|
||||
}
|
||||
|
||||
static void Y_TickVoteStageStrike(void)
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
if (Y_CheckStageStrikeStatus() == false)
|
||||
{
|
||||
Y_ExitStageStrike();
|
||||
return;
|
||||
}
|
||||
|
||||
SINT8 the_only_level = VOTE_NOT_PICKED;
|
||||
for (i = 0; i < VOTE_NUM_LEVELS; i++)
|
||||
{
|
||||
if (g_votes_striked[i] == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (the_only_level != VOTE_NOT_PICKED)
|
||||
{
|
||||
// More than 1 valid level.
|
||||
// Unset and stop iterating.
|
||||
the_only_level = VOTE_NOT_PICKED;
|
||||
break;
|
||||
}
|
||||
|
||||
the_only_level = i;
|
||||
}
|
||||
|
||||
if (the_only_level != VOTE_NOT_PICKED)
|
||||
{
|
||||
vote.timer = 0;
|
||||
vote.strikeFinalize = STRIKE_DELAY_TIME;
|
||||
|
||||
if (vote.selectFinalize < SELECT_DELAY_TIME)
|
||||
{
|
||||
if (vote.selectFinalize == 0)
|
||||
{
|
||||
for (i = 0; i <= splitscreen; i++)
|
||||
{
|
||||
UINT8 p = g_localplayers[i];
|
||||
|
||||
if (p != (vote.strike_loser - players)
|
||||
&& p != (vote.strike_winner - players))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
y_vote_player *const player = &vote.players[i];
|
||||
y_vote_catcher *const catcher = &player->catcher;
|
||||
|
||||
player->selection = the_only_level;
|
||||
catcher->action = CATCHER_FG_LOWER;
|
||||
|
||||
catcher->x = catcher->destX = SELECTION_X + (SELECTION_SPACING_W * player->selection);
|
||||
catcher->y = CATCHER_OFFSCREEN;
|
||||
catcher->destY = SELECTION_Y - SELECTION_HOP;
|
||||
catcher->spr = 0;
|
||||
catcher->level = VOTE_NOT_PICKED;
|
||||
|
||||
S_StartSound(NULL, sfx_kc37);
|
||||
}
|
||||
}
|
||||
|
||||
vote.selectFinalize++;
|
||||
}
|
||||
|
||||
if (vote.selectFinalize >= SELECT_DELAY_TIME)
|
||||
{
|
||||
if (vote.pickFinalize < PICK_DELAY_TIME)
|
||||
{
|
||||
vote.pickFinalize++;
|
||||
}
|
||||
else if (vote.endtic == -1)
|
||||
{
|
||||
vote.notYetPicked = false; /* don't pick vote twice */
|
||||
|
||||
if (server)
|
||||
{
|
||||
D_PickVote( the_only_level );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vote.timer == 0)
|
||||
{
|
||||
if (vote.strikeFinalize < STRIKE_DELAY_TIME)
|
||||
{
|
||||
vote.strikeFinalize++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vote.strikeFinalize = 0;
|
||||
}
|
||||
|
||||
if (vote.strikeFinalize >= STRIKE_DELAY_TIME)
|
||||
{
|
||||
// We didn't get their timeout strike net command.
|
||||
// Maybe they hacked their exe, or connection was
|
||||
// interrupted, or some other issue.
|
||||
|
||||
// Let's just strike a random stage for them.
|
||||
if (server && vote.strike_time_out == false)
|
||||
{
|
||||
INT32 rng = M_RandomKey(VOTE_NUM_LEVELS);
|
||||
for (i = 0; i < VOTE_NUM_LEVELS; i++)
|
||||
{
|
||||
if (g_votes_striked[i] == false)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
rng++;
|
||||
if (rng >= VOTE_NUM_LEVELS)
|
||||
{
|
||||
rng = 0;
|
||||
}
|
||||
}
|
||||
|
||||
D_ModifyClientVote((vote.strike_turn == true) ? VOTE_TIMEOUT_WINNER : VOTE_TIMEOUT_LOSER, rng);
|
||||
}
|
||||
|
||||
vote.strike_time_out = true;
|
||||
}
|
||||
else if (vote.timer > 0)
|
||||
{
|
||||
vote.timer--;
|
||||
vote.selectFinalize = 0;
|
||||
vote.pickFinalize = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Y_TickVoteSelection(void)
|
||||
{
|
||||
boolean everyone_voted = true;/* the default condition */
|
||||
|
|
@ -1463,6 +1871,22 @@ static void Y_TickVoteSelection(void)
|
|||
// Time's up, send our vote ASAP.
|
||||
if (vote.players[i].sentTimeOutVote == false)
|
||||
{
|
||||
// Move off of striked stages for the timeout vote.
|
||||
INT32 j;
|
||||
for (j = 0; j < VOTE_NUM_LEVELS; j++)
|
||||
{
|
||||
if (g_votes_striked[vote.players[i].selection] == false)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
vote.players[i].selection++;
|
||||
if (vote.players[i].selection >= VOTE_NUM_LEVELS)
|
||||
{
|
||||
vote.players[i].selection = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Y_PlayerSendVote(i);
|
||||
vote.players[i].sentTimeOutVote = true;
|
||||
vote.players[i].delay = NEWTICRATE/7;
|
||||
|
|
@ -1514,12 +1938,27 @@ static void Y_TickVoteSelection(void)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (players[i].bot == true && g_votes[i] == VOTE_NOT_PICKED)
|
||||
if (server && players[i].bot == true && Y_PlayerIDCanVoteRightNow(i) == true && g_votes[i] == VOTE_NOT_PICKED)
|
||||
{
|
||||
if (( M_RandomFixed() % 100 ) == 0)
|
||||
{
|
||||
// bots vote randomly
|
||||
D_ModifyClientVote(i, M_RandomKey(VOTE_NUM_LEVELS));
|
||||
INT32 rng = M_RandomKey(VOTE_NUM_LEVELS);
|
||||
for (i = 0; i < VOTE_NUM_LEVELS; i++)
|
||||
{
|
||||
if (g_votes_striked[i] == false)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
rng++;
|
||||
if (rng >= VOTE_NUM_LEVELS)
|
||||
{
|
||||
rng = 0;
|
||||
}
|
||||
}
|
||||
|
||||
D_ModifyClientVote(i, rng);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1529,6 +1968,13 @@ static void Y_TickVoteSelection(void)
|
|||
}
|
||||
}
|
||||
|
||||
if (vote.stage_striking == true)
|
||||
{
|
||||
// Use the same selection logic, otherwise use separate ending logic.
|
||||
Y_TickVoteStageStrike();
|
||||
return;
|
||||
}
|
||||
|
||||
if (everyone_voted == true)
|
||||
{
|
||||
vote.timer = 0;
|
||||
|
|
@ -1649,6 +2095,7 @@ static void Y_InitVoteDrawing(void)
|
|||
INT32 i = 0, j = 0;
|
||||
|
||||
vote_draw.ruby_icon = W_CachePatchName("RUBYICON", PU_STATIC);
|
||||
vote_draw.strike_icon = W_CachePatchName("K_NOBLNS", PU_STATIC);
|
||||
|
||||
for (i = 0; i < PLANET_FRAMES; i++)
|
||||
{
|
||||
|
|
@ -1736,6 +2183,103 @@ static void Y_InitVoteDrawing(void)
|
|||
vote_draw.selectTransition = FRACUNIT;
|
||||
}
|
||||
|
||||
static boolean Y_DetermineStageStrike(void)
|
||||
{
|
||||
player_t *a = NULL;
|
||||
player_t *b = NULL;
|
||||
|
||||
UINT8 num_voters = 0;
|
||||
|
||||
INT32 i;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (Y_PlayerIDCanVote(i) == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
num_voters++;
|
||||
|
||||
// Just set the pointers for now, sort them later.
|
||||
if (a == NULL)
|
||||
{
|
||||
a = &players[i];
|
||||
}
|
||||
else if (b == NULL)
|
||||
{
|
||||
b = &players[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Too many players
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_voters != 2 || a == NULL || b == NULL)
|
||||
{
|
||||
// Requires exactly 2 of them.
|
||||
return false;
|
||||
}
|
||||
|
||||
UINT32 score_a = 0;
|
||||
UINT32 score_b = 0;
|
||||
|
||||
intertype_t scoring_type = Y_GetIntermissionType();
|
||||
switch (scoring_type)
|
||||
{
|
||||
case int_time:
|
||||
{
|
||||
score_a = UINT32_MAX - a->realtime;
|
||||
score_b = UINT32_MAX - b->realtime;
|
||||
break;
|
||||
}
|
||||
case int_score:
|
||||
{
|
||||
score_a = a->score;
|
||||
score_b = b->realtime;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// Invalid, exit now.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (a->pflags & PF_NOCONTEST)
|
||||
{
|
||||
score_a = 0;
|
||||
}
|
||||
|
||||
if (b->pflags & PF_NOCONTEST)
|
||||
{
|
||||
score_b = 0;
|
||||
}
|
||||
|
||||
if (score_a == score_b)
|
||||
{
|
||||
// TODO: should be a coin flip, but how
|
||||
// should the RNG for this be handled?
|
||||
score_a++;
|
||||
}
|
||||
|
||||
if (score_a > score_b)
|
||||
{
|
||||
vote.strike_loser = b;
|
||||
vote.strike_winner = a;
|
||||
}
|
||||
else
|
||||
{
|
||||
vote.strike_loser = a;
|
||||
vote.strike_winner = b;
|
||||
}
|
||||
|
||||
vote.stage_striking = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Y_StartVote(void)
|
||||
{
|
||||
INT32 i = 0;
|
||||
|
|
@ -1749,12 +2293,6 @@ void Y_StartVote(void)
|
|||
|
||||
vote.tic = vote.endtic = -1;
|
||||
|
||||
#ifdef VOTE_TIME_WAIT_FOR_VOTE
|
||||
vote.timer = -1; // Timer is not set until the first vote is added
|
||||
#else
|
||||
vote.timer = cv_votetime.value * TICRATE;
|
||||
#endif
|
||||
|
||||
g_pickedVote = VOTE_NOT_PICKED;
|
||||
vote.notYetPicked = true;
|
||||
|
||||
|
|
@ -1782,6 +2320,19 @@ void Y_StartVote(void)
|
|||
catcher->player = i;
|
||||
}
|
||||
|
||||
for (i = 0; i < VOTE_NUM_LEVELS; i++)
|
||||
{
|
||||
g_votes_striked[i] = false;
|
||||
}
|
||||
|
||||
Y_DetermineStageStrike();
|
||||
|
||||
#ifdef VOTE_TIME_WAIT_FOR_VOTE
|
||||
vote.timer = -1; // Timer is not set until the first vote is added
|
||||
#else
|
||||
Y_SetVoteTimer();
|
||||
#endif
|
||||
|
||||
Y_InitVoteDrawing();
|
||||
|
||||
vote.loaded = true;
|
||||
|
|
@ -1803,6 +2354,7 @@ static void Y_UnloadVoteData(void)
|
|||
}
|
||||
|
||||
UNLOAD(vote_draw.ruby_icon);
|
||||
UNLOAD(vote_draw.strike_icon);
|
||||
|
||||
for (i = 0; i < PLANET_FRAMES; i++)
|
||||
{
|
||||
|
|
@ -1941,4 +2493,25 @@ void Y_SetupVoteFinish(SINT8 pick, SINT8 level, SINT8 anger)
|
|||
vote.timer = -1;
|
||||
vote.selectFinalize = SELECT_DELAY_TIME;
|
||||
vote.pickFinalize = PICK_DELAY_TIME;
|
||||
vote.strikeFinalize = STRIKE_DELAY_TIME;
|
||||
}
|
||||
|
||||
//
|
||||
// Y_VoteContext
|
||||
//
|
||||
|
||||
enum
|
||||
{
|
||||
VOTE_CTX_NORMAL = 0,
|
||||
VOTE_CTX_DUEL,
|
||||
};
|
||||
|
||||
UINT8 Y_VoteContext(void)
|
||||
{
|
||||
if (vote.stage_striking == true)
|
||||
{
|
||||
return VOTE_CTX_DUEL;
|
||||
}
|
||||
|
||||
return VOTE_CTX_NORMAL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,9 +19,6 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define VOTE_NUM_LEVELS (4)
|
||||
#define VOTE_NOT_PICKED (-1)
|
||||
|
||||
#define VOTE_MOD_ENCORE (0x01)
|
||||
|
||||
boolean Y_PlayerIDCanVote(const UINT8 playerId);
|
||||
|
|
@ -32,6 +29,7 @@ void Y_VoteTicker(void);
|
|||
void Y_StartVote(void);
|
||||
void Y_EndVote(void);
|
||||
void Y_SetupVoteFinish(SINT8 pick, SINT8 level, SINT8 anger);
|
||||
UINT8 Y_VoteContext(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ enum mobj_e {
|
|||
mobj_reappear,
|
||||
mobj_punt_ref,
|
||||
mobj_owner,
|
||||
mobj_relinkplayer,
|
||||
};
|
||||
|
||||
static const char *const mobj_opt[] = {
|
||||
|
|
@ -201,6 +202,7 @@ static const char *const mobj_opt[] = {
|
|||
"reappear",
|
||||
"punt_ref",
|
||||
"owner",
|
||||
"relinkplayer",
|
||||
NULL};
|
||||
|
||||
#define UNIMPLEMENTED luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", mobj_opt[field])
|
||||
|
|
@ -519,6 +521,9 @@ static int mobj_get(lua_State *L)
|
|||
}
|
||||
LUA_PushUserdata(L, mo->owner, META_MOBJ);
|
||||
break;
|
||||
case mobj_relinkplayer:
|
||||
lua_pushinteger(L, mo->relinkplayer);
|
||||
break;
|
||||
default: // extra custom variables in Lua memory
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
|
||||
I_Assert(lua_istable(L, -1));
|
||||
|
|
@ -934,6 +939,9 @@ static int mobj_set(lua_State *L)
|
|||
P_SetTarget(&mo->owner, owner);
|
||||
}
|
||||
break;
|
||||
case mobj_relinkplayer:
|
||||
mo->relinkplayer = luaL_checkinteger(L, 3);
|
||||
break;
|
||||
default:
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
|
||||
I_Assert(lua_istable(L, -1));
|
||||
|
|
|
|||
|
|
@ -498,6 +498,14 @@ static int player_get(lua_State *L)
|
|||
lua_pushinteger(L, plr->sneakertimer);
|
||||
else if (fastcmp(field,"numsneakers"))
|
||||
lua_pushinteger(L, plr->numsneakers);
|
||||
else if (fastcmp(field,"panelsneakertimer"))
|
||||
lua_pushinteger(L, plr->panelsneakertimer);
|
||||
else if (fastcmp(field,"numpanelsneakers"))
|
||||
lua_pushinteger(L, plr->numpanelsneakers);
|
||||
else if (fastcmp(field,"weaksneakertimer"))
|
||||
lua_pushinteger(L, plr->weaksneakertimer);
|
||||
else if (fastcmp(field,"numweaksneakers"))
|
||||
lua_pushinteger(L, plr->numweaksneakers);
|
||||
else if (fastcmp(field,"floorboost"))
|
||||
lua_pushinteger(L, plr->floorboost);
|
||||
else if (fastcmp(field,"growshrinktimer"))
|
||||
|
|
@ -709,6 +717,8 @@ static int player_get(lua_State *L)
|
|||
lua_pushinteger(L, plr->checkskip);
|
||||
else if (fastcmp(field,"cheatchecknum"))
|
||||
lua_pushinteger(L, plr->cheatchecknum);
|
||||
else if (fastcmp(field,"duelscore"))
|
||||
lua_pushinteger(L, plr->duelscore);
|
||||
else if (fastcmp(field,"lastsidehit"))
|
||||
lua_pushinteger(L, plr->lastsidehit);
|
||||
else if (fastcmp(field,"lastlinehit"))
|
||||
|
|
@ -1312,6 +1322,8 @@ static int player_set(lua_State *L)
|
|||
plr->checkskip = (INT32)luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"cheatchecknum"))
|
||||
plr->cheatchecknum = (INT32)luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"duelscore"))
|
||||
plr->duelscore = (INT16)luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"lastsidehit"))
|
||||
plr->lastsidehit = (INT16)luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"lastlinehit"))
|
||||
|
|
|
|||
|
|
@ -1858,7 +1858,10 @@ boolean M_CheckCondition(condition_t *cn, player_t *player)
|
|||
&& (gamespeed != KARTSPEED_EASY)
|
||||
&& (player->tally.active == true)
|
||||
&& (player->tally.totalExp > 0) // Only true if not Time Attack
|
||||
&& (player->tally.exp >= player->tally.totalExp));
|
||||
&& (
|
||||
(player->tally.exp >= player->tally.totalExp)
|
||||
|| (K_InRaceDuel() && player->duelscore == DUELWINNINGSCORE)
|
||||
));
|
||||
case UCRP_FINISHALLPRISONS:
|
||||
return (battleprisons
|
||||
&& !(player->pflags & PF_NOCONTEST)
|
||||
|
|
|
|||
|
|
@ -92,6 +92,8 @@ typedef enum
|
|||
PR_ITEM_SPAWNER = PROLDDEMO, // Battle mode item spawners
|
||||
PR_TEAMS, // Teamplay shuffling
|
||||
|
||||
PR_NUISANCE, // Margin Boost HUD
|
||||
|
||||
PRNUMSYNCED,
|
||||
|
||||
PR_INTERPHUDRANDOM = PRNUMSYNCED, // Interpolation-accomodating HUD randomisation
|
||||
|
|
|
|||
|
|
@ -1272,6 +1272,7 @@ void M_GonerTutorial(INT32 choice)
|
|||
|
||||
// Please also see M_LevelSelectInit as called in extras-1.c
|
||||
levellist.netgame = false;
|
||||
levellist.canqueue = false;
|
||||
levellist.levelsearch.checklocked = true;
|
||||
levellist.levelsearch.grandprix = false;
|
||||
levellist.levelsearch.timeattack = false;
|
||||
|
|
|
|||
|
|
@ -52,6 +52,17 @@ menuitem_t OPTIONS_Gameplay[] =
|
|||
NULL, {.cvar = &cv_kartbumpers}, 0, 0},
|
||||
|
||||
|
||||
|
||||
{IT_HEADER, "Duel...", NULL,
|
||||
NULL, {NULL}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CVAR, "Duel Time Limit", "How long it takes for Margin Boost to kick in (seconds).",
|
||||
NULL, {.cvar = &cv_dueltimelimit}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CVAR, "Duel Score Limit", "How many points a player must be ahead to win a Duel.",
|
||||
NULL, {.cvar = &cv_duelscorelimit}, 0, 0},
|
||||
|
||||
|
||||
{IT_SPACE | IT_DYBIGSPACE, NULL, NULL,
|
||||
NULL, {NULL}, 0, 0},
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ menuitem_t OPTIONS_Server[] =
|
|||
{IT_STRING | IT_CVAR, "CPU Level", "Bots can fill unused slots. How strong should they be?",
|
||||
NULL, {.cvar = &cv_kartbot}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CVAR, "Use PWR.LV", "Should players should be rated on their performance?",
|
||||
{IT_STRING | IT_CVAR, "Use Mobiums", "Should players should be rated on their performance?",
|
||||
NULL, {.cvar = &cv_kartusepwrlv}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CVAR, "Antigrief Timer (seconds)", "How long can players stop progressing before they're removed?",
|
||||
|
|
|
|||
|
|
@ -72,6 +72,24 @@ static boolean input_routine(INT32)
|
|||
return false;
|
||||
}
|
||||
|
||||
static void init_routine(void)
|
||||
{
|
||||
if (!netgame)
|
||||
{
|
||||
S_SoundInputSetEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean quit_routine(void)
|
||||
{
|
||||
if (!netgame)
|
||||
{
|
||||
S_SoundInputSetEnabled(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
menu_t OPTIONS_VoiceDef = {
|
||||
sizeof (OPTIONS_Voice) / sizeof (menuitem_t),
|
||||
&OPTIONS_MainDef,
|
||||
|
|
@ -85,7 +103,7 @@ menu_t OPTIONS_VoiceDef = {
|
|||
draw_routine,
|
||||
M_DrawOptionsCogs,
|
||||
tick_routine,
|
||||
NULL,
|
||||
NULL,
|
||||
init_routine,
|
||||
quit_routine,
|
||||
input_routine,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -614,34 +614,19 @@ void M_StartTimeAttack(INT32 choice)
|
|||
{
|
||||
modeattacking |= ATTACKING_SPB;
|
||||
}
|
||||
|
||||
if (gamestate == GS_MENU)
|
||||
{
|
||||
encoremode = true; // guarantees short wipe
|
||||
}
|
||||
|
||||
modeprefix = "spb-";
|
||||
}
|
||||
|
||||
// DON'T SOFTLOCK
|
||||
CON_ToggleOff();
|
||||
|
||||
// Still need to reset devmode
|
||||
cht_debug = 0;
|
||||
|
||||
if (demo.playback)
|
||||
G_StopDemo();
|
||||
|
||||
splitscreen = 0;
|
||||
SplitScreen_OnChange();
|
||||
|
||||
S_StartSound(NULL, sfx_s3k63);
|
||||
|
||||
paused = false;
|
||||
|
||||
S_StopMusicCredit();
|
||||
|
||||
// Early fadeout to let the sound finish playing
|
||||
F_WipeStartScreen();
|
||||
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
|
||||
F_WipeEndScreen();
|
||||
F_RunWipe(wipe_level_toblack, wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
|
||||
|
||||
SV_StartSinglePlayerServer(levellist.newgametype, false);
|
||||
M_MenuToLevelPreamble(0, (gamestate != GS_MENU || cv_dummyspbattack.value == 1));
|
||||
|
||||
gpath = va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s",
|
||||
srb2home, timeattackfolder);
|
||||
|
|
@ -659,8 +644,17 @@ void M_StartTimeAttack(INT32 choice)
|
|||
|
||||
restoreMenu = &PLAY_TimeAttackDef;
|
||||
|
||||
D_MapChange(
|
||||
levellist.choosemap+1,
|
||||
levellist.newgametype,
|
||||
(cv_dummyspbattack.value == 1),
|
||||
true,
|
||||
1,
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
M_ClearMenus(true);
|
||||
D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_dummyspbattack.value == 1), 1, 1, false, false);
|
||||
|
||||
G_UpdateTimeStickerMedals(levellist.choosemap, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ void M_MPSetupNetgameMapSelect(INT32 choice)
|
|||
|
||||
// Yep, we'll be starting a netgame.
|
||||
levellist.netgame = true;
|
||||
levellist.canqueue = true;
|
||||
// Make sure we reset those
|
||||
levellist.levelsearch.timeattack = false;
|
||||
levellist.levelsearch.checklocked = true;
|
||||
|
|
|
|||
|
|
@ -57,26 +57,7 @@ static void M_StartCup(UINT8 entry)
|
|||
entry = UINT8_MAX;
|
||||
}
|
||||
|
||||
S_StartSound(NULL, sfx_s3k63);
|
||||
|
||||
paused = false;
|
||||
|
||||
S_StopMusicCredit();
|
||||
|
||||
// Early fadeout to let the sound finish playing
|
||||
F_WipeStartScreen();
|
||||
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
|
||||
F_WipeEndScreen();
|
||||
F_RunWipe(wipe_level_toblack, wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
|
||||
|
||||
if (cv_maxconnections.value < ssplayers+1)
|
||||
CV_SetValue(&cv_maxconnections, ssplayers+1);
|
||||
|
||||
if (splitscreen != ssplayers)
|
||||
{
|
||||
splitscreen = ssplayers;
|
||||
SplitScreen_OnChange();
|
||||
}
|
||||
M_MenuToLevelPreamble(ssplayers, false);
|
||||
|
||||
if (entry == UINT8_MAX)
|
||||
{
|
||||
|
|
@ -135,10 +116,6 @@ static void M_StartCup(UINT8 entry)
|
|||
}
|
||||
}
|
||||
|
||||
paused = false;
|
||||
|
||||
SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame);
|
||||
|
||||
M_ClearMenus(true);
|
||||
|
||||
restoreMenu = &PLAY_CupSelectDef;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include "../../v_video.h"
|
||||
#include "../../g_game.h" // G_GetBackupCupData
|
||||
#include "../../p_saveg.h" // cupsavedata
|
||||
#include "../../d_netcmd.h" // Handle_MapQueueSend
|
||||
|
||||
cupheader_t dummy_lostandfound;
|
||||
|
||||
|
|
@ -657,14 +658,22 @@ void M_LevelSelectInit(INT32 choice)
|
|||
case 0:
|
||||
levellist.levelsearch.grandprix = false;
|
||||
levellist.levelsearch.timeattack = false;
|
||||
levellist.canqueue = true;
|
||||
|
||||
CV_StealthSet(&cv_kartbot, cv_dummymatchbots.string);
|
||||
CV_StealthSet(&cv_kartencore, (cv_dummygpencore.value == 1) ? "On" : "Auto");
|
||||
CV_StealthSet(&cv_kartspeed, (cv_dummykartspeed.value == KARTSPEED_NORMAL) ? "Auto Gear" : cv_dummykartspeed.string);
|
||||
|
||||
break;
|
||||
case 1:
|
||||
levellist.levelsearch.grandprix = false;
|
||||
levellist.levelsearch.timeattack = true;
|
||||
levellist.canqueue = false;
|
||||
break;
|
||||
case 2:
|
||||
levellist.levelsearch.grandprix = true;
|
||||
levellist.levelsearch.timeattack = false;
|
||||
levellist.canqueue = false;
|
||||
break;
|
||||
default:
|
||||
CONS_Alert(CONS_WARNING, "Bad level select init\n");
|
||||
|
|
@ -676,10 +685,14 @@ void M_LevelSelectInit(INT32 choice)
|
|||
gt = menugametype;
|
||||
}
|
||||
|
||||
if (levellist.levelsearch.timeattack && gt == GT_RACE && skins[R_SkinAvailableEx(cv_skin[0].string, false)].flags & SF_HIVOLT)
|
||||
if (levellist.levelsearch.timeattack && gt == GT_RACE)
|
||||
{
|
||||
M_StartMessage("A long-forgotten power...", "You are using a \x82prototype engine\x80.\nRecords will not be saved.", NULL, MM_NOTHING, NULL, NULL);
|
||||
S_StartSound(NULL, sfx_s3k81);
|
||||
const INT32 skinid = R_SkinAvailableEx(cv_skin[0].string, false);
|
||||
if (skinid >= 0 && (skins[skinid].flags & SF_HIVOLT))
|
||||
{
|
||||
M_StartMessage("A long-forgotten power...", "You are using a \x82prototype engine\x80.\nRecords will not be saved.", NULL, MM_NOTHING, NULL, NULL);
|
||||
S_StartSound(NULL, sfx_s3k81);
|
||||
}
|
||||
}
|
||||
|
||||
if (!M_LevelListFromGametype(gt))
|
||||
|
|
@ -689,7 +702,41 @@ void M_LevelSelectInit(INT32 choice)
|
|||
}
|
||||
}
|
||||
|
||||
void M_LevelSelected(INT16 add, boolean menuupdate)
|
||||
void M_MenuToLevelPreamble(UINT8 ssplayers, boolean nowipe)
|
||||
{
|
||||
cht_debug = 0;
|
||||
|
||||
if (demo.playback)
|
||||
G_StopDemo();
|
||||
|
||||
if (cv_maxconnections.value < ssplayers+1)
|
||||
CV_SetValue(&cv_maxconnections, ssplayers+1);
|
||||
|
||||
if (splitscreen != ssplayers)
|
||||
{
|
||||
splitscreen = ssplayers;
|
||||
SplitScreen_OnChange();
|
||||
}
|
||||
|
||||
paused = false;
|
||||
|
||||
S_StopMusicCredit();
|
||||
|
||||
if (!nowipe)
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3k63);
|
||||
|
||||
// Early fadeout to let the sound finish playing
|
||||
F_WipeStartScreen();
|
||||
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
|
||||
F_WipeEndScreen();
|
||||
F_RunWipe(wipe_level_toblack, wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
|
||||
}
|
||||
|
||||
SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame);
|
||||
}
|
||||
|
||||
INT16 M_LevelFromScrolledList(INT16 add)
|
||||
{
|
||||
UINT8 i = 0;
|
||||
INT16 map = M_GetFirstLevelInList(&i, &levellist.levelsearch);
|
||||
|
|
@ -706,6 +753,13 @@ void M_LevelSelected(INT16 add, boolean menuupdate)
|
|||
add--;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void M_LevelSelected(INT16 add, boolean menuupdate)
|
||||
{
|
||||
INT16 map = M_LevelFromScrolledList(add);
|
||||
|
||||
if (map >= nummapheaders)
|
||||
{
|
||||
// This shouldn't happen
|
||||
|
|
@ -733,75 +787,116 @@ void M_LevelSelected(INT16 add, boolean menuupdate)
|
|||
{
|
||||
if (gamestate == GS_MENU)
|
||||
{
|
||||
UINT8 ssplayers = levellist.levelsearch.tutorial ? 0 : cv_splitplayers.value-1;
|
||||
|
||||
netgame = false;
|
||||
multiplayer = true;
|
||||
|
||||
strlcpy(connectedservername, cv_servername.string, MAXSERVERNAME);
|
||||
M_MenuToLevelPreamble(
|
||||
(levellist.levelsearch.tutorial
|
||||
? 0
|
||||
: cv_splitplayers.value-1
|
||||
),
|
||||
(
|
||||
(cv_kartencore.value == 1)
|
||||
&& (gametypes[levellist.newgametype]->rules & GTR_ENCORE)
|
||||
)
|
||||
);
|
||||
|
||||
// Still need to reset devmode
|
||||
cht_debug = 0;
|
||||
|
||||
if (demo.playback)
|
||||
G_StopDemo();
|
||||
|
||||
/*if (levellist.choosemap == 0)
|
||||
levellist.choosemap = G_RandMap(G_TOLFlag(levellist.newgametype), -1, 0, 0, false, NULL);*/
|
||||
|
||||
if (cv_maxconnections.value < ssplayers+1)
|
||||
CV_SetValue(&cv_maxconnections, ssplayers+1);
|
||||
|
||||
if (splitscreen != ssplayers)
|
||||
{
|
||||
splitscreen = ssplayers;
|
||||
SplitScreen_OnChange();
|
||||
}
|
||||
|
||||
S_StartSound(NULL, sfx_s3k63);
|
||||
|
||||
paused = false;
|
||||
|
||||
S_StopMusicCredit();
|
||||
|
||||
// Early fadeout to let the sound finish playing
|
||||
F_WipeStartScreen();
|
||||
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
|
||||
F_WipeEndScreen();
|
||||
F_RunWipe(wipe_level_toblack, wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
|
||||
|
||||
SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame);
|
||||
|
||||
if (!levellist.netgame)
|
||||
CV_StealthSet(&cv_kartbot, cv_dummymatchbots.string);
|
||||
|
||||
CV_StealthSet(&cv_kartencore, (cv_dummygpencore.value == 1) ? "On" : "Auto");
|
||||
CV_StealthSet(&cv_kartspeed, (cv_dummykartspeed.value == KARTSPEED_NORMAL) ? "Auto Gear" : cv_dummykartspeed.string);
|
||||
|
||||
D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false);
|
||||
|
||||
if (levellist.netgame == true)
|
||||
{
|
||||
restoreMenu = &PLAY_MP_OptSelectDef;
|
||||
}
|
||||
else /*if (!M_GameTrulyStarted() ||
|
||||
levellist.levelsearch.tutorial)*/
|
||||
{
|
||||
restoreMenu = currentMenu;
|
||||
}
|
||||
restoreMenu = (levellist.netgame)
|
||||
? &PLAY_MP_OptSelectDef
|
||||
: currentMenu;
|
||||
|
||||
restorelevellist = levellist;
|
||||
}
|
||||
else
|
||||
{
|
||||
// directly do the map change
|
||||
D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false);
|
||||
}
|
||||
|
||||
D_MapChange(
|
||||
levellist.choosemap+1,
|
||||
levellist.newgametype,
|
||||
(cv_kartencore.value == 1),
|
||||
true,
|
||||
1,
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
M_ClearMenus(true);
|
||||
}
|
||||
}
|
||||
|
||||
static void M_MenuQueueStopSend(INT32 ch)
|
||||
{
|
||||
(void)ch;
|
||||
|
||||
memset(&menuqueue, 0, sizeof(struct menuqueue));
|
||||
}
|
||||
|
||||
static void M_MenuQueueSelectedLocal(void)
|
||||
{
|
||||
UINT8 i = 0;
|
||||
|
||||
for (; i < menuqueue.size; i++)
|
||||
{
|
||||
G_MapIntoRoundQueue(
|
||||
menuqueue.entries[i].mapnum,
|
||||
menuqueue.entries[i].gametype,
|
||||
menuqueue.entries[i].encore,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
roundqueue.netcommunicate = true;
|
||||
|
||||
if (gamestate == GS_MENU)
|
||||
{
|
||||
levellist.choosemap = roundqueue.entries[0].mapnum;
|
||||
|
||||
multiplayer = true;
|
||||
|
||||
M_MenuToLevelPreamble(
|
||||
cv_splitplayers.value-1,
|
||||
(
|
||||
roundqueue.entries[0].encore
|
||||
&& (gametypes[roundqueue.entries[0].gametype]->rules & GTR_ENCORE)
|
||||
)
|
||||
);
|
||||
|
||||
restoreMenu = (levellist.netgame)
|
||||
? &PLAY_MP_OptSelectDef
|
||||
: currentMenu;
|
||||
|
||||
restorelevellist = levellist;
|
||||
|
||||
roundqueue.position = roundqueue.roundnum = 1;
|
||||
|
||||
D_MapChange(
|
||||
roundqueue.entries[0].mapnum + 1,
|
||||
roundqueue.entries[0].gametype,
|
||||
roundqueue.entries[0].encore,
|
||||
true,
|
||||
1,
|
||||
false,
|
||||
roundqueue.entries[0].rankrestricted
|
||||
);
|
||||
|
||||
M_MenuQueueStopSend(MA_NONE);
|
||||
|
||||
M_ClearMenus(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gamestate == GS_LEVEL && roundqueue.position == 0)
|
||||
{
|
||||
menuqueue.sending = UINT8_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
S_StartSound(NULL, sfx_chchng);
|
||||
|
||||
M_MenuQueueStopSend(MA_NONE);
|
||||
|
||||
M_ClearMenus(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean M_LevelSelectCupSwitch(boolean next, boolean skipones)
|
||||
{
|
||||
levelsearch_t templevelsearch = levellist.levelsearch;
|
||||
|
|
@ -883,6 +978,44 @@ boolean M_LevelSelectCupSwitch(boolean next, boolean skipones)
|
|||
}
|
||||
}
|
||||
|
||||
static void M_MenuQueueResponse(INT32 ch)
|
||||
{
|
||||
M_ClearMenus(false);
|
||||
|
||||
if (ch != MA_YES)
|
||||
return;
|
||||
|
||||
if (!(server || (IsPlayerAdmin(consoleplayer))))
|
||||
return;
|
||||
|
||||
if (!(gamestate == GS_LEVEL && roundqueue.position == 0))
|
||||
return;
|
||||
|
||||
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
static void M_ClearQueueResponse(INT32 ch)
|
||||
{
|
||||
if (ch != MA_YES)
|
||||
return;
|
||||
|
||||
if (!(server || (IsPlayerAdmin(consoleplayer))))
|
||||
return;
|
||||
|
||||
S_StartSound(NULL, sfx_slip);
|
||||
|
||||
if (netgame)
|
||||
{
|
||||
if (roundqueue.size)
|
||||
{
|
||||
Handle_MapQueueSend(0, ROUNDQUEUE_CMD_CLEAR, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&roundqueue, 0, sizeof(struct roundqueue));
|
||||
}
|
||||
void M_LevelSelectHandler(INT32 choice)
|
||||
{
|
||||
const UINT8 pid = 0;
|
||||
|
|
@ -894,6 +1027,11 @@ void M_LevelSelectHandler(INT32 choice)
|
|||
return;
|
||||
}
|
||||
|
||||
if (menuqueue.sending)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (menucmd[pid].dpad_ud > 0)
|
||||
{
|
||||
levellist.cursor++;
|
||||
|
|
@ -926,10 +1064,96 @@ void M_LevelSelectHandler(INT32 choice)
|
|||
|
||||
M_LevelSelectScrollDest();
|
||||
|
||||
if (M_MenuConfirmPressed(pid) /*|| M_MenuButtonPressed(pid, MBT_START)*/)
|
||||
if (M_MenuConfirmPressed(pid))
|
||||
{
|
||||
// Starting immediately OR importing queue
|
||||
|
||||
M_SetMenuDelay(pid);
|
||||
M_LevelSelected(levellist.cursor, true);
|
||||
|
||||
while ((menuqueue.size + roundqueue.size) > ROUNDQUEUE_MAX)
|
||||
menuqueue.size--;
|
||||
|
||||
if (!levellist.canqueue || !menuqueue.size)
|
||||
{
|
||||
M_LevelSelected(levellist.cursor, true);
|
||||
}
|
||||
else if (netgame)
|
||||
{
|
||||
menuqueue.anchor = roundqueue.size;
|
||||
menuqueue.sending = 1;
|
||||
|
||||
M_StartMessage("Queueing Rounds",
|
||||
va(M_GetText(
|
||||
"Attempting to send %d Round%s...\n"
|
||||
"\n"
|
||||
"If this is taking longer than you\n"
|
||||
"expect, exit out of this message.\n"
|
||||
), menuqueue.size, (menuqueue.size == 1 ? "" : "s")
|
||||
), &M_MenuQueueStopSend, MM_NOTHING,
|
||||
NULL,
|
||||
"This is taking too long..."
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
M_MenuQueueSelectedLocal();
|
||||
}
|
||||
}
|
||||
else if (levellist.canqueue && M_MenuButtonPressed(pid, MBT_Z))
|
||||
{
|
||||
// Adding to queue
|
||||
|
||||
INT16 map = NEXTMAP_INVALID;
|
||||
|
||||
M_SetMenuDelay(pid);
|
||||
|
||||
if ((roundqueue.size + menuqueue.size) < ROUNDQUEUE_MAX)
|
||||
{
|
||||
map = M_LevelFromScrolledList(levellist.cursor);
|
||||
}
|
||||
|
||||
if (map < nummapheaders)
|
||||
{
|
||||
memset(menuqueue.entries+menuqueue.size, 0, sizeof(roundentry_t));
|
||||
menuqueue.entries[menuqueue.size].mapnum = map;
|
||||
menuqueue.entries[menuqueue.size].gametype = levellist.newgametype;
|
||||
menuqueue.entries[menuqueue.size].encore = (cv_kartencore.value == 1);
|
||||
|
||||
menuqueue.size++;
|
||||
|
||||
S_StartSound(NULL, sfx_s3k4a);
|
||||
}
|
||||
else
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3kb2);
|
||||
}
|
||||
}
|
||||
else if (levellist.canqueue && M_MenuExtraPressed(pid))
|
||||
{
|
||||
while ((menuqueue.size + roundqueue.size) > ROUNDQUEUE_MAX)
|
||||
menuqueue.size--;
|
||||
|
||||
if (menuqueue.size)
|
||||
{
|
||||
S_StartSound(NULL, sfx_shldls);
|
||||
menuqueue.size--;
|
||||
}
|
||||
else if (roundqueue.size)
|
||||
{
|
||||
M_StartMessage("Queue Clearing",
|
||||
va(M_GetText(
|
||||
"There %s %d Round%s of play queued.\n"
|
||||
"\n"
|
||||
"Do you want to empty the queue?\n"
|
||||
),
|
||||
(roundqueue.size == 1 ? "is" : "are"),
|
||||
roundqueue.size,
|
||||
(roundqueue.size == 1 ? "" : "s")
|
||||
), &M_ClearQueueResponse, MM_YESNO,
|
||||
"Time to start fresh",
|
||||
"Not right now"
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (M_MenuBackPressed(pid))
|
||||
{
|
||||
|
|
@ -944,4 +1168,55 @@ void M_LevelSelectHandler(INT32 choice)
|
|||
|
||||
void M_LevelSelectTick(void)
|
||||
{
|
||||
if (!menuqueue.sending)
|
||||
return;
|
||||
|
||||
if ((menuqueue.sending <= menuqueue.size) // Sending
|
||||
&& (roundqueue.size >= menuqueue.anchor)) // Didn't get it wiped
|
||||
{
|
||||
if (!netgame)
|
||||
return;
|
||||
|
||||
const UINT8 idcount = (roundqueue.size - menuqueue.anchor);
|
||||
|
||||
if (idcount == menuqueue.sending-1)
|
||||
{
|
||||
Handle_MapQueueSend(
|
||||
menuqueue.entries[idcount].mapnum,
|
||||
menuqueue.entries[idcount].gametype,
|
||||
menuqueue.entries[idcount].encore
|
||||
);
|
||||
}
|
||||
if (idcount >= menuqueue.sending-1)
|
||||
{
|
||||
menuqueue.sending++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (menuqueue.size)
|
||||
{
|
||||
S_StartSound(NULL, sfx_chchng);
|
||||
|
||||
if (roundqueue.position || gamestate != GS_LEVEL)
|
||||
{
|
||||
M_StopMessage(MA_NONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
M_StartMessage("Round Queue",
|
||||
va(M_GetText(
|
||||
"You just queued %d Round%s of play.\n"
|
||||
"\n"
|
||||
"Do you want to skip the current\n"
|
||||
"course and start them immediately?\n"
|
||||
), menuqueue.size, (menuqueue.size == 1 ? "" : "s")
|
||||
), &M_MenuQueueResponse, MM_YESNO,
|
||||
"Let's get going!",
|
||||
"Not right now"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
M_MenuQueueStopSend(MA_NONE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,21 +39,21 @@ menuitem_t PAUSE_PlaybackMenu[] =
|
|||
{IT_CALL | IT_STRING, "Hide Menu", NULL, "M_PHIDE", {.routine = M_SelectableClearMenus}, 0, 0},
|
||||
|
||||
{IT_CALL | IT_STRING, "Restart", NULL, "M_PRSTRT", {.routine = M_PlaybackRewind}, 20, 0},
|
||||
{IT_CALL | IT_STRING, "Pause", NULL, "M_PPAUSE", {.routine = M_PlaybackPause}, 36, 0},
|
||||
{IT_CALL | IT_STRING, "Fast-Forward", NULL, "M_PFFWD", {.routine = M_PlaybackFastForward}, 52, 0},
|
||||
{IT_CALL | IT_STRING, "Restart", NULL, "M_PRSTRT", {.routine = M_PlaybackRewind}, 20, 0},
|
||||
{IT_CALL | IT_STRING, "Resume", NULL, "M_PRESUM", {.routine = M_PlaybackPause}, 36, 0},
|
||||
{IT_CALL | IT_STRING, "Advance Frame", NULL, "M_PFADV", {.routine = M_PlaybackAdvance}, 52, 0},
|
||||
{IT_CALL | IT_STRING, "Rewind 5 seconds", NULL, "M_PREW", {.routine = M_PlaybackRewind}, 36, 0},
|
||||
{IT_CALL | IT_STRING, "Pause", NULL, "M_PPAUSE", {.routine = M_PlaybackPause}, 52, 0},
|
||||
{IT_CALL | IT_STRING, "Fast-Forward", NULL, "M_PFFWD", {.routine = M_PlaybackFastForward}, 68, 0},
|
||||
{IT_CALL | IT_STRING, "Resume", NULL, "M_PRESUM", {.routine = M_PlaybackPause}, 52, 0},
|
||||
{IT_CALL | IT_STRING, "Advance Frame", NULL, "M_PFADV", {.routine = M_PlaybackAdvance}, 68, 0},
|
||||
|
||||
{IT_ARROWS | IT_STRING, "View Count", NULL, "M_PVIEWS", {.routine = M_PlaybackSetViews}, 72, 0},
|
||||
{IT_ARROWS | IT_STRING, "Viewpoint", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 88, 0},
|
||||
{IT_ARROWS | IT_STRING, "Viewpoint 2", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 104, 0},
|
||||
{IT_ARROWS | IT_STRING, "Viewpoint 3", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 120, 0},
|
||||
{IT_ARROWS | IT_STRING, "Viewpoint 4", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 136, 0},
|
||||
{IT_ARROWS | IT_STRING, "View Count", NULL, "M_PVIEWS", {.routine = M_PlaybackSetViews}, 88, 0},
|
||||
{IT_ARROWS | IT_STRING, "Viewpoint", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 104, 0},
|
||||
{IT_ARROWS | IT_STRING, "Viewpoint 2", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 120, 0},
|
||||
{IT_ARROWS | IT_STRING, "Viewpoint 3", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 136, 0},
|
||||
{IT_ARROWS | IT_STRING, "Viewpoint 4", NULL, "M_PNVIEW", {.routine = M_PlaybackAdjustView}, 152, 0},
|
||||
|
||||
{IT_CALL | IT_STRING, "Toggle Director", NULL, "UN_IC11A", {.routine = M_PlaybackToggleDirector}, 156, 0},
|
||||
{IT_CALL | IT_STRING, "Toggle Free Camera", NULL, "M_PVIEWS", {.routine = M_PlaybackToggleFreecam}, 172, 0},
|
||||
{IT_CALL | IT_STRING, "Stop Playback", NULL, "M_PEXIT", {.routine = M_PlaybackQuit}, 188, 0},
|
||||
{IT_CALL | IT_STRING, "Toggle Director", NULL, "UN_IC11A", {.routine = M_PlaybackToggleDirector}, 172, 0},
|
||||
{IT_CALL | IT_STRING, "Toggle Free Camera", NULL, "M_PVIEWS", {.routine = M_PlaybackToggleFreecam}, 188, 0},
|
||||
{IT_CALL | IT_STRING, "Stop Playback", NULL, "M_PEXIT", {.routine = M_PlaybackQuit}, 204, 0},
|
||||
};
|
||||
|
||||
menu_t PAUSE_PlaybackMenuDef = {
|
||||
|
|
@ -151,21 +151,21 @@ static void M_PlaybackTick(void)
|
|||
playback_last_menu_interaction_leveltime = leveltime - 6*TICRATE;
|
||||
|
||||
// Toggle items
|
||||
if (paused && !demo.rewinding)
|
||||
if (paused)
|
||||
{
|
||||
PAUSE_PlaybackMenu[playback_pause].status = PAUSE_PlaybackMenu[playback_fastforward].status = PAUSE_PlaybackMenu[playback_rewind].status = IT_DISABLED;
|
||||
PAUSE_PlaybackMenu[playback_resume].status = PAUSE_PlaybackMenu[playback_advanceframe].status = PAUSE_PlaybackMenu[playback_backframe].status = IT_CALL|IT_STRING;
|
||||
PAUSE_PlaybackMenu[playback_pause].status = PAUSE_PlaybackMenu[playback_fastforward].status = IT_DISABLED;
|
||||
PAUSE_PlaybackMenu[playback_resume].status = PAUSE_PlaybackMenu[playback_advanceframe].status = IT_CALL|IT_STRING;
|
||||
|
||||
if (itemOn >= playback_rewind && itemOn <= playback_fastforward)
|
||||
itemOn += playback_backframe - playback_rewind;
|
||||
if (itemOn >= playback_pause && itemOn <= playback_fastforward)
|
||||
itemOn += playback_resume - playback_pause;
|
||||
}
|
||||
else
|
||||
{
|
||||
PAUSE_PlaybackMenu[playback_pause].status = PAUSE_PlaybackMenu[playback_fastforward].status = PAUSE_PlaybackMenu[playback_rewind].status = IT_CALL|IT_STRING;
|
||||
PAUSE_PlaybackMenu[playback_resume].status = PAUSE_PlaybackMenu[playback_advanceframe].status = PAUSE_PlaybackMenu[playback_backframe].status = IT_DISABLED;
|
||||
PAUSE_PlaybackMenu[playback_pause].status = PAUSE_PlaybackMenu[playback_fastforward].status = IT_CALL|IT_STRING;
|
||||
PAUSE_PlaybackMenu[playback_resume].status = PAUSE_PlaybackMenu[playback_advanceframe].status = IT_DISABLED;
|
||||
|
||||
if (itemOn >= playback_backframe && itemOn <= playback_advanceframe)
|
||||
itemOn -= playback_backframe - playback_rewind;
|
||||
if (itemOn >= playback_resume && itemOn <= playback_advanceframe)
|
||||
itemOn -= playback_resume - playback_pause;
|
||||
}
|
||||
|
||||
if (modeattacking)
|
||||
|
|
@ -173,10 +173,10 @@ static void M_PlaybackTick(void)
|
|||
for (i = playback_viewcount; i <= playback_director; i++)
|
||||
PAUSE_PlaybackMenu[i].status = IT_DISABLED;
|
||||
|
||||
PAUSE_PlaybackMenu[playback_freecam].mvar1 = 72;
|
||||
PAUSE_PlaybackMenu[playback_quit].mvar1 = 88;
|
||||
PAUSE_PlaybackMenu[playback_freecam].mvar1 = 88;
|
||||
PAUSE_PlaybackMenu[playback_quit].mvar1 = 104;
|
||||
|
||||
currentMenu->x = BASEVIDWIDTH/2 - 52;
|
||||
currentMenu->x = BASEVIDWIDTH/2 - 60;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -189,11 +189,11 @@ static void M_PlaybackTick(void)
|
|||
for (i = r_splitscreen+1; i < 4; i++)
|
||||
PAUSE_PlaybackMenu[playback_view1+i].status = IT_DISABLED;
|
||||
|
||||
PAUSE_PlaybackMenu[playback_freecam].mvar1 = 172;
|
||||
PAUSE_PlaybackMenu[playback_quit].mvar1 = 188;
|
||||
PAUSE_PlaybackMenu[playback_freecam].mvar1 = 188;
|
||||
PAUSE_PlaybackMenu[playback_quit].mvar1 = 204;
|
||||
|
||||
//currentMenu->x = BASEVIDWIDTH/2 - 94;
|
||||
currentMenu->x = BASEVIDWIDTH/2 - 96;
|
||||
//currentMenu->x = BASEVIDWIDTH/2 - 102;
|
||||
currentMenu->x = BASEVIDWIDTH/2 - 104;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -204,34 +204,38 @@ void M_SetPlaybackMenuPointer(void)
|
|||
|
||||
void M_PlaybackRewind(INT32 choice)
|
||||
{
|
||||
#if 0
|
||||
static tic_t lastconfirmtime;
|
||||
const tic_t curleveltime = leveltime;
|
||||
|
||||
(void)choice;
|
||||
|
||||
if (!demo.rewinding)
|
||||
if (choice == playback_rewind)
|
||||
{
|
||||
if (paused)
|
||||
demo.simplerewind = (paused ? DEMO_REWIND_PAUSE : DEMO_REWIND_RESUME);
|
||||
menuactive = false;
|
||||
}
|
||||
|
||||
G_DoPlayDemo(NULL); // Restart the current demo
|
||||
|
||||
if (demo.simplerewind)
|
||||
{
|
||||
if (curleveltime > 5*TICRATE)
|
||||
{
|
||||
G_ConfirmRewind(leveltime-1);
|
||||
paused = true;
|
||||
S_PauseAudio();
|
||||
g_fast_forward = curleveltime - (5 * TICRATE);
|
||||
g_fast_forward_clock_stop = INFTICS; //I_GetTime() + 2 * TICRATE; -- maybe?
|
||||
}
|
||||
else
|
||||
demo.rewinding = paused = true;
|
||||
{
|
||||
if (demo.simplerewind == DEMO_REWIND_PAUSE)
|
||||
{
|
||||
paused = true;
|
||||
S_PauseAudio();
|
||||
}
|
||||
demo.simplerewind = DEMO_REWIND_OFF;
|
||||
}
|
||||
menuactive = true;
|
||||
}
|
||||
else if (lastconfirmtime + TICRATE/2 < I_GetTime())
|
||||
else
|
||||
{
|
||||
lastconfirmtime = I_GetTime();
|
||||
G_ConfirmRewind(leveltime);
|
||||
M_ClearMenus(true);
|
||||
}
|
||||
|
||||
CV_SetValue(&cv_playbackspeed, 1);
|
||||
#else
|
||||
(void)choice;
|
||||
G_DoPlayDemo(NULL); // Restart the current demo
|
||||
M_ClearMenus(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
void M_PlaybackPause(INT32 choice)
|
||||
|
|
@ -240,13 +244,7 @@ void M_PlaybackPause(INT32 choice)
|
|||
|
||||
paused = !paused;
|
||||
|
||||
if (demo.rewinding)
|
||||
{
|
||||
G_ConfirmRewind(leveltime);
|
||||
paused = true;
|
||||
S_PauseAudio();
|
||||
}
|
||||
else if (paused)
|
||||
if (paused)
|
||||
S_PauseAudio();
|
||||
else
|
||||
S_ResumeAudio();
|
||||
|
|
@ -258,12 +256,6 @@ void M_PlaybackFastForward(INT32 choice)
|
|||
{
|
||||
(void)choice;
|
||||
|
||||
if (demo.rewinding)
|
||||
{
|
||||
G_ConfirmRewind(leveltime);
|
||||
paused = false;
|
||||
S_ResumeAudio();
|
||||
}
|
||||
CV_SetValue(&cv_playbackspeed, cv_playbackspeed.value == 1 ? 4 : 1);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -495,7 +495,24 @@ struct CheckpointManager
|
|||
else // Checkpoint isn't in the list, find any associated tagged lines and make the pair
|
||||
{
|
||||
if (chk->linetag())
|
||||
lines_.try_emplace(chk->linetag(), tagged_lines(chk->linetag()));
|
||||
{
|
||||
auto lines = tagged_lines(chk->linetag());
|
||||
if (lines.empty() && gametype != GT_TUTORIAL)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "Checkpoint thing %d, has linetag %d, but no lines found. Please ensure all checkpoints have associated lines.\n", chk->spawnpoint - mapthings, chk->linetag());
|
||||
}
|
||||
else
|
||||
{
|
||||
lines_.try_emplace(chk->linetag(), lines);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gametype != GT_TUTORIAL)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "Checkpoint thing %d, has no linetag. Please ensure all checkpoint things have a linetag.\n", chk->spawnpoint - mapthings);
|
||||
}
|
||||
}
|
||||
list_.push_front(chk);
|
||||
count_ += 1; // Mobjlist can't have a count on it, so we keep it here
|
||||
}
|
||||
|
|
@ -567,6 +584,11 @@ void Obj_CheckpointThink(mobj_t* end)
|
|||
|
||||
void Obj_CrossCheckpoints(player_t* player, fixed_t old_x, fixed_t old_y)
|
||||
{
|
||||
if (player->exiting) // can't cross checkpoints when exiting
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LineOnDemand ray(old_x, old_y, player->mo->x, player->mo->y, player->mo->radius);
|
||||
|
||||
auto it = std::find_if(
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ struct Side : Graphic
|
|||
|
||||
struct Toucher : Mobj
|
||||
{
|
||||
bool boosting() const { return player && (player->sneakertimer || player->panelsneakertimer || K_PlayerCanPunt(player)); }
|
||||
bool boosting() const { return player && (player->sneakertimer || player->panelsneakertimer || player->weaksneakertimer || K_PlayerCanPunt(player)); }
|
||||
};
|
||||
|
||||
struct AnyBox : Graphic
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ struct Visual : Mobj
|
|||
}
|
||||
|
||||
move_origin(shield()->pos());
|
||||
scale(5 * shield()->follow()->scale() / 4);
|
||||
|
||||
renderflags = (renderflags & ~RF_DONTDRAW) |
|
||||
(shield()->state()->num() == S_INVISIBLE ? 0 : RF_DONTDRAW);
|
||||
|
|
|
|||
|
|
@ -599,7 +599,8 @@ hyudoro_patrol_hit_player
|
|||
|
||||
P_SetTarget(&hyudoro_target(hyu), master);
|
||||
|
||||
K_SpawnAmps(master->player, K_PvPAmpReward(20, master->player, player), toucher);
|
||||
if (master && !P_MobjWasRemoved(master))
|
||||
K_SpawnAmps(master->player, K_PvPAmpReward(20, master->player, player), toucher);
|
||||
|
||||
if (center)
|
||||
P_RemoveMobj(center);
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ struct Visual : Mobj
|
|||
}
|
||||
|
||||
move_origin(shield()->pos());
|
||||
scale(5 * shield()->follow()->scale() / 4);
|
||||
dispoffset = state()->num() == S_THNB1 ? -1 : 1;
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (K_TryPickMeUp(t1, t2))
|
||||
if (K_TryPickMeUp(t1, t2, false))
|
||||
return true;
|
||||
|
||||
if (t1->type == MT_GARDENTOP)
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ private:
|
|||
{
|
||||
const player_t* p = toucher->player;
|
||||
|
||||
if (p->sneakertimer || p->panelsneakertimer || p->invincibilitytimer || p->growshrinktimer > 0 || p->hyudorotimer)
|
||||
if (p->sneakertimer || p->panelsneakertimer || p->weaksneakertimer || p->invincibilitytimer || p->growshrinktimer > 0 || p->hyudorotimer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -542,58 +542,6 @@ void Obj_SLSTMaceMobjThink(mobj_t* mo)
|
|||
}
|
||||
}
|
||||
|
||||
#define BUMPER_STRENGTH (56)
|
||||
|
||||
void Obj_SSBumperTouchSpecial(mobj_t* special, mobj_t* toucher)
|
||||
{
|
||||
angle_t hang;
|
||||
angle_t vang;
|
||||
fixed_t str;
|
||||
int i;
|
||||
|
||||
hang = R_PointToAngle2(special->x, special->y, toucher->x, toucher->y);
|
||||
vang = 0;
|
||||
|
||||
if (P_IsObjectOnGround(toucher) == false)
|
||||
{
|
||||
vang = R_PointToAngle2(
|
||||
FixedHypot(special->x, special->y), special->z + (special->height >> 1),
|
||||
FixedHypot(toucher->x, toucher->y), toucher->z + (toucher->height >> 1)
|
||||
);
|
||||
}
|
||||
|
||||
str = (BUMPER_STRENGTH * special->scale) >> 1;
|
||||
|
||||
toucher->momx = FixedMul(FixedMul(str, FCOS(hang)), abs(FCOS(vang)));
|
||||
toucher->momy = FixedMul(FixedMul(str, FSIN(hang)), abs(FCOS(vang)));
|
||||
toucher->momz = FixedMul(str, FSIN(vang));
|
||||
|
||||
if (toucher->player)
|
||||
{
|
||||
if (toucher->player->tiregrease == 0)
|
||||
{
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
mobj_t *grease = P_SpawnMobjFromMobj(toucher, 0, 0, 0, MT_TIREGREASE);
|
||||
P_SetTarget(&grease->target, toucher);
|
||||
grease->angle = toucher->angle;
|
||||
grease->extravalue1 = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (toucher->player->tiregrease < 2*TICRATE) // greasetics
|
||||
{
|
||||
toucher->player->tiregrease = 2*TICRATE;
|
||||
}
|
||||
}
|
||||
|
||||
if (special->state != &states[special->info->seestate])
|
||||
{
|
||||
S_StartSound(special, special->info->deathsound);
|
||||
P_SetMobjState(special, special->info->seestate);
|
||||
}
|
||||
}
|
||||
|
||||
void Obj_SSBumperMobjSpawn(mobj_t* mo)
|
||||
{
|
||||
mo->shadowscale = FRACUNIT;
|
||||
|
|
|
|||
|
|
@ -895,7 +895,7 @@ static UINT8 GetUFODamage(mobj_t *inflictor, UINT8 damageType)
|
|||
{
|
||||
// Players deal damage relative to how many sneakers they used.
|
||||
targetdamaging = UFOD_BOOST;
|
||||
ret = 15 * max(1, inflictor->player->numsneakers + inflictor->player->numpanelsneakers);
|
||||
ret = 15 * max(1, inflictor->player->numsneakers + inflictor->player->numpanelsneakers + inflictor->player->numweaksneakers);
|
||||
break;
|
||||
}
|
||||
case MT_SPECIAL_UFO:
|
||||
|
|
@ -1056,6 +1056,8 @@ void Obj_PlayerUFOCollide(mobj_t *ufo, mobj_t *other)
|
|||
other->player->numsneakers = 0;
|
||||
other->player->panelsneakertimer = 0;
|
||||
other->player->numpanelsneakers = 0;
|
||||
other->player->weaksneakertimer = 0;
|
||||
other->player->numweaksneakers = 0;
|
||||
|
||||
// Copied from Obj_OrbinautThrown
|
||||
const ffloor_t *rover = P_IsObjectFlipped(other) ? other->ceilingrover : other->floorrover;
|
||||
|
|
|
|||
|
|
@ -321,6 +321,7 @@ void A_BlendEyePuyoHack(mobj_t *actor);
|
|||
void A_MakeSSCandle(mobj_t *actor);
|
||||
void A_HologramRandomTranslucency(mobj_t *actor);
|
||||
void A_SSChainShatter(mobj_t *actor);
|
||||
void A_GenericBumper(mobj_t *actor);
|
||||
|
||||
//for p_enemy.c
|
||||
|
||||
|
|
@ -12668,3 +12669,54 @@ void A_SSChainShatter(mobj_t* actor)
|
|||
|
||||
actor->fuse = 1;
|
||||
}
|
||||
|
||||
// var1 = If -1, triggered by collision event
|
||||
// var2 = Strength value
|
||||
//
|
||||
// mobjinfo dependencies:
|
||||
// - deathsound - bumper noise
|
||||
// - seestate - bumper flashing state
|
||||
//
|
||||
void A_GenericBumper(mobj_t* actor)
|
||||
{
|
||||
if (var1 != -1)
|
||||
return;
|
||||
|
||||
mobj_t *other = actor->target;
|
||||
|
||||
if (!other)
|
||||
return;
|
||||
|
||||
// This code was ported from Lua
|
||||
// Original was Balloon Park's bumpers?
|
||||
INT32 hang = R_PointToAngle2(
|
||||
actor->x, actor->y,
|
||||
other->x, other->y
|
||||
);
|
||||
|
||||
INT32 vang = 0;
|
||||
|
||||
if (!P_IsObjectOnGround(other))
|
||||
{
|
||||
vang = R_PointToAngle2(
|
||||
FixedHypot(actor->x, actor->y), actor->z + (actor->height / 2),
|
||||
FixedHypot(other->x, other->y), other->z + (other->height / 2)
|
||||
);
|
||||
}
|
||||
|
||||
INT32 baseStrength = abs(astate->var2);
|
||||
fixed_t strength = (baseStrength * actor->scale) / 2;
|
||||
|
||||
other->momx = FixedMul(FixedMul(strength, FCOS(hang)), abs(FCOS(vang)));
|
||||
other->momy = FixedMul(FixedMul(strength, FSIN(hang)), abs(FCOS(vang)));
|
||||
other->momz = FixedMul(strength, FSIN(vang));
|
||||
|
||||
if (other->player)
|
||||
K_SetTireGrease(other->player, max(other->player->tiregrease, 2*TICRATE));
|
||||
|
||||
if (actor->state != &states[actor->info->seestate])
|
||||
{
|
||||
S_StartSound(actor, actor->info->deathsound);
|
||||
P_SetMobjState(actor, actor->info->seestate);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -667,7 +667,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
if (!player->mo || player->spectator)
|
||||
return;
|
||||
|
||||
if (K_TryPickMeUp(special, toucher))
|
||||
if (K_TryPickMeUp(special, toucher, false))
|
||||
return;
|
||||
|
||||
// attach to player!
|
||||
|
|
@ -1083,10 +1083,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
Obj_TrickBalloonTouchSpecial(special, toucher);
|
||||
return;
|
||||
|
||||
case MT_SEALEDSTAR_BUMPER:
|
||||
Obj_SSBumperTouchSpecial(special, toucher);
|
||||
return;
|
||||
|
||||
case MT_PULLUPHOOK:
|
||||
Obj_PulleyHookTouch(special, toucher);
|
||||
return;
|
||||
|
|
@ -3292,6 +3288,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
|
||||
player->sneakertimer = player->numsneakers = 0;
|
||||
player->panelsneakertimer = player->numpanelsneakers = 0;
|
||||
player->weaksneakertimer = player->numweaksneakers = 0;
|
||||
player->driftboost = player->strongdriftboost = 0;
|
||||
player->gateBoost = 0;
|
||||
player->fastfall = 0;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
svg_rocks
|
||||
|
||||
|
||||
|
||||
using link = mobj_t*;
|
||||
using each_ref = std::initializer_list<std::reference_wrapper<mobj_t*>>;
|
||||
|
||||
|
|
|
|||
|
|
@ -612,6 +612,9 @@ void P_InitTIDHash(void);
|
|||
void P_AddThingTID(mobj_t *mo);
|
||||
void P_RemoveThingTID(mobj_t *mo);
|
||||
void P_SetThingTID(mobj_t *mo, mtag_t tid);
|
||||
|
||||
// This function cannot be safely called after *i is removed!
|
||||
// Please call at start of loops if *i is to be mutated
|
||||
mobj_t *P_FindMobjFromTID(mtag_t tid, mobj_t *i, mobj_t *activator);
|
||||
|
||||
void P_DeleteMobjStringArgs(mobj_t *mobj);
|
||||
|
|
|
|||
14
src/p_map.c
14
src/p_map.c
|
|
@ -1552,7 +1552,19 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
|||
|
||||
if (!K_PuntCollide(thing, g_tm.thing))
|
||||
{
|
||||
K_KartSolidBounce(g_tm.thing, thing);
|
||||
state_t *st = &states[thing->info->spawnstate];
|
||||
|
||||
if (st->action.acp1 == A_GenericBumper)
|
||||
{
|
||||
P_SetTarget(&thing->target, g_tm.thing);
|
||||
|
||||
var1 = -1;
|
||||
var2 = 0;
|
||||
astate = st;
|
||||
st->action.acp1(thing);
|
||||
}
|
||||
else
|
||||
K_KartSolidBounce(g_tm.thing, thing);
|
||||
}
|
||||
return BMIT_CONTINUE;
|
||||
}
|
||||
|
|
|
|||
32
src/p_mobj.c
32
src/p_mobj.c
|
|
@ -7864,10 +7864,12 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
|
|||
if (!mobj->target || !mobj->target->health
|
||||
|| !mobj->target->player || !mobj->target->player->tripwireLeniency)
|
||||
{
|
||||
if (mobj->target && mobj->target->player && P_IsDisplayPlayer(mobj->target->player))
|
||||
if (mobj->target && mobj->target->player
|
||||
&& P_IsDisplayPlayer(mobj->target->player)
|
||||
&& !(mobj->target->player->curshield == KSHIELD_TOP))
|
||||
{
|
||||
S_StopSoundByID(mobj->target, sfx_s3k40);
|
||||
S_StartSound(mobj->target, sfx_gshaf);
|
||||
S_StopSoundByID(mobj->target, TRIPWIRE_OK_SOUND);
|
||||
S_StartSound(mobj->target, TRIPWIRE_NG_SOUND);
|
||||
}
|
||||
|
||||
P_RemoveMobj(mobj);
|
||||
|
|
@ -7989,7 +7991,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
|
|||
|
||||
if (p)
|
||||
{
|
||||
UINT16 timer = max(p->sneakertimer, p->panelsneakertimer);
|
||||
UINT16 timer = max(max(p->sneakertimer, p->panelsneakertimer), p->weaksneakertimer);
|
||||
if (timer > mobj->movecount)
|
||||
P_SetMobjState(mobj, S_BOOSTFLAME);
|
||||
mobj->movecount = timer;
|
||||
|
|
@ -9110,7 +9112,10 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
|
|||
&& (gamespeed != KARTSPEED_EASY)
|
||||
&& (newplayer->tally.active == true)
|
||||
&& (newplayer->tally.totalExp > 0) // Only true if not Time Attack
|
||||
&& (newplayer->tally.exp >= newplayer->tally.totalExp)
|
||||
&& (
|
||||
(newplayer->tally.exp >= newplayer->tally.totalExp) ||
|
||||
(K_InRaceDuel() && newplayer->duelscore == DUELWINNINGSCORE)
|
||||
)
|
||||
)
|
||||
{
|
||||
UINT8 pnum = (newplayer-players);
|
||||
|
|
@ -10350,9 +10355,24 @@ void P_MobjThinker(mobj_t *mobj)
|
|||
I_Assert(mobj != NULL);
|
||||
I_Assert(!P_MobjWasRemoved(mobj));
|
||||
|
||||
if (P_IsKartItem(mobj->type) && mobj->target && !P_MobjWasRemoved(mobj->target))
|
||||
{
|
||||
player_t *link = mobj->target->player;
|
||||
if (link && playeringame[link-players] && !link->spectator)
|
||||
mobj->relinkplayer = (link-players) + 1;
|
||||
}
|
||||
|
||||
// Remove dead target/tracer.
|
||||
if (mobj->target && P_MobjWasRemoved(mobj->target))
|
||||
{
|
||||
P_SetTarget(&mobj->target, NULL);
|
||||
if (P_IsKartItem(mobj->type) && mobj->relinkplayer && mobj->relinkplayer <= MAXPLAYERS)
|
||||
{
|
||||
player_t *relink = &players[mobj->relinkplayer-1];
|
||||
if (playeringame[relink-players] && !relink->spectator && relink->mo && !P_MobjWasRemoved(relink->mo))
|
||||
P_SetTarget(&mobj->target, relink->mo);
|
||||
}
|
||||
}
|
||||
if (mobj->tracer && P_MobjWasRemoved(mobj->tracer))
|
||||
P_SetTarget(&mobj->tracer, NULL);
|
||||
if (mobj->hnext && P_MobjWasRemoved(mobj->hnext))
|
||||
|
|
@ -15414,6 +15434,8 @@ void P_SetThingTID(mobj_t *mo, mtag_t tid)
|
|||
//
|
||||
// P_FindMobjFromTID
|
||||
// Mobj tag search function.
|
||||
// This function cannot be safely called after *i is removed!
|
||||
// Please call at start of loops if *i is to be mutated
|
||||
//
|
||||
mobj_t *P_FindMobjFromTID(mtag_t tid, mobj_t *i, mobj_t *activator)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -450,6 +450,8 @@ struct mobj_t
|
|||
|
||||
INT32 po_movecount; // Polyobject carrying (NOT savegame, NOT Lua)
|
||||
|
||||
UINT8 relinkplayer; // reassociate kartitem target when it dies. ACHTUNG 1-INDEXED
|
||||
|
||||
// WARNING: New fields must be added separately to savegame and Lua.
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -291,6 +291,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
WRITEUINT32(save->p, players[i].exp);
|
||||
WRITEINT32(save->p, players[i].gradingfactor);
|
||||
WRITEUINT16(save->p, players[i].gradingpointnum);
|
||||
WRITEINT16(save->p, players[i].duelscore);
|
||||
WRITEINT32(save->p, players[i].cheatchecknum);
|
||||
WRITEINT32(save->p, players[i].checkpointId);
|
||||
|
||||
|
|
@ -549,6 +550,8 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
WRITEUINT8(save->p, players[i].numsneakers);
|
||||
WRITEUINT16(save->p, players[i].panelsneakertimer);
|
||||
WRITEUINT8(save->p, players[i].numpanelsneakers);
|
||||
WRITEUINT16(save->p, players[i].weaksneakertimer);
|
||||
WRITEUINT8(save->p, players[i].numweaksneakers);
|
||||
|
||||
WRITEUINT8(save->p, players[i].floorboost);
|
||||
|
||||
|
|
@ -983,6 +986,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
players[i].exp = READUINT32(save->p);
|
||||
players[i].gradingfactor = READINT32(save->p);
|
||||
players[i].gradingpointnum = READUINT16(save->p);
|
||||
players[i].duelscore = READINT16(save->p);
|
||||
players[i].cheatchecknum = READINT32(save->p);
|
||||
players[i].checkpointId = READINT32(save->p);
|
||||
|
||||
|
|
@ -1194,6 +1198,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
players[i].numsneakers = READUINT8(save->p);
|
||||
players[i].panelsneakertimer = READUINT16(save->p);
|
||||
players[i].numpanelsneakers = READUINT8(save->p);
|
||||
players[i].weaksneakertimer = READUINT16(save->p);
|
||||
players[i].numweaksneakers = READUINT8(save->p);
|
||||
players[i].floorboost = READUINT8(save->p);
|
||||
|
||||
players[i].growshrinktimer = READINT16(save->p);
|
||||
|
|
@ -2997,6 +3003,7 @@ typedef enum
|
|||
MD3_REAPPEAR = 1<<1,
|
||||
MD3_PUNT_REF = 1<<2,
|
||||
MD3_OWNER = 1<<3,
|
||||
MD3_RELINK_PLAYER = 1<<4,
|
||||
} mobj_diff3_t;
|
||||
|
||||
typedef enum
|
||||
|
|
@ -3320,6 +3327,8 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8
|
|||
diff3 |= MD3_PUNT_REF;
|
||||
if (mobj->owner)
|
||||
diff3 |= MD3_OWNER;
|
||||
if (mobj->relinkplayer)
|
||||
diff3 |= MD3_RELINK_PLAYER;
|
||||
|
||||
if (diff3 != 0)
|
||||
diff2 |= MD2_MORE;
|
||||
|
|
@ -3610,6 +3619,10 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8
|
|||
{
|
||||
WRITEUINT32(save->p, mobj->owner->mobjnum);
|
||||
}
|
||||
if (diff3 & MD3_RELINK_PLAYER)
|
||||
{
|
||||
WRITEUINT8(save->p, mobj->relinkplayer);
|
||||
}
|
||||
|
||||
WRITEUINT32(save->p, mobj->mobjnum);
|
||||
}
|
||||
|
|
@ -4923,6 +4936,10 @@ static thinker_t* LoadMobjThinker(savebuffer_t *save, actionf_p1 thinker)
|
|||
{
|
||||
mobj->owner = (mobj_t *)(size_t)READUINT32(save->p);
|
||||
}
|
||||
if (diff3 & MD3_OWNER)
|
||||
{
|
||||
mobj->relinkplayer = READUINT8(save->p);
|
||||
}
|
||||
|
||||
// link tid set earlier
|
||||
P_AddThingTID(mobj);
|
||||
|
|
@ -6757,6 +6774,7 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending)
|
|||
{
|
||||
WRITEUINT16(save->p, g_voteLevels[i][0]);
|
||||
WRITEUINT16(save->p, g_voteLevels[i][1]);
|
||||
WRITEUINT8(save->p, g_votes_striked[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < VOTE_TOTAL; i++)
|
||||
|
|
@ -6834,6 +6852,7 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending)
|
|||
WRITESINT8(save->p, spbplace);
|
||||
WRITEUINT8(save->p, rainbowstartavailable);
|
||||
WRITEUINT8(save->p, inDuel);
|
||||
WRITEUINT8(save->p, overtimecheckpoints);
|
||||
|
||||
WRITEUINT32(save->p, introtime);
|
||||
WRITEUINT32(save->p, starttime);
|
||||
|
|
@ -7143,6 +7162,7 @@ static boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading)
|
|||
{
|
||||
g_voteLevels[i][0] = READUINT16(save->p);
|
||||
g_voteLevels[i][1] = READUINT16(save->p);
|
||||
g_votes_striked[i] = (boolean)READUINT8(save->p);
|
||||
}
|
||||
|
||||
for (i = 0; i < VOTE_TOTAL; i++)
|
||||
|
|
@ -7216,6 +7236,7 @@ static boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading)
|
|||
spbplace = READSINT8(save->p);
|
||||
rainbowstartavailable = (boolean)READUINT8(save->p);
|
||||
inDuel = (boolean)READUINT8(save->p);
|
||||
overtimecheckpoints = (boolean)READUINT8(save->p);
|
||||
|
||||
introtime = READUINT32(save->p);
|
||||
starttime = READUINT32(save->p);
|
||||
|
|
|
|||
130
src/p_setup.cpp
130
src/p_setup.cpp
|
|
@ -8388,6 +8388,46 @@ void P_LoadLevelMusic(void)
|
|||
Music_ResetLevelVolume();
|
||||
}
|
||||
|
||||
void P_FreeLevelState(void)
|
||||
{
|
||||
if (numsectors)
|
||||
{
|
||||
F_EndTextPrompt(false, true);
|
||||
K_UnsetDialogue();
|
||||
|
||||
ACS_InvalidateMapScope();
|
||||
LUA_InvalidateLevel();
|
||||
|
||||
Obj_ClearCheckpoints();
|
||||
|
||||
sector_t *ss;
|
||||
for (ss = sectors; sectors+numsectors != ss; ss++)
|
||||
{
|
||||
Z_Free(ss->attached);
|
||||
Z_Free(ss->attachedsolid);
|
||||
}
|
||||
|
||||
// This is the simplest guard against double frees.
|
||||
// No valid map has zero sectors. Or, come to think
|
||||
// of it, less than two in general! ~toast 310525
|
||||
numsectors = 0;
|
||||
}
|
||||
|
||||
// Clear pointers that would be left dangling by the purge
|
||||
R_FlushTranslationColormapCache();
|
||||
|
||||
#ifdef HWRENDER
|
||||
// Free GPU textures before freeing patches.
|
||||
if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED))
|
||||
HWR_ClearAllTextures();
|
||||
#endif
|
||||
|
||||
G_FreeGhosts(); // ghosts are allocated with PU_LEVEL
|
||||
Patch_FreeTag(PU_PATCH_LOWPRIORITY);
|
||||
Patch_FreeTag(PU_PATCH_ROTATED);
|
||||
Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1);
|
||||
}
|
||||
|
||||
/** Loads a level from a lump or external wad.
|
||||
*
|
||||
* \param fromnetsave If true, skip some stuff because we're loading a netgame snapshot.
|
||||
|
|
@ -8401,7 +8441,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
|
|||
// 99% of the things already did, so.
|
||||
// Map header should always be in place at this point
|
||||
INT32 i, ranspecialwipe = 0;
|
||||
sector_t *ss;
|
||||
virtlump_t *encoreLump = NULL;
|
||||
|
||||
levelloading = true;
|
||||
|
|
@ -8474,7 +8513,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
|
|||
wipegamestate = gamestate; // Don't fade if reloading the gamestate
|
||||
// Encore mode fade to pink to white
|
||||
// This is handled BEFORE sounds are stopped.
|
||||
else if (encoremode && !prevencoremode && modeattacking == ATTACKING_NONE && !demo.rewinding)
|
||||
else if (encoremode && !prevencoremode && !demo.simplerewind)
|
||||
{
|
||||
if (rendermode != render_none)
|
||||
{
|
||||
|
|
@ -8545,7 +8584,14 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
|
|||
|
||||
// Let's fade to white here
|
||||
// But only if we didn't do the encore startup wipe
|
||||
if (!demo.rewinding && !reloadinggamestate)
|
||||
if (demo.attract || demo.simplerewind)
|
||||
{
|
||||
// Leave the music alone! We're already playing what we want!
|
||||
// Pull from RNG even though music will never change
|
||||
// To silence playback has desynced warning
|
||||
P_Random(PR_MUSICSELECT);
|
||||
}
|
||||
else if (!reloadinggamestate)
|
||||
{
|
||||
int wipetype = wipe_level_toblack;
|
||||
|
||||
|
|
@ -8558,15 +8604,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
|
|||
FixedDiv((F_GetWipeLength(wipedefs[wipe_level_toblack])-2)*NEWTICRATERATIO, NEWTICRATE), MUSICRATE));
|
||||
#endif
|
||||
|
||||
if (demo.attract)
|
||||
{
|
||||
; // Leave the music alone! We're already playing what we want!
|
||||
|
||||
// Pull from RNG even though music will never change
|
||||
// To silence playback has desynced warning
|
||||
P_Random(PR_MUSICSELECT);
|
||||
}
|
||||
else if (K_PodiumSequence())
|
||||
if (K_PodiumSequence())
|
||||
{
|
||||
// mapmusrng is set by local player position in K_ResetCeremony
|
||||
P_LoadLevelMusic();
|
||||
|
|
@ -8643,44 +8681,42 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
|
|||
}
|
||||
|
||||
F_RunWipe(wipetype, wipedefs[wipetype], false, ((levelfadecol == 0) ? "FADEMAP1" : "FADEMAP0"), false, false);
|
||||
|
||||
// Hold respawn to keep waiting until you're ready
|
||||
if (G_IsModeAttackRetrying() && !demo.playback)
|
||||
{
|
||||
nowtime = lastwipetic;
|
||||
while (G_PlayerInputDown(0, gc_respawn, splitscreen + 1) == true)
|
||||
{
|
||||
while (!((nowtime = I_GetTime()) - lastwipetic))
|
||||
{
|
||||
I_Sleep(cv_sleep.value);
|
||||
I_UpdateTime();
|
||||
} \
|
||||
|
||||
I_OsPolling();
|
||||
G_ResetAllDeviceResponding();
|
||||
|
||||
for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1))
|
||||
{
|
||||
HandleGamepadDeviceEvents(&events[eventtail]);
|
||||
G_MapEventsToControls(&events[eventtail]);
|
||||
}
|
||||
|
||||
lastwipetic = nowtime;
|
||||
if (moviemode && rendermode == render_opengl)
|
||||
M_LegacySaveFrame();
|
||||
else if (moviemode && rendermode == render_soft)
|
||||
I_CaptureVideoFrame();
|
||||
NetKeepAlive();
|
||||
}
|
||||
|
||||
//wipestyleflags |= (WSF_FADEOUT|WSF_TOWHITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (!titlemapinaction)
|
||||
wipegamestate = GS_LEVEL;
|
||||
*/
|
||||
|
||||
// Close text prompt before freeing the old level
|
||||
F_EndTextPrompt(false, true);
|
||||
|
||||
K_UnsetDialogue();
|
||||
|
||||
ACS_InvalidateMapScope();
|
||||
|
||||
LUA_InvalidateLevel();
|
||||
|
||||
Obj_ClearCheckpoints();
|
||||
|
||||
for (ss = sectors; sectors+numsectors != ss; ss++)
|
||||
{
|
||||
Z_Free(ss->attached);
|
||||
Z_Free(ss->attachedsolid);
|
||||
}
|
||||
|
||||
// Clear pointers that would be left dangling by the purge
|
||||
R_FlushTranslationColormapCache();
|
||||
|
||||
#ifdef HWRENDER
|
||||
// Free GPU textures before freeing patches.
|
||||
if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED))
|
||||
HWR_ClearAllTextures();
|
||||
#endif
|
||||
|
||||
G_FreeGhosts(); // ghosts are allocated with PU_LEVEL
|
||||
Patch_FreeTag(PU_PATCH_LOWPRIORITY);
|
||||
Patch_FreeTag(PU_PATCH_ROTATED);
|
||||
Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1);
|
||||
P_FreeLevelState();
|
||||
|
||||
R_InitializeLevelInterpolators();
|
||||
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ extern mapthing_t *mapthings;
|
|||
|
||||
void P_SetupLevelSky(const char *skytexname, boolean global);
|
||||
void P_RespawnThings(void);
|
||||
void P_FreeLevelState(void);
|
||||
void P_ResetLevelMusic(void);
|
||||
boolean P_UseContinuousLevelMusic(void);
|
||||
void P_LoadLevelMusic(void);
|
||||
|
|
|
|||
61
src/p_spec.c
61
src/p_spec.c
|
|
@ -53,6 +53,7 @@
|
|||
#include "m_easing.h"
|
||||
#include "music.h"
|
||||
#include "k_battle.h" // battleprisons
|
||||
#include "k_endcam.h" // K_EndCameraIsFreezing()
|
||||
|
||||
// Not sure if this is necessary, but it was in w_wad.c, so I'm putting it here too -Shadow Hog
|
||||
#include <errno.h>
|
||||
|
|
@ -1995,7 +1996,7 @@ static void K_HandleLapIncrement(player_t *player)
|
|||
}
|
||||
|
||||
// finished race exit setup
|
||||
if (player->laps > numlaps)
|
||||
if (player->laps > numlaps && !K_InRaceDuel())
|
||||
{
|
||||
pflags_t applyflags = 0;
|
||||
if (specialstageinfo.valid == true)
|
||||
|
|
@ -2021,7 +2022,8 @@ static void K_HandleLapIncrement(player_t *player)
|
|||
: skins[player->skin].flags;
|
||||
if (skinflags & SF_IRONMAN)
|
||||
{
|
||||
SetRandomFakePlayerSkin(player, true, false);
|
||||
if ((player->laps == 1 && lapisfresh) || !K_InRaceDuel()) // We'll do this in K_CheckpointCrossAward if necessary.
|
||||
SetRandomFakePlayerSkin(player, true, false);
|
||||
}
|
||||
|
||||
// Always trust waypoints entering the first lap.
|
||||
|
|
@ -2064,24 +2066,32 @@ static void K_HandleLapIncrement(player_t *player)
|
|||
|
||||
if (rainbowstartavailable == true && player->mo->hitlag == 0)
|
||||
{
|
||||
S_StartSound(player->mo, sfx_s23c);
|
||||
player->startboost = 125;
|
||||
|
||||
K_SpawnDriftBoostExplosion(player, 4);
|
||||
K_SpawnDriftElectricSparks(player, SKINCOLOR_SILVER, false);
|
||||
K_SpawnAmps(player, 35, player->mo);
|
||||
|
||||
if (g_teamplay)
|
||||
if (K_InRaceDuel())
|
||||
{
|
||||
for (UINT8 j = 0; i < MAXPLAYERS; i++)
|
||||
K_SpawnDriftElectricSparks(player, player->skincolor, false);
|
||||
K_SpawnAmps(player, 20, player->mo);
|
||||
}
|
||||
else
|
||||
{
|
||||
S_StartSound(player->mo, sfx_s23c);
|
||||
player->startboost = 125;
|
||||
|
||||
K_SpawnDriftBoostExplosion(player, 4);
|
||||
K_SpawnDriftElectricSparks(player, SKINCOLOR_SILVER, false);
|
||||
K_SpawnAmps(player, (K_InRaceDuel()) ? 20 : 35, player->mo);
|
||||
|
||||
if (g_teamplay)
|
||||
{
|
||||
if (!playeringame[j] || players[j].spectator || !players[j].mo || P_MobjWasRemoved(players[j].mo))
|
||||
continue;
|
||||
if (!G_SameTeam(player, &players[j]))
|
||||
continue;
|
||||
if (player == &players[j])
|
||||
continue;
|
||||
K_SpawnAmps(&players[j], 10, player->mo);
|
||||
for (UINT8 j = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[j] || players[j].spectator || !players[j].mo || P_MobjWasRemoved(players[j].mo))
|
||||
continue;
|
||||
if (!G_SameTeam(player, &players[j]))
|
||||
continue;
|
||||
if (player == &players[j])
|
||||
continue;
|
||||
K_SpawnAmps(&players[j], 10, player->mo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2105,7 +2115,9 @@ static void K_HandleLapIncrement(player_t *player)
|
|||
}
|
||||
else if (P_IsDisplayPlayer(player))
|
||||
{
|
||||
if (numlaps > 1 && player->laps == numlaps) // final lap
|
||||
if (K_InRaceDuel())
|
||||
S_StartSound(NULL, sfx_s221);
|
||||
else if (numlaps > 1 && player->laps == numlaps) // final lap
|
||||
S_StartSound(NULL, sfx_s3k68);
|
||||
else if ((player->laps > 1) && (player->laps < numlaps)) // non-final lap
|
||||
S_StartSound(NULL, sfx_s221);
|
||||
|
|
@ -2118,7 +2130,7 @@ static void K_HandleLapIncrement(player_t *player)
|
|||
}
|
||||
else
|
||||
{
|
||||
if ((player->laps > numlaps) && (player->position == 1))
|
||||
if ((player->laps > numlaps) && (player->position == 1) && (!K_InRaceDuel()))
|
||||
{
|
||||
// opponent finished
|
||||
S_StartSound(NULL, sfx_s253);
|
||||
|
|
@ -3263,8 +3275,13 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha
|
|||
return false;
|
||||
}
|
||||
|
||||
while ((targetThing = P_FindMobjFromTID(args[1], targetThing, mo)) != NULL)
|
||||
mobj_t *next = P_FindMobjFromTID(args[1], targetThing, mo);
|
||||
|
||||
while ((targetThing = next) != NULL)
|
||||
{
|
||||
// First in case of deletion. (Can't check for state == S_NULL because of A_ calls, etc)
|
||||
next = P_FindMobjFromTID(args[1], targetThing, mo);
|
||||
|
||||
if (targetThing->player != NULL)
|
||||
{
|
||||
continue;
|
||||
|
|
@ -4752,7 +4769,7 @@ void P_SetupSignExit(player_t *player, boolean tie)
|
|||
return;
|
||||
|
||||
// SRB2Kart: FINALLY, add in an alternative if no place is found
|
||||
if (player->mo && !P_MobjWasRemoved(player->mo))
|
||||
if (player->mo && !P_MobjWasRemoved(player->mo) && !K_EndCameraIsFreezing())
|
||||
{
|
||||
thing = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->floorz, MT_SIGN);
|
||||
thing->angle = bestAngle;
|
||||
|
|
|
|||
56
src/p_tick.c
56
src/p_tick.c
|
|
@ -599,9 +599,9 @@ static inline void P_DeviceRumbleTick(void)
|
|||
{
|
||||
low = high = 65536 / 2;
|
||||
}
|
||||
else if (player->sneakertimer > (sneakertime-(TICRATE/2)) || player->panelsneakertimer > (sneakertime-(TICRATE/2)))
|
||||
else if (player->sneakertimer > (sneakertime-(TICRATE/2)) || player->panelsneakertimer > (sneakertime-(TICRATE/2)) || player->weaksneakertimer > (sneakertime-(TICRATE/2)))
|
||||
{
|
||||
low = high = 65536 / (3+player->numsneakers+player->numpanelsneakers);
|
||||
low = high = 65536 / (3+player->numsneakers+player->numpanelsneakers+player->numweaksneakers);
|
||||
}
|
||||
else if (((player->boostpower < FRACUNIT) || (player->stairjank > 8))
|
||||
&& P_IsObjectOnGround(player->mo) && player->speed != 0)
|
||||
|
|
@ -739,15 +739,7 @@ void P_Ticker(boolean run)
|
|||
// Check for pause or menu up in single player
|
||||
if (paused || P_AutoPause())
|
||||
{
|
||||
if (demo.rewinding && leveltime > 0)
|
||||
{
|
||||
leveltime = (leveltime-1) & ~3;
|
||||
if (timeinmap > 0)
|
||||
timeinmap = (timeinmap-1) & ~3;
|
||||
G_PreviewRewind(leveltime);
|
||||
}
|
||||
else
|
||||
P_RunChaseCameras(); // special case: allow freecam to MOVE during pause!
|
||||
P_RunChaseCameras(); // special case: allow freecam to MOVE during pause!
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -762,17 +754,23 @@ void P_Ticker(boolean run)
|
|||
|
||||
if (demo.recording)
|
||||
{
|
||||
G_WriteDemoExtraData();
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
if (playeringame[i])
|
||||
G_WriteDemoTiccmd(&players[i].cmd, i);
|
||||
if (!G_ConsiderEndingDemoWrite())
|
||||
{
|
||||
G_WriteDemoExtraData();
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
if (playeringame[i])
|
||||
G_WriteDemoTiccmd(&players[i].cmd, i);
|
||||
}
|
||||
}
|
||||
if (demo.playback && !demo.waitingfortally)
|
||||
{
|
||||
G_ReadDemoExtraData();
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
if (playeringame[i])
|
||||
G_ReadDemoTiccmd(&players[i].cmd, i);
|
||||
if (!G_ConsiderEndingDemoRead())
|
||||
{
|
||||
G_ReadDemoExtraData();
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
if (playeringame[i])
|
||||
G_ReadDemoTiccmd(&players[i].cmd, i);
|
||||
}
|
||||
}
|
||||
|
||||
LUA_ResetTicTimers();
|
||||
|
|
@ -1078,7 +1076,7 @@ void P_Ticker(boolean run)
|
|||
}
|
||||
}
|
||||
|
||||
if (g_fast_forward == 0)
|
||||
if (g_fast_forward == 0 || demo.simplerewind)
|
||||
{
|
||||
timeinmap++;
|
||||
}
|
||||
|
|
@ -1168,14 +1166,21 @@ void P_Ticker(boolean run)
|
|||
|
||||
if (demo.recording)
|
||||
{
|
||||
G_WriteAllGhostTics();
|
||||
|
||||
if (cv_recordmultiplayerdemos.value && demo.savebutton && demo.savebutton + 3*TICRATE < leveltime)
|
||||
G_CheckDemoTitleEntry();
|
||||
|
||||
if (!G_ConsiderEndingDemoWrite())
|
||||
{
|
||||
G_WriteAllGhostTics();
|
||||
}
|
||||
}
|
||||
else if (demo.playback && !demo.waitingfortally) // Use Ghost data for consistency checks.
|
||||
else if (demo.playback && !demo.waitingfortally)
|
||||
{
|
||||
G_ConsAllGhostTics();
|
||||
if (!G_ConsiderEndingDemoRead())
|
||||
{
|
||||
// Use Ghost data for consistency checks.
|
||||
G_ConsAllGhostTics();
|
||||
}
|
||||
}
|
||||
|
||||
if (modeattacking)
|
||||
|
|
@ -1238,9 +1243,6 @@ void P_Ticker(boolean run)
|
|||
|
||||
P_MapEnd();
|
||||
|
||||
if (demo.playback)
|
||||
G_StoreRewindInfo();
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
G_CopyTiccmd(&players[i].oldcmd, &players[i].cmd, 1);
|
||||
|
|
|
|||
17
src/p_user.c
17
src/p_user.c
|
|
@ -1389,7 +1389,7 @@ void P_DoPlayerExit(player_t *player, pflags_t flags)
|
|||
void P_DoAllPlayersExit(pflags_t flags, boolean trygivelife)
|
||||
{
|
||||
UINT8 i;
|
||||
const boolean dofinishsound = (musiccountdown == 0);
|
||||
const boolean dofinishsound = (musiccountdown == 0) && (!K_InRaceDuel());
|
||||
|
||||
if (grandprixinfo.gp == false
|
||||
|| grandprixinfo.eventmode == GPEVENT_SPECIAL
|
||||
|
|
@ -2348,7 +2348,7 @@ static void P_UpdatePlayerAngle(player_t *player)
|
|||
// This is the hardest case for the turn solver, because your handling properties on
|
||||
// client side are very different than your handling properties on server side—at least,
|
||||
// until your drift status makes the full round-trip and is reflected in your gamestate.
|
||||
if (player->drift && abs(player->drift) < 5)
|
||||
if (player->drift && abs(player->drift) < 5 && player->cmd.latency)
|
||||
{
|
||||
steeringRight = KART_FULLTURN;
|
||||
steeringLeft = -KART_FULLTURN;
|
||||
|
|
@ -2621,7 +2621,7 @@ void P_MovePlayer(player_t *player)
|
|||
////////////////////////////
|
||||
|
||||
// SRB2kart - Drifting smoke and fire
|
||||
if ((player->sneakertimer || player->panelsneakertimer || player->flamedash)
|
||||
if ((player->sneakertimer || player->panelsneakertimer || player->weaksneakertimer || player->flamedash)
|
||||
&& onground && (leveltime & 1))
|
||||
K_SpawnBoostTrail(player);
|
||||
|
||||
|
|
@ -3766,7 +3766,6 @@ boolean P_SpectatorJoinGame(player_t *player)
|
|||
}
|
||||
player->spectator = false;
|
||||
player->pflags &= ~PF_WANTSTOJOIN;
|
||||
player->spectatewait = 0;
|
||||
player->team = TEAM_UNASSIGNED; // We will auto-assign later.
|
||||
player->playerstate = PST_REBORN;
|
||||
player->enteredGame = true;
|
||||
|
|
@ -3781,6 +3780,10 @@ boolean P_SpectatorJoinGame(player_t *player)
|
|||
// a surprise tool that will help us later...
|
||||
text = va("\x82*%s entered the game.", player_names[player-players]);
|
||||
|
||||
if (P_IsMachineLocalPlayer(player) && player->spectatewait > TICRATE)
|
||||
S_StartSound(NULL, sfx_s3ka9);
|
||||
player->spectatewait = 0;
|
||||
|
||||
HU_AddChatText(text, false);
|
||||
return true; // no more player->mo, cannot continue.
|
||||
}
|
||||
|
|
@ -4251,6 +4254,12 @@ void P_PlayerThink(player_t *player)
|
|||
player->airtime++;
|
||||
}
|
||||
|
||||
if ((player->pflags & PF_FAULT) || (player->pflags & PF_VOID))
|
||||
{
|
||||
player->lastairtime = 0;
|
||||
player->airtime = 0;
|
||||
}
|
||||
|
||||
cmd = &player->cmd;
|
||||
|
||||
// SRB2kart
|
||||
|
|
|
|||
|
|
@ -3797,6 +3797,12 @@ boolean R_ThingVisible (mobj_t *thing)
|
|||
case MT_BATTLECAPSULE_PIECE:
|
||||
case MT_SPRAYCAN:
|
||||
case MT_PLAYER:
|
||||
case MT_LANDMINE:
|
||||
case MT_SSMINE:
|
||||
case MT_SSMINE_SHIELD:
|
||||
case MT_CHECKPOINT_END:
|
||||
case MT_SIGNSPARKLE:
|
||||
case MT_THOK: // checkpoint parts
|
||||
return false;
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -198,7 +198,6 @@ static void (*music_fade_callback)();
|
|||
|
||||
static SDL_AudioDeviceID g_device_id;
|
||||
static SDL_AudioDeviceID g_input_device_id;
|
||||
static boolean g_input_device_paused;
|
||||
|
||||
void* I_GetSfx(sfxinfo_t* sfx)
|
||||
{
|
||||
|
|
@ -999,14 +998,14 @@ void I_UpdateAudioRecorder(void)
|
|||
|
||||
boolean I_SoundInputIsEnabled(void)
|
||||
{
|
||||
return g_input_device_id != 0 && !g_input_device_paused;
|
||||
return g_input_device_id != 0;
|
||||
}
|
||||
|
||||
boolean I_SoundInputSetEnabled(boolean enabled)
|
||||
{
|
||||
if (g_input_device_id == 0 && enabled)
|
||||
{
|
||||
if (SDL_GetNumAudioDevices(true) == 0)
|
||||
if (!sound_started || SDL_GetNumAudioDevices(true) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1023,21 +1022,17 @@ boolean I_SoundInputSetEnabled(boolean enabled)
|
|||
CONS_Alert(CONS_WARNING, "Failed to open input audio device: %s\n", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
g_input_device_paused = true;
|
||||
}
|
||||
|
||||
if (enabled && g_input_device_paused)
|
||||
{
|
||||
SDL_PauseAudioDevice(g_input_device_id, SDL_FALSE);
|
||||
g_input_device_paused = false;
|
||||
}
|
||||
else if (!enabled && !g_input_device_paused)
|
||||
else if (g_input_device_id != 0 && !enabled)
|
||||
{
|
||||
SDL_PauseAudioDevice(g_input_device_id, SDL_TRUE);
|
||||
SDL_ClearQueuedAudio(g_input_device_id);
|
||||
g_input_device_paused = true;
|
||||
SDL_CloseAudioDevice(g_input_device_id);
|
||||
g_input_device_id = 0;
|
||||
}
|
||||
return !g_input_device_paused;
|
||||
|
||||
return enabled;
|
||||
}
|
||||
|
||||
UINT32 I_SoundInputDequeueSamples(void *data, UINT32 len)
|
||||
|
|
|
|||
|
|
@ -737,6 +737,8 @@ void ST_startTitleCard(void)
|
|||
lt_ticker = lt_exitticker = lt_lasttic = 0;
|
||||
lt_endtime = 4*TICRATE; // + (10*NEWTICRATERATIO);
|
||||
lt_fade = 0;
|
||||
|
||||
WipeStageTitle = false;
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -788,7 +790,7 @@ patch_t *ST_getRoundPicture(boolean small)
|
|||
//
|
||||
void ST_runTitleCard(void)
|
||||
{
|
||||
boolean run = !(paused || P_AutoPause() || g_fast_forward > 0);
|
||||
boolean run = !(paused || P_AutoPause() || (g_fast_forward > 0 && demo.simplerewind == DEMO_REWIND_OFF));
|
||||
INT32 auxticker;
|
||||
boolean doroundicon = (ST_getRoundPicture(false) != NULL);
|
||||
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ TYPEDEF (plrconfig);
|
|||
TYPEDEF (filesneededconfig_pak);
|
||||
TYPEDEF (doomdata_t);
|
||||
TYPEDEF (serverelem_t);
|
||||
TYPEDEF (rewind_t);
|
||||
TYPEDEF (clientkey_pak);
|
||||
TYPEDEF (serverchallenge_pak);
|
||||
TYPEDEF (challengeall_pak);
|
||||
|
|
|
|||
280
src/y_inter.cpp
280
src/y_inter.cpp
|
|
@ -951,6 +951,18 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset)
|
|||
i--;
|
||||
}
|
||||
while (true);
|
||||
|
||||
if (standings->rankingsmode)
|
||||
{
|
||||
if (standings->isduel)
|
||||
{
|
||||
Y_DrawRankMode(BASEVIDWIDTH / 2 + xoffset, BASEVIDHEIGHT - 19, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Y_DrawRankMode(x + 122, returny - yspacing + 7, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -959,11 +971,15 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset)
|
|||
// Handles drawing the bottom-of-screen progression.
|
||||
// Currently requires intermission y_data for animation only.
|
||||
//
|
||||
void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, boolean widescreen)
|
||||
void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, boolean widescreen, boolean adminmode)
|
||||
{
|
||||
if (roundqueue.size == 0)
|
||||
{
|
||||
return;
|
||||
if (!adminmode
|
||||
|| menuqueue.size == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The following is functionally a hack.
|
||||
|
|
@ -1029,6 +1045,10 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
|
|||
prize_dot[BPP_AHEAD] = static_cast<patch_t*>(W_CachePatchName("R_RRMRK4", PU_PATCH));
|
||||
prize_dot[BPP_DONE] = static_cast<patch_t*>(W_CachePatchName("R_RRMRK6", PU_PATCH));
|
||||
|
||||
patch_t *rpmark[2];
|
||||
rpmark[0] = static_cast<patch_t*>(W_CachePatchName("R_RPMARK", PU_PATCH));
|
||||
rpmark[1] = static_cast<patch_t*>(W_CachePatchName("R_R2MARK", PU_PATCH));
|
||||
|
||||
UINT8 *colormap = NULL, *oppositemap = NULL;
|
||||
fixed_t playerx = 0, playery = 0;
|
||||
UINT8 pskin = MAXSKINS;
|
||||
|
|
@ -1067,10 +1087,37 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
|
|||
upwa = true;
|
||||
}
|
||||
|
||||
workingqueuesize--;
|
||||
if (!adminmode)
|
||||
{
|
||||
workingqueuesize--;
|
||||
}
|
||||
}
|
||||
|
||||
INT32 widthofroundqueue = 24*(workingqueuesize - 1);
|
||||
INT32 widthofroundqueue, totalsteps;
|
||||
|
||||
INT32 menusendoffset = 0;
|
||||
if (menuqueue.sending)
|
||||
{
|
||||
if (menuqueue.sending > menuqueue.size)
|
||||
{
|
||||
menusendoffset = menuqueue.size;
|
||||
}
|
||||
else
|
||||
{
|
||||
menusendoffset = menuqueue.sending-1;
|
||||
}
|
||||
}
|
||||
|
||||
if (adminmode)
|
||||
{
|
||||
totalsteps = std::min(workingqueuesize + (menuqueue.size - menusendoffset), ROUNDQUEUE_MAX);
|
||||
}
|
||||
else
|
||||
{
|
||||
totalsteps = workingqueuesize;
|
||||
}
|
||||
|
||||
widthofroundqueue = (totalsteps - 1) * 24;
|
||||
|
||||
INT32 x = (BASEVIDWIDTH - widthofroundqueue) / 2;
|
||||
INT32 y, basey = 167 + offset;
|
||||
|
|
@ -1079,7 +1126,7 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
|
|||
|
||||
// The following block handles horizontal easing of the
|
||||
// progression bar on the last non-rankrestricted round.
|
||||
if (standings->showrank == true)
|
||||
if (!adminmode && standings->showrank == true)
|
||||
{
|
||||
fixed_t percentslide = 0;
|
||||
SINT8 deferxoffs = 0;
|
||||
|
|
@ -1139,12 +1186,22 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
|
|||
V_DrawMappedPatch(xiter, basey, baseflags, queuebg_upwa, greymap);
|
||||
}
|
||||
|
||||
// Draw to left side of screen
|
||||
while (xiter > -bufferspace)
|
||||
{
|
||||
xiter -= 24;
|
||||
V_DrawMappedPatch(xiter, basey, baseflags, queuebg_flat, greymap);
|
||||
}
|
||||
|
||||
// Draw to right side of screen
|
||||
xiter = x + widthofroundqueue;
|
||||
while (xiter < BASEVIDWIDTH + bufferspace)
|
||||
{
|
||||
xiter += 24;
|
||||
V_DrawMappedPatch(xiter, basey, baseflags, queuebg_flat, greymap);
|
||||
}
|
||||
|
||||
// Actually queued maps
|
||||
for (i = 0; i < workingqueuesize; i++)
|
||||
{
|
||||
// Draw the background, and grab the appropriate line, to the right of the dot
|
||||
|
|
@ -1174,7 +1231,13 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
|
|||
}
|
||||
else
|
||||
{
|
||||
V_DrawMappedPatch(x, basey, baseflags, queuebg_flat, greymap);
|
||||
V_DrawMappedPatch(
|
||||
x,
|
||||
basey,
|
||||
baseflags,
|
||||
((workingqueuesize == totalsteps) ? queuebg_flat : queuebg_upwa),
|
||||
greymap
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1309,17 +1372,9 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
|
|||
}
|
||||
else
|
||||
{
|
||||
// No more line! Fill in background to right edge of screen
|
||||
xiter = x;
|
||||
while (xiter < BASEVIDWIDTH + bufferspace)
|
||||
{
|
||||
xiter += 24;
|
||||
V_DrawMappedPatch(xiter, basey, baseflags, queuebg_flat, greymap);
|
||||
}
|
||||
|
||||
// Handle special entry on the end
|
||||
// (has to be drawn before the semifinal dot due to overlap)
|
||||
if (standings->showrank == true)
|
||||
if (!adminmode && standings->showrank == true)
|
||||
{
|
||||
const fixed_t x2 = x + spacetospecial;
|
||||
|
||||
|
|
@ -1330,7 +1385,7 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
|
|||
}
|
||||
else if (
|
||||
doanimations == true
|
||||
&& roundqueue.position == roundqueue.size-1
|
||||
&& roundqueue.position == workingqueuesize
|
||||
&& timer - interpoffs <= 2*TICRATE
|
||||
)
|
||||
{
|
||||
|
|
@ -1510,13 +1565,62 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
|
|||
x += 24;
|
||||
}
|
||||
|
||||
totalsteps -= i;
|
||||
|
||||
// Maps in the progress of being queued on the menu
|
||||
if (adminmode && totalsteps)
|
||||
{
|
||||
for (i = menusendoffset; i < (totalsteps + menusendoffset); i++)
|
||||
{
|
||||
upwa ^= true;
|
||||
if (upwa == false)
|
||||
{
|
||||
y = basey + 4;
|
||||
|
||||
V_DrawMappedPatch(x, basey, baseflags, queuebg_down, greymap);
|
||||
}
|
||||
else
|
||||
{
|
||||
y = basey + 12;
|
||||
|
||||
if (i+1 != menuqueue.size) // no more line?
|
||||
{
|
||||
V_DrawMappedPatch(x, basey, baseflags, queuebg_upwa, greymap);
|
||||
}
|
||||
else
|
||||
{
|
||||
V_DrawMappedPatch(x, basey, baseflags, queuebg_flat, greymap);
|
||||
}
|
||||
}
|
||||
|
||||
V_DrawMappedPatch(
|
||||
x - 8, y,
|
||||
baseflags,
|
||||
level_dot[BPP_AHEAD],
|
||||
NULL
|
||||
);
|
||||
|
||||
V_DrawMappedPatch(
|
||||
x - 10, y - 14,
|
||||
baseflags,
|
||||
rpmark[0],
|
||||
NULL
|
||||
);
|
||||
|
||||
K_DrawMapAsFace(
|
||||
x - 9, y - 13,
|
||||
(baseflags|((menuqueue.entries[i].encore) ? V_FLIP : 0)),
|
||||
menuqueue.entries[i].mapnum,
|
||||
NULL
|
||||
);
|
||||
|
||||
x += 24;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the player position through the round queue!
|
||||
if (playery != 0)
|
||||
{
|
||||
patch_t *rpmark[2];
|
||||
rpmark[0] = static_cast<patch_t*>(W_CachePatchName("R_RPMARK", PU_PATCH));
|
||||
rpmark[1] = static_cast<patch_t*>(W_CachePatchName("R_R2MARK", PU_PATCH));
|
||||
|
||||
// Change alignment
|
||||
playerx -= (10 * FRACUNIT);
|
||||
playery -= (14 * FRACUNIT);
|
||||
|
|
@ -1627,6 +1731,88 @@ void Y_DrawIntermissionButton(INT32 startslide, INT32 through, boolean widescree
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Y_DrawRankMode
|
||||
//
|
||||
// Draws EXP or MOBIUMS label depending on context.
|
||||
// x and y designate the coordinates of the most bottom-right pixel to draw from (because it is the left extent and patch heights that vary),
|
||||
// or the bottom-center if center is true.
|
||||
//
|
||||
void Y_DrawRankMode(INT32 x, INT32 y, boolean center)
|
||||
{
|
||||
boolean useMobiums = (powertype != PWRLV_DISABLED);
|
||||
INT32 textWidth, middleLeftEdge, middleRightEdge, middleWidth;
|
||||
|
||||
char text[8];
|
||||
char iconPatchName[8];
|
||||
UINT8 iconWidth; // the graphic paddings are inconsistent...
|
||||
UINT8 *iconColormap;
|
||||
UINT8 *stickerColormap;
|
||||
|
||||
patch_t *iconPatch;
|
||||
patch_t *stickerTail = static_cast<patch_t*>(W_CachePatchName("INT_STK1", PU_CACHE));
|
||||
patch_t *stickerMiddle = static_cast<patch_t*>(W_CachePatchName("INT_STK2", PU_CACHE));
|
||||
patch_t *stickerHead = center ? stickerTail : static_cast<patch_t*>(W_CachePatchName("INT_STK3", PU_CACHE));
|
||||
UINT32 stickerHeadFlags = 0;
|
||||
UINT8 stickerHeadOffset = 0;
|
||||
|
||||
if (useMobiums)
|
||||
{
|
||||
snprintf(text, sizeof text, "MOBIUMS");
|
||||
snprintf(iconPatchName, sizeof iconPatchName, "K_STMOB");
|
||||
iconWidth = 22;
|
||||
iconColormap = R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(SKINCOLOR_NONE), GTC_CACHE);
|
||||
stickerColormap = R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(SKINCOLOR_TEA), GTC_CACHE);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(text, sizeof text, "EXP");
|
||||
snprintf(iconPatchName, sizeof iconPatchName, "K_STEXP");
|
||||
iconWidth = 16;
|
||||
iconColormap = R_GetTranslationColormap(TC_RAINBOW, static_cast<skincolornum_t>(SKINCOLOR_MUSTARD), GTC_CACHE);
|
||||
stickerColormap = R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(SKINCOLOR_MUSTARD), GTC_CACHE);
|
||||
}
|
||||
|
||||
iconPatch = static_cast<patch_t*>(W_CachePatchName(iconPatchName, PU_CACHE));
|
||||
textWidth = (INT32)V_ThinStringWidth(text, 0);
|
||||
middleLeftEdge = x - iconWidth - textWidth - 8;
|
||||
middleRightEdge = x - stickerHead->width;
|
||||
middleWidth = middleRightEdge - middleLeftEdge;
|
||||
|
||||
if (center)
|
||||
{
|
||||
// flip the right-hand sticker tail and keep it left-aligned
|
||||
stickerHeadFlags |= V_FLIP;
|
||||
stickerHeadOffset += stickerHead->width;
|
||||
|
||||
// sliiightly extend the right side of the sticker
|
||||
middleWidth += 2;
|
||||
middleRightEdge += 2;
|
||||
|
||||
// shift all components to the right so that our x coordinates are center-aligned
|
||||
#define CENTER_SHIFT (stickerHead->width + middleWidth / 2)
|
||||
x += CENTER_SHIFT;
|
||||
middleLeftEdge += CENTER_SHIFT;
|
||||
middleRightEdge += CENTER_SHIFT;
|
||||
#undef CENTER_SHIFT
|
||||
}
|
||||
|
||||
// draw sticker
|
||||
V_DrawMappedPatch(middleRightEdge + stickerHeadOffset, y - stickerHead->height, stickerHeadFlags, stickerHead, stickerColormap);
|
||||
V_DrawStretchyFixedPatch(
|
||||
middleLeftEdge << FRACBITS,
|
||||
(y - stickerMiddle->height) << FRACBITS,
|
||||
(middleWidth << FRACBITS) / stickerMiddle->width + 1,
|
||||
FRACUNIT,
|
||||
0, stickerMiddle, stickerColormap
|
||||
);
|
||||
V_DrawMappedPatch(middleLeftEdge - stickerTail->width, y - stickerTail->height, 0, stickerTail, stickerColormap);
|
||||
|
||||
// draw icon and text
|
||||
V_DrawMappedPatch(x - iconPatch->width - 6, y - iconPatch->height + 4, 0, iconPatch, iconColormap);
|
||||
V_DrawThinString(middleLeftEdge - 1, y - 9, 0, text);
|
||||
}
|
||||
|
||||
void Y_DrawIntermissionHeader(fixed_t x, fixed_t y, boolean gotthrough, const char *headerstring, boolean showroundnum, boolean small)
|
||||
{
|
||||
const INT32 v_width = (small ? BASEVIDWIDTH/2 : BASEVIDWIDTH);
|
||||
|
|
@ -1840,7 +2026,7 @@ skiptallydrawer:
|
|||
goto finalcounter;
|
||||
|
||||
// Returns early if there's no roundqueue entries to draw
|
||||
Y_RoundQueueDrawer(&data, 0, true, false);
|
||||
Y_RoundQueueDrawer(&data, 0, true, false, false);
|
||||
|
||||
if (netgame)
|
||||
{
|
||||
|
|
@ -2081,7 +2267,9 @@ void Y_Ticker(void)
|
|||
// Basic bitch points
|
||||
if (data.increase[data.num[q]])
|
||||
{
|
||||
if (--data.increase[data.num[q]])
|
||||
data.increase[data.num[q]] = std::max(data.increase[data.num[q]] - 3, 0);
|
||||
|
||||
if (data.increase[data.num[q]] != 0)
|
||||
kaching = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -2117,6 +2305,35 @@ boolean Y_ShouldDoIntermission(void)
|
|||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Y_GetIntermissionType
|
||||
//
|
||||
// Returns the intermission type from the current gametype.
|
||||
//
|
||||
intertype_t Y_GetIntermissionType(void)
|
||||
{
|
||||
intertype_t ret = static_cast<intertype_t>(gametypes[gametype]->intermission);
|
||||
|
||||
if (ret == int_scoreortimeattack)
|
||||
{
|
||||
UINT8 i = 0, nump = 0;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
nump++;
|
||||
}
|
||||
|
||||
ret = (nump < 2 ? int_time : int_score);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//
|
||||
// Y_DetermineIntermissionType
|
||||
//
|
||||
|
|
@ -2131,21 +2348,7 @@ void Y_DetermineIntermissionType(void)
|
|||
return;
|
||||
}
|
||||
|
||||
// set initially
|
||||
intertype = static_cast<intertype_t>(gametypes[gametype]->intermission);
|
||||
|
||||
// special cases
|
||||
if (intertype == int_scoreortimeattack)
|
||||
{
|
||||
UINT8 i = 0, nump = 0;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator)
|
||||
continue;
|
||||
nump++;
|
||||
}
|
||||
intertype = (nump < 2 ? int_time : int_score);
|
||||
}
|
||||
intertype = Y_GetIntermissionType();
|
||||
}
|
||||
|
||||
static UINT8 Y_PlayersBestPossiblePosition(player_t *const player)
|
||||
|
|
@ -2433,9 +2636,10 @@ void Y_StartIntermission(void)
|
|||
}
|
||||
|
||||
K_CashInPowerLevels();
|
||||
SV_BumpMatchStats();
|
||||
}
|
||||
|
||||
SV_BumpMatchStats();
|
||||
|
||||
if (!timer)
|
||||
{
|
||||
Y_EndIntermission();
|
||||
|
|
|
|||
|
|
@ -54,16 +54,14 @@ void Y_Ticker(void);
|
|||
|
||||
// Specific sub-drawers
|
||||
void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset);
|
||||
void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, boolean widescreen);
|
||||
void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, boolean widescreen, boolean adminmode);
|
||||
void Y_DrawIntermissionButton(INT32 startslide, INT32 through, boolean widescreen);
|
||||
void Y_DrawRankMode(INT32 x, INT32 y, boolean center);
|
||||
|
||||
void Y_StartIntermission(void);
|
||||
void Y_MidIntermission(void);
|
||||
void Y_EndIntermission(void);
|
||||
|
||||
boolean Y_ShouldDoIntermission(void);
|
||||
void Y_DetermineIntermissionType(void);
|
||||
|
||||
void Y_PlayIntermissionMusic(void);
|
||||
|
||||
boolean Y_IntermissionPlayerLock(void);
|
||||
|
|
@ -78,6 +76,10 @@ typedef enum
|
|||
|
||||
extern intertype_t intertype;
|
||||
|
||||
boolean Y_ShouldDoIntermission(void);
|
||||
intertype_t Y_GetIntermissionType(void);
|
||||
void Y_DetermineIntermissionType(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue