Merge pull request 'Cherries™️ Part 2' (#175) from cherries into blankart-dev

Reviewed-on: https://codeberg.org/NepDisk/blankart/pulls/175
This commit is contained in:
NepDisk 2025-10-24 20:20:57 +02:00
commit 65cea70c05
35 changed files with 1038 additions and 588 deletions

View file

@ -143,6 +143,7 @@ k_director.c
k_follower.c
k_mapuser.c
k_stats.c
k_specialstage.c
h_timers.cpp
stun.c
lonesha256.c

View file

@ -90,6 +90,15 @@ CV_PossibleValue_t kartspeed_cons_t[] = {
{KARTSPEED_EXPERT, "Expert"},
{0, NULL}
};
CV_PossibleValue_t gpdifficulty_cons_t[] = {
{KARTSPEED_EASY, "Easy"},
{KARTSPEED_NORMAL, "Normal"},
{KARTSPEED_HARD, "Hard"},
{KARTSPEED_EXPERT, "Expert"},
{KARTGP_MASTER, "Master"},
{KARTGP_NIGHTMARE, "Nightmare"},
{0, NULL}
};
consvar_t cv_resetnetvars = CVAR_INIT ("resetnetvars", "Off", CV_SAVE, CV_OnOff, NULL);
@ -2102,6 +2111,15 @@ static void CV_SetCVar(consvar_t *var, const char *value, boolean stealth)
return;
}
if (var == &cv_kartspeed && !M_SecretUnlocked(SECRET_HARDSPEED))
{
if (!stricmp(value, "Hard") || !stricmp(value, "Expert") || atoi(value) >= KARTSPEED_HARD)
{
CONS_Printf(M_GetText("You haven't unlocked this yet!\n"));
return;
}
}
if (var == &cv_forceskin)
{
INT32 skin = R_SkinAvailable(value);
@ -2307,9 +2325,14 @@ void CV_AddValue(consvar_t *var, INT32 increment)
if (var->PossibleValue[max].value == var->value)
currentindice = max;
if ((var == &cv_kartspeed || var == &cv_kartbattlespeed) && !M_SecretUnlocked(SECRET_HARDSPEED))
if (var->PossibleValue == kartspeed_cons_t || var->PossibleValue == gpdifficulty_cons_t)
{
max = (M_SecretUnlocked(SECRET_HARDSPEED) ? 5 : 3);
if (!M_SecretUnlocked(SECRET_HARDSPEED))
{
max = KARTSPEED_NORMAL+1;
if (var->PossibleValue == kartspeed_cons_t)
max++; // Accommodate KARTSPEED_AUTO
}
}
#ifdef PARANOIA
if (currentindice == -1)

View file

@ -187,7 +187,7 @@ extern CV_PossibleValue_t CV_Natural[];
#define KARTSPEED_EXPERT 3
#define KARTGP_MASTER 4 // Not a speed setting, gives hard speed with maxed out bots
#define KARTGP_NIGHTMARE 5 // Not a speed setting, gives expert speed with maxed out bots
extern CV_PossibleValue_t kartspeed_cons_t[];
extern CV_PossibleValue_t kartspeed_cons_t[], gpdifficulty_cons_t[];
// Invincibility types.
#define KARTINVIN_LEGACY 0
#define KARTINVIN_ALTERN 1

View file

@ -4119,30 +4119,22 @@ void SV_StopServer(void)
}
// called at singleplayer start and stopdemo
void SV_StartSinglePlayerServer(void)
void SV_StartSinglePlayerServer(INT32 dogametype, boolean donetgame)
{
INT32 lastgametype = gametype;
server = true;
netgame = false;
multiplayer = false;
multiplayer = (modeattacking == ATTACKING_NONE) && !grandprixinfo.gp; // G: no multiplayer in GP!
if ((modeattacking == ATTACKING_ITEMBREAK) || (bossinfo.boss == true))
{
G_SetGametype(GT_BATTLE);
}
else
{
G_SetGametype(GT_RACE);
}
netgame = false; // so setting timelimit works... (XD_NETVAR doesn't play nice with SV_StopServer)
G_SetGametype(dogametype);
if (gametype != lastgametype)
D_GameTypeChanged(lastgametype);
netgame = donetgame;
// no more tic the game with this settings!
SV_StopServer();
if (splitscreen)
multiplayer = true;
}
static void SV_SendRefuse(INT32 node, const char *reason)

View file

@ -509,7 +509,7 @@ typedef enum
void NetKeepAlive(void);
void NetUpdate(void);
void SV_StartSinglePlayerServer(void);
void SV_StartSinglePlayerServer(INT32 dogametype, boolean donetgame);
boolean SV_SpawnServer(void);
void SV_StopServer(void);
void SV_ResetServer(void);

View file

@ -81,6 +81,7 @@
#include "doomstat.h"
#include "m_random.h" // P_ClearRandom
#include "acs/interface.h"
#include "k_specialstage.h"
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
@ -1188,6 +1189,9 @@ void D_StartTitle(void)
// Reset boss info
K_ResetBossInfo();
// Reset Special Stage
K_ResetSpecialStage();
// empty maptol so mario/etc sounds don't play in sound test when they shouldn't
maptol = 0;
@ -2027,34 +2031,20 @@ void D_SRB2Main(void)
INT16 newskill = -1;
const char *sskill = M_GetNextParm();
const char *masterstr = "Master";
const char *nightmarestr = "NIGHTMARE";
if (!strcasecmp(masterstr, sskill))
for (j = 0; gpdifficulty_cons_t[j].strvalue; j++)
{
newskill = KARTGP_MASTER;
}
else if (!strcasecmp(nightmarestr, sskill))
{
newskill = KARTGP_NIGHTMARE;
}
else
{
for (j = 0; kartspeed_cons_t[j].strvalue; j++)
if (!strcasecmp(gpdifficulty_cons_t[j].strvalue, sskill))
{
if (!strcasecmp(kartspeed_cons_t[j].strvalue, sskill))
{
newskill = (INT16)kartspeed_cons_t[j].value;
break;
}
newskill = (INT16)gpdifficulty_cons_t[j].value;
break;
}
}
if (!kartspeed_cons_t[j].strvalue) // reached end of the list with no match
{
j = atoi(sskill); // assume they gave us a skill number, which is okay too
if (j >= KARTSPEED_EASY && j <= KARTGP_NIGHTMARE)
newskill = (INT16)j;
}
if (!gpdifficulty_cons_t[j].strvalue) // reached end of the list with no match
{
j = atoi(sskill); // assume they gave us a skill number, which is okay too
if (j >= KARTSPEED_EASY && j <= KARTGP_NIGHTMARE)
newskill = (INT16)j;
}
if (grandprixinfo.gp == true)

View file

@ -67,6 +67,7 @@
#include "deh_tables.h"
#include "m_perfstats.h"
#include "g_party.h"
#include "k_specialstage.h"
#define CV_RESTRICT CV_NETVAR
@ -632,6 +633,7 @@ consvar_t cv_kartdebugcheckpoint = CVAR_INIT ("kartdebugcheckpoint", "Off", 0, C
consvar_t cv_kartdebugnodes = CVAR_INIT ("kartdebugnodes", "Off", 0, CV_OnOff, NULL);
consvar_t cv_kartdebugcolorize = CVAR_INIT ("kartdebugcolorize", "Off", 0, CV_OnOff, NULL);
consvar_t cv_kartdebugdirector = CVAR_INIT ("kartdebugdirector", "Off", 0, CV_OnOff, NULL);
consvar_t cv_gptest = CVAR_INIT ("gptest", "Off", CV_CHEAT|CV_NETVAR, CV_OnOff, NULL);
static CV_PossibleValue_t votetime_cons_t[] = {{10, "MIN"}, {3600, "MAX"}, {0, NULL}};
consvar_t cv_votetime = CVAR_INIT ("votetime", "20", CV_NETVAR, votetime_cons_t, NULL);
@ -657,9 +659,9 @@ consvar_t cv_overtime = CVAR_INIT ("overtime", "Yes", CV_NETVAR|CV_CHEAT, CV_Yes
consvar_t cv_rollingdemos = CVAR_INIT ("rollingdemos", "On", CV_SAVE, CV_OnOff, NULL);
static CV_PossibleValue_t pointlimit_cons_t[] = {{1, "MIN"}, {MAXSCORE, "MAX"}, {0, "None"}, {0, NULL}};
consvar_t cv_pointlimit = CVAR_INIT ("pointlimit", "None", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, pointlimit_cons_t, PointLimit_OnChange);
consvar_t cv_pointlimit = CVAR_INIT ("pointlimit", "None", CV_NETVAR|CV_CALL|CV_NOINIT, pointlimit_cons_t, PointLimit_OnChange);
static CV_PossibleValue_t timelimit_cons_t[] = {{1, "MIN"}, {99999, "MAX"}, {0, "None"}, {0, NULL}};
consvar_t cv_timelimit = CVAR_INIT ("timelimit", "None", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, timelimit_cons_t, TimeLimit_OnChange);
consvar_t cv_timelimit = CVAR_INIT ("timelimit", "None", CV_NETVAR|CV_CALL|CV_NOINIT, timelimit_cons_t, TimeLimit_OnChange);
static CV_PossibleValue_t numlaps_cons_t[] = {{0, "MIN"}, {MAX_LAPS, "MAX"}, {-1, "Map default"}, {0, NULL}};
consvar_t cv_numlaps = CVAR_INIT ("numlaps", "Map default", CV_NETVAR|CV_CALL|CV_CHEAT, numlaps_cons_t, NumLaps_OnChange);
@ -2889,6 +2891,10 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r
{
pencoremode = bossinfo.encore;
}
else if (specialStage.active == true)
{
pencoremode = specialStage.encore;
}
else if (grandprixinfo.gp == true)
{
pencoremode = grandprixinfo.encore;
@ -3280,6 +3286,7 @@ static void Command_Map_f(void)
if (newgametype == GT_BATTLE)
{
grandprixinfo.gp = false;
specialStage.active = false;
K_ResetBossInfo();
if (mapheaderinfo[newmapnum-1] &&
@ -3289,76 +3296,76 @@ static void Command_Map_f(void)
bossinfo.encore = newencoremode;
}
}
else // default GP
else
{
grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value);
grandprixinfo.masterbots = false;
if (option_skill)
if (mapheaderinfo[newmapnum-1] &&
mapheaderinfo[newmapnum-1]->typeoflevel & TOL_SPECIAL) // Special Stage
{
const char *masterstr = "Master";
const char *nightmarestr = "NIGHTMARE";
const char *skillname = COM_Argv(option_skill + 1);
INT32 newskill = -1;
INT32 j;
grandprixinfo.gp = false;
bossinfo.boss = false;
if (!strcasecmp(masterstr, skillname))
specialStage.active = true;
specialStage.encore = newencoremode;
}
else // default GP
{
grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value);
grandprixinfo.masterbots = false;
if (option_skill)
{
newskill = KARTGP_MASTER;
}
else if (!strcasecmp(nightmarestr, skillname))
{
newskill = KARTGP_NIGHTMARE;
}
else
{
for (j = 0; kartspeed_cons_t[j].strvalue; j++)
const char *skillname = COM_Argv(option_skill + 1);
INT32 newskill = -1;
INT32 j;
for (j = 0; gpdifficulty_cons_t[j].strvalue; j++)
{
if (!strcasecmp(kartspeed_cons_t[j].strvalue, skillname))
if (!strcasecmp(gpdifficulty_cons_t[j].strvalue, skillname))
{
newskill = (INT16)kartspeed_cons_t[j].value;
newskill = (INT16)gpdifficulty_cons_t[j].value;
break;
}
}
if (!kartspeed_cons_t[j].strvalue) // reached end of the list with no match
if (!gpdifficulty_cons_t[j].strvalue) // reached end of the list with no match
{
INT32 num = atoi(COM_Argv(option_skill + 1)); // assume they gave us a skill number, which is okay too
if (num >= KARTSPEED_EASY && num <= KARTGP_NIGHTMARE)
newskill = (INT16)num;
}
if (newskill != -1)
{
if (newskill == KARTGP_MASTER)
{
grandprixinfo.gamespeed = KARTSPEED_HARD;
grandprixinfo.masterbots = true;
}
else if (newskill == KARTGP_NIGHTMARE)
{
grandprixinfo.gamespeed = KARTSPEED_EXPERT;
grandprixinfo.masterbots = true;
}
else
{
grandprixinfo.gamespeed = newskill;
grandprixinfo.masterbots = false;
}
}
}
if (newskill != -1)
{
if (newskill == KARTGP_MASTER)
{
grandprixinfo.gamespeed = KARTSPEED_HARD;
grandprixinfo.masterbots = true;
}
else if (newskill == KARTGP_NIGHTMARE)
{
grandprixinfo.gamespeed = KARTSPEED_EXPERT;
grandprixinfo.masterbots = true;
}
else
{
grandprixinfo.gamespeed = newskill;
grandprixinfo.masterbots = false;
}
}
grandprixinfo.encore = newencoremode;
grandprixinfo.gp = true;
grandprixinfo.roundnum = 0;
grandprixinfo.cup = NULL;
grandprixinfo.wonround = false;
bossinfo.boss = false;
specialStage.active = false;
grandprixinfo.initalize = true;
}
grandprixinfo.encore = newencoremode;
grandprixinfo.gp = true;
grandprixinfo.roundnum = 0;
grandprixinfo.cup = NULL;
grandprixinfo.wonround = false;
bossinfo.boss = false;
grandprixinfo.initalize = true;
}
}
@ -4504,7 +4511,7 @@ void Schedule_Run(void)
return;
}
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
// Don't engage in automation while in a restricted context.
return;
@ -4640,7 +4647,7 @@ void Automate_Run(automateEvents_t type)
return;
}
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
// Don't engage in automation while in a restricted context.
return;
@ -5428,7 +5435,12 @@ void ItemFinder_OnChange(void)
*/
static void PointLimit_OnChange(void)
{
// Don't allow pointlimit in Single Player/Co-Op/Race!
if (K_CanChangeRules(false) == false)
{
return;
}
// Don't allow pointlimit in non-pointlimited gametypes!
if (server && Playing() && !(gametyperules & GTR_POINTLIMIT))
{
if (cv_pointlimit.value)
@ -5443,7 +5455,7 @@ static void PointLimit_OnChange(void)
cv_pointlimit.value,
cv_pointlimit.value > 1 ? "s" : "");
}
else if (netgame || multiplayer)
else
CONS_Printf(M_GetText("Point limit disabled\n"));
}
@ -5478,26 +5490,40 @@ UINT32 secretextratime = 0;
*/
static void TimeLimit_OnChange(void)
{
// Don't allow timelimit in Single Player/Co-Op/Race!
if (server && Playing() && cv_timelimit.value != 0 && (bossinfo.boss || !(gametyperules & GTR_TIMELIMIT)))
if (K_CanChangeRules(false) == false)
{
CV_SetValue(&cv_timelimit, 0);
return;
}
if (cv_timelimit.value != 0)
if (gamestate == GS_LEVEL && leveltime < starttime)
{
CONS_Printf(M_GetText("Rounds will end after %d minute%s.\n"),cv_timelimit.value,cv_timelimit.value == 1 ? "" : "s"); // Graue 11-17-2003
timelimitintics = cv_timelimit.value * (60*TICRATE);
if (cv_timelimit.value)
{
CONS_Printf(M_GetText("Time limit has been set to %d minute%s.\n"), cv_timelimit.value,cv_timelimit.value == 1 ? "" : "s");
}
else
{
CONS_Printf(M_GetText("Time limit has been disabled.\n"));
}
// Note the deliberate absence of any code preventing
// pointlimit and timelimit from being set simultaneously.
// Some people might like to use them together. It works.
}
timelimitintics = cv_timelimit.value * (60*TICRATE);
extratimeintics = secretextratime = 0;
#ifdef HAVE_DISCORDRPC
DRPC_UpdatePresence();
DRPC_UpdatePresence();
#endif
}
else
{
if (cv_timelimit.value)
{
CONS_Printf(M_GetText("Time limit will be %d minute%s next round.\n"), cv_timelimit.value,cv_timelimit.value == 1 ? "" : "s");
}
else
{
CONS_Printf(M_GetText("Time limit will be disabled next round.\n"));
}
}
}
/** Adjusts certain settings to match a changed gametype.
@ -5523,7 +5549,7 @@ void D_GameTypeChanged(INT32 lastgametype)
}
// Only do the following as the server, not as remote admin.
// There will always be a server, and this only needs to be done once.
if (server && (multiplayer || netgame))
if (server && multiplayer)
{
if (!cv_timelimit.changed) // user hasn't changed limits
{
@ -5534,27 +5560,6 @@ void D_GameTypeChanged(INT32 lastgametype)
CV_SetValue(&cv_pointlimit, pointlimits[gametype]);
}
}
/* -- no longer useful
else if (!multiplayer && !netgame)
{
G_SetGametype(GT_RACE);
}
*/
// reset timelimit and pointlimit in race/coop, prevent stupid cheats
if (server)
{
if (!(gametyperules & GTR_TIMELIMIT))
{
if (cv_timelimit.value)
CV_SetValue(&cv_timelimit, 0);
}
if (!(gametyperules & GTR_POINTLIMIT))
{
if (cv_pointlimit.value)
CV_SetValue(&cv_pointlimit, 0);
}
}
// don't retain teams in other modes or between changes from ctf to team match.
// also, stop any and all forms of team scrambling that might otherwise take place.
@ -7313,6 +7318,11 @@ static void Command_ShowTime_f(void)
// SRB2Kart: On change messages
static void NumLaps_OnChange(void)
{
if (K_CanChangeRules(false) == false)
{
return;
}
if (gamestate == GS_LEVEL)
{
numlaps = K_RaceLapCount(gamemap - 1);
@ -7341,12 +7351,12 @@ static void NumLaps_OnChange(void)
static void KartFrantic_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
if (leveltime < starttime)
if (gamestate == GS_LEVEL && leveltime < starttime)
{
CONS_Printf(M_GetText("Frantic items has been set to %s.\n"), cv_kartfrantic.value ? M_GetText("on") : M_GetText("off"));
franticitems = (boolean)cv_kartfrantic.value;
@ -7359,19 +7369,12 @@ static void KartFrantic_OnChange(void)
static void KartSpeed_OnChange(void)
{
if (!M_SecretUnlocked(SECRET_HARDSPEED) && (cv_kartspeed.value == KARTSPEED_HARD || cv_kartspeed.value == KARTSPEED_EXPERT))
{
CONS_Printf(M_GetText("You haven't earned this yet.\n"));
CV_StealthSet(&cv_kartspeed, cv_kartspeed.defaultvalue);
return;
}
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
if (leveltime < starttime && cv_kartspeed.value != KARTSPEED_AUTO)
if (gamestate == GS_LEVEL && leveltime < starttime && cv_kartspeed.value != KARTSPEED_AUTO)
{
CONS_Printf(M_GetText("Race speed has been changed to \"%s\".\n"), cv_kartspeed.string);
gamespeed = (UINT8)cv_kartspeed.value;
@ -7391,7 +7394,7 @@ static void KartBattleSpeed_OnChange(void)
return;
}
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
@ -7409,7 +7412,7 @@ static void KartBattleSpeed_OnChange(void)
static void KartEncore_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
@ -7419,7 +7422,7 @@ static void KartEncore_OnChange(void)
static void KartComeback_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
@ -7442,7 +7445,7 @@ static void KartEliminateLast_OnChange(void)
static void KartRings_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
@ -7459,7 +7462,7 @@ static void KartRings_OnChange(void)
static void KartPurpleDrift_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
@ -7492,7 +7495,7 @@ static void KartPurpleDrift_OnChange(void)
static void KartStacking_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
@ -7525,7 +7528,7 @@ static void KartStacking_OnChange(void)
static void KartChaining_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
@ -7558,7 +7561,7 @@ static void KartChaining_OnChange(void)
static void KartSlipdash_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
@ -7591,7 +7594,7 @@ static void KartSlipdash_OnChange(void)
static void KartSlopeBoost_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
@ -7624,7 +7627,7 @@ static void KartSlopeBoost_OnChange(void)
static void KartDrafting_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
@ -7657,7 +7660,7 @@ static void KartDrafting_OnChange(void)
static void KartAirDrop_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
@ -7690,7 +7693,7 @@ static void KartAirDrop_OnChange(void)
static void KartItemLitter_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
@ -7723,7 +7726,7 @@ static void KartItemLitter_OnChange(void)
static void KartAntiBump_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
@ -7747,7 +7750,7 @@ static void KartAntiBump_OnChange(void)
static void KartItemBreaker_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
@ -7769,7 +7772,7 @@ static void KartItemBreaker_OnChange(void)
static void KartInvinType_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
@ -7787,7 +7790,7 @@ static void KartInvinType_OnChange(void)
static void KartBumpSpark_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}

View file

@ -224,6 +224,7 @@ extern consvar_t cv_kartdebugitem, cv_kartdebugamount, cv_kartdebugdistribution,
extern consvar_t cv_kartdebugshrink;
extern consvar_t cv_kartdebugcheckpoint, cv_kartdebugnodes, cv_kartdebugcolorize, cv_kartdebugdirector;
extern consvar_t cv_kartdebugwaypoints, cv_kartdebuglap, cv_kartdebugbot, cv_kartdebugcluster, cv_kartdebugrings;
extern consvar_t cv_gptest;
extern consvar_t cv_itemfinder;

View file

@ -258,6 +258,9 @@ typedef enum
khud_lapanimation, // Used to show the lap start wing logo animation
khud_laphand, // Lap hand gfx to use; 0 = none, 1 = :ok_hand:, 2 = :thumbs_up:, 3 = :thumps_down:
// Big text
khud_finish, // Set when completing a round
// Camera
khud_boostcam, // Camera push forward on boost
khud_destboostcam, // Ditto

View file

@ -3632,7 +3632,15 @@ void readcupheader(MYFILE *f, cupheader_t *cup)
}
else if (fastcmp(word, "LEVELLIST"))
{
cup->numlevels = 0;
while (cup->numlevels > 0)
{
cup->numlevels--;
Z_Free(cup->levellist[cup->numlevels]);
cup->levellist[cup->numlevels] = NULL;
if (cup->cachedlevels[cup->numlevels] == NEXTMAP_INVALID)
continue;
mapheaderinfo[cup->cachedlevels[cup->numlevels]]->cup = NULL;
}
tmp = strtok(word2,",");
do {
@ -3649,11 +3657,13 @@ void readcupheader(MYFILE *f, cupheader_t *cup)
}
else if (fastcmp(word, "BONUSGAME"))
{
Z_Free(cup->levellist[CUPCACHE_BONUS]);
cup->levellist[CUPCACHE_BONUS] = Z_StrDup(word2);
cup->cachedlevels[CUPCACHE_BONUS] = NEXTMAP_INVALID;
}
else if (fastcmp(word, "SPECIALSTAGE"))
{
Z_Free(cup->levellist[CUPCACHE_SPECIAL]);
cup->levellist[CUPCACHE_SPECIAL] = Z_StrDup(word2);
cup->cachedlevels[CUPCACHE_SPECIAL] = NEXTMAP_INVALID;
}

View file

@ -533,6 +533,11 @@ extern int compuncommitted;
/// Experimental attempts at preventing MF_PAPERCOLLISION objects from getting stuck in walls.
//#define PAPER_COLLISIONCORRECTION
#ifdef DEVELOP
// Easily make it so that overtime works offline
#define TESTOVERTIMEINFREEPLAY
#endif
/// FINALLY some real clipping that doesn't make walls dissappear AND speeds the game up
/// (that was the original comment from SRB2CB, sadly it is a lie and actually slows game down)
/// on the bright side it fixes some weird issues with translucent walls

View file

@ -530,6 +530,7 @@ enum TypeOfLevel
TOL_RACE = 0x0001, ///< Race
TOL_BATTLE = 0x0002, ///< Battle
TOL_BOSS = 0x0004, ///< Boss (variant of battle, but forbidden)
TOL_SPECIAL = 0x0008, ///< Special Stage (variant of race, but forbidden)
// Compat
TOL_COMPAT1 = 0x0008, ///< For compat. Handles all the unused kart v1 types.

View file

@ -1876,10 +1876,8 @@ void F_EndCutScene(void)
F_StartGameEvaluation();
else if (cutnum == introtoplay-1)
D_StartTitle();
else if (nextmap < NEXTMAP_SPECIAL)
G_NextLevel();
else
G_EndGame();
G_NextLevel();
}
}

View file

@ -59,6 +59,7 @@
#include "k_color.h"
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_specialstage.h"
#include "k_bot.h"
#include "k_odds.h"
#include "doomstat.h"
@ -2709,6 +2710,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
INT32 nextcheck; // Distace to Next Legacy Checkpoint
INT32 exiting;
INT32 khudfinish;
INT32 khudcardanimation;
INT16 totalring;
UINT8 laps;
@ -2825,6 +2827,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
latestlap = 0;
roundscore = 0;
exiting = 0;
khudfinish = 0;
khudcardanimation = 0;
starpostx =0;
starposty = 0;
@ -2835,7 +2838,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
starposttime = 0;
prevcheck = 0;
nextcheck = 0;
xtralife = 0;
for (i = 0; i < LAP__MAX; i++)
{
@ -2883,7 +2885,16 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
roundscore = players[player].roundscore;
exiting = players[player].exiting;
khudcardanimation = (exiting > 0) ? players[player].karthud[khud_cardanimation] : 0;
if (exiting > 0)
{
khudfinish = players[player].karthud[khud_finish];
khudcardanimation = players[player].karthud[khud_cardanimation];
}
else
{
khudfinish = 0;
khudcardanimation = 0;
}
starpostx = players[player].starpostx;
starposty = players[player].starposty;
@ -2961,6 +2972,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->nextcheck = nextcheck;
p->exiting = exiting;
p->karthud[khud_finish] = khudfinish;
p->karthud[khud_cardanimation] = khudcardanimation;
p->laps = laps;
@ -3798,6 +3810,7 @@ tolinfo_t TYPEOFLEVEL[NUMTOLNAMES] = {
{"RACE",TOL_RACE},
{"BATTLE",TOL_BATTLE},
{"BOSS",TOL_BOSS},
{"SPECIAL",TOL_SPECIAL},
{"TV",TOL_TV},
// Compat stuff
@ -4324,7 +4337,7 @@ static void G_UpdateVisited(void)
return;
// Update visitation flags
maprecord_t *record = G_AllocateMapRecord(G_BuildMapName(gamemap));
maprecord_t *record = G_AllocateMapRecord(G_BuildMapName(prevmap+1));
record->visited |= MV_BEATEN;
if (encoremode == true)
record->visited |= MV_ENCORE;
@ -4372,6 +4385,7 @@ static void G_HandleSaveLevel(void)
static INT16 G_GetNextMap(boolean advancemap)
{
boolean spec = G_IsSpecialStage(prevmap+1);
INT32 i;
INT16 newmap, curmap = gamestate == GS_LEVEL ? gamemap-1 : prevmap;
@ -4383,20 +4397,104 @@ static INT16 G_GetNextMap(boolean advancemap)
}
else if (grandprixinfo.gp == true)
{
if (grandprixinfo.roundnum == 0 || grandprixinfo.cup == NULL) // Single session
// G: oh dear, this whole GP block has loads of side effects...
// for now, just repeat the same map for "map +"
// you're not supposed to use that in GP anyways
if (!advancemap || // ...right?
grandprixinfo.roundnum == 0 || grandprixinfo.cup == NULL) // Single session
{
newmap = curmap; // Same map
}
else
{
if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) // On final map
INT32 lastgametype = gametype;
// If we're in a GP event, don't immediately follow it up with another.
// I also suspect this will not work with online GP so I'm gonna prevent it right now.
// The server might have to communicate eventmode (alongside other GP data) in XD_MAP later.
if (netgame || grandprixinfo.eventmode != GPEVENT_NONE)
{
grandprixinfo.eventmode = GPEVENT_NONE;
G_SetGametype(GT_RACE);
if (gametype != lastgametype)
D_GameTypeChanged(lastgametype);
specialStage.active = false;
bossinfo.boss = false;
}
// Special stage
else if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels)
{
INT16 totaltotalring = 0;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].spectator)
continue;
if (players[i].bot)
continue;
totaltotalring += players[i].totalring;
}
if (totaltotalring >= 50)
{
const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_SPECIAL];
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum]
&& mapheaderinfo[cupLevelNum]->typeoflevel & (TOL_SPECIAL|TOL_BOSS|TOL_BATTLE))
{
grandprixinfo.eventmode = GPEVENT_SPECIAL;
newmap = cupLevelNum;
}
}
}
else if (grandprixinfo.roundnum == (grandprixinfo.cup->numlevels+1)/2) // 3 for a 5-map cup
{
// todo any other condition?
{
const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_BONUS];
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum]
&& mapheaderinfo[cupLevelNum]->typeoflevel & (TOL_BOSS|TOL_BATTLE))
{
grandprixinfo.eventmode = GPEVENT_BONUS;
newmap = cupLevelNum;
}
}
}
if (grandprixinfo.eventmode != GPEVENT_NONE)
{
// nextmap is set above
const INT32 newtol = mapheaderinfo[newmap]->typeoflevel;
if (newtol & TOL_SPECIAL)
{
specialStage.active = true;
specialStage.encore = grandprixinfo.encore;
}
else //(if newtol & (TOL_BATTLE|TOL_BOSS)) -- safe to assume??
{
G_SetGametype(GT_BATTLE);
if (gametype != lastgametype)
D_GameTypeChanged(lastgametype);
if (newtol & TOL_BOSS)
{
K_ResetBossInfo();
bossinfo.boss = true;
bossinfo.encore = grandprixinfo.encore;
}
}
}
else if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) // On final map
{
newmap = NEXTMAP_CEREMONY; // ceremonymap
}
else
{
// Proceed to next map
const INT32 cupLevelNum =grandprixinfo.cup->cachedlevels[grandprixinfo.roundnum];
const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[grandprixinfo.roundnum];
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum])
{
@ -4478,14 +4576,7 @@ static INT16 G_GetNextMap(boolean advancemap)
// Didn't get a nextmap before reaching the end?
if (gettingresult != 2)
{
if (marathonmode)
{
newmap = NEXTMAP_CEREMONY; // ceremonymap
}
else
{
newmap = NEXTMAP_TITLE;
}
newmap = NEXTMAP_CEREMONY; // ceremonymap
}
}
else
@ -4513,19 +4604,39 @@ static INT16 G_GetNextMap(boolean advancemap)
newmap = cm;
}
if (advancemap && !marathonmode)
if (advancemap && K_CanChangeRules(true))
{
if (cv_advancemap.value == 0) // Stay on same map.
switch (cv_advancemap.value)
{
newmap = curmap;
}
else if (cv_advancemap.value == 2) // Go to random map.
{
newmap = G_RandMap(G_TOLFlag(gametype), curmap, 0, 0, NULL);
}
else if (nextmap >= NEXTMAP_SPECIAL) // Loop back around
{
newmap = G_GetFirstMapOfGametype(gametype);
case 0: // Stay on same map.
newmap = curmap;
break;
case 3: // Voting screen.
{
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].spectator)
continue;
break;
}
if (i != MAXPLAYERS)
{
newmap = NEXTMAP_VOTING;
break;
}
}
/* FALLTHRU */
case 2: // Go to random map.
newmap = G_RandMap(G_TOLFlag(gametype), curmap, 0, 0, NULL);
break;
default:
if (newmap >= NEXTMAP_SPECIAL) // Loop back around
{
newmap = G_GetFirstMapOfGametype(gametype);
}
break;
}
}
}
@ -4534,6 +4645,9 @@ static INT16 G_GetNextMap(boolean advancemap)
if (newmap == NEXTMAP_INVALID || (newmap < NEXTMAP_SPECIAL && (newmap >= nummapheaders || !mapheaderinfo[newmap] || mapheaderinfo[newmap]->lumpnum == LUMPERROR)))
I_Error("G_GetNextMap: Internal map ID %d not found (nummapheaders = %d)\n", newmap, nummapheaders);
if (!spec)
lastmap = newmap;
return newmap;
}
@ -4543,8 +4657,6 @@ static INT16 G_GetNextMap(boolean advancemap)
static void G_DoCompleted(void)
{
INT32 i, j = 0;
boolean spec = G_IsSpecialStage(gamemap);
SINT8 powertype = K_UsingPowerLevels();
if (modeattacking && pausedelay)
pausedelay = 0;
@ -4587,8 +4699,9 @@ static void G_DoCompleted(void)
}
}
// See Y_StartIntermission timer handling
if ((gametyperules & GTR_CIRCUIT) && ((multiplayer && demo.playback) || j == r_splitscreen+1) && (!K_CanChangeRules(false) || cv_inttime.value > 0))
// play some generic music if there's no win/cool/lose music going on (for exitlevel commands)
if ((gametyperules & GTR_CIRCUIT) && ((multiplayer && demo.playback) || j == r_splitscreen+1) && (cv_inttime.value > 0))
{
S_ChangeMusicInternal("racent", true);
S_ShowMusicCredit(-30*FRACUNIT, 5*TICRATE, V_SNAPTOTOP);
@ -4603,14 +4716,8 @@ static void G_DoCompleted(void)
if (!demo.playback)
{
nextmap = G_GetNextMap(true);
// Remember last map for when you come out of the special stage.
if (!spec)
lastmap = nextmap;
// Set up power level gametype scrambles
K_SetPowerLevelScrambles(powertype);
K_SetPowerLevelScrambles(K_UsingPowerLevels());
}
// If the current gametype has no intermission screen set, then don't start it.
@ -4621,7 +4728,6 @@ static void G_DoCompleted(void)
|| (intertype == int_none))
{
G_UpdateVisited();
G_HandleSaveLevel();
G_AfterIntermission();
}
else
@ -4629,7 +4735,6 @@ static void G_DoCompleted(void)
G_SetGamestate(GS_INTERMISSION);
Y_StartIntermission();
G_UpdateVisited();
G_HandleSaveLevel();
}
}
@ -4663,14 +4768,17 @@ void G_AfterIntermission(void)
return;
}
if (gamestate != GS_VOTING)
{
nextmap = G_GetNextMap(true);
G_HandleSaveLevel();
}
if (grandprixinfo.gp == true && mapheaderinfo[prevmap]->cutscenenum && !modeattacking && skipstats <= 1 && (gamecomplete || !(marathonmode & MA_NOCUTSCENES))) // Start a custom cutscene.
F_StartCustomCutscene(mapheaderinfo[prevmap]->cutscenenum-1, false, false);
else
{
if (nextmap < NEXTMAP_SPECIAL)
G_NextLevel();
else
G_EndGame();
G_NextLevel();
}
}
@ -4682,25 +4790,15 @@ void G_AfterIntermission(void)
//
void G_NextLevel(void)
{
if (gamestate != GS_VOTING)
if (nextmap >= NEXTMAP_SPECIAL)
{
if ((cv_advancemap.value == 3) && grandprixinfo.gp == false && bossinfo.boss == false && !modeattacking && !skipstats && (multiplayer || netgame))
{
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator)
{
gameaction = ga_startvote;
return;
}
}
}
forceresetplayers = false;
deferencoremode = (cv_kartencore.value == 1);
G_EndGame();
return;
}
forceresetplayers = false;
deferencoremode = (cv_kartencore.value == 1);
gameaction = ga_worlddone;
}
@ -4727,7 +4825,11 @@ static void G_DoWorldDone(void)
static void G_DoStartVote(void)
{
if (server)
{
if (gamestate == GS_VOTING)
I_Error("G_DoStartVote: NEXTMAP_VOTING causes recursive vote!");
D_SetupVote();
}
gameaction = ga_nothing;
}
@ -4805,8 +4907,12 @@ static void G_DoContinued(void)
// when something new is added.
void G_EndGame(void)
{
if (demo.recording && (modeattacking || demo.savemode != DSM_NOTSAVING))
G_SaveDemo();
// Handle voting
if (nextmap == NEXTMAP_VOTING)
{
gameaction = ga_startvote;
return;
}
// Only do evaluation and credits in singleplayer contexts
if (!netgame && grandprixinfo.gp == true)
@ -5492,6 +5598,7 @@ cleanup:
void G_DeferedInitNew(boolean pencoremode, INT32 map, INT32 pickedchar, UINT8 ssplayers, boolean FLS)
{
UINT16 color = SKINCOLOR_NONE;
INT32 dogametype;
paused = false;
@ -5502,8 +5609,17 @@ void G_DeferedInitNew(boolean pencoremode, INT32 map, INT32 pickedchar, UINT8 ss
G_ResetRandMapBuffer();
if ((modeattacking == ATTACKING_ITEMBREAK) || (bossinfo.boss == true))
{
dogametype = GT_BATTLE;
}
else
{
dogametype = GT_RACE;
}
// this leave the actual game if needed
SV_StartSinglePlayerServer();
SV_StartSinglePlayerServer(dogametype, false);
if (splitscreen != ssplayers)
{

View file

@ -48,7 +48,8 @@ typedef enum
NEXTMAP_EVALUATION = INT16_MAX-2,
NEXTMAP_CREDITS = INT16_MAX-3,
NEXTMAP_CEREMONY = INT16_MAX-4,
NEXTMAP_INVALID = INT16_MAX-5, // Always last (swap with NEXTMAP_RESERVED when removing that)
NEXTMAP_VOTING = INT16_MAX-5,
NEXTMAP_INVALID = INT16_MAX-6, // Always last
NEXTMAP_SPECIAL = NEXTMAP_INVALID
} nextmapspecial_t;

View file

@ -2510,7 +2510,7 @@ static void HU_DrawRankings(void)
if ((gametyperules & (GTR_TIMELIMIT|GTR_POINTLIMIT)) && !bossinfo.boss)
{
if ((gametyperules & GTR_TIMELIMIT) && cv_timelimit.value && timelimitintics > 0)
if ((gametyperules & GTR_TIMELIMIT) && timelimitintics > 0)
{
UINT32 timeval = (timelimitintics + starttime + 1 - leveltime);
if (timeval > timelimitintics+1)

View file

@ -261,7 +261,7 @@ void K_CheckBumpers(void)
winnerscoreadd -= players[i].roundscore;
}
if (K_CanChangeRules() == false)
if (K_CanChangeRules(true) == false)
{
if (nobumpers)
{
@ -274,7 +274,8 @@ void K_CheckBumpers(void)
if (!itembreaker && cv_kartitembreaker.value)
{
// Reset map to turn on battle capsules
D_MapChange(gamemap, gametype, encoremode, true, 0, false, false);
if (server)
D_MapChange(gamemap, gametype, encoremode, true, 0, false, false);
}
else
{
@ -486,22 +487,13 @@ void K_SpawnPlayerBattleBumpers(player_t *p)
}
}
void K_BattleInit(UINT8 numPlayers)
void K_BattleInit(boolean singleplayercontext)
{
if ((gametyperules & GTR_ITEMBREAKER) && !itembreaker && !bossinfo.boss)
if ((gametyperules & GTR_ITEMBREAKER) && singleplayercontext && !itembreaker && !bossinfo.boss)
{
if (modeattacking != ATTACKING_ITEMBREAK)
{
if (!cv_kartitembreaker.value)
goto afteritembreaker;
if (numPlayers > 1)
goto afteritembreaker;
}
itembreaker = true;
if (!(K_CanChangeRules(true) && !cv_kartitembreaker.value))
itembreaker = true;
}
afteritembreaker:
if (gametyperules & GTR_BUMPERS)
{

View file

@ -21,7 +21,7 @@ void K_CheckBumpers(void);
UINT8 K_NumEmeralds(player_t *player);
void K_RunPaperItemSpawners(void);
void K_SpawnPlayerBattleBumpers(player_t *p);
void K_BattleInit(UINT8 numPlayers);
void K_BattleInit(boolean singleplayercontext);
void K_RespawnBattleBoxes(void);
#ifdef __cplusplus

View file

@ -259,7 +259,7 @@ void K_UpdateMatchRaceBots(void)
{
difficulty = 0;
}
else if (K_CanChangeRules() == false)
else if (K_CanChangeRules(true) == false)
{
difficulty = 0;
}

View file

@ -12,6 +12,7 @@
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_specialstage.h"
#include "doomdef.h"
#include "d_player.h"
#include "g_game.h"
@ -737,18 +738,12 @@ void K_PlayerLoseLife(player_t *player)
}
/*--------------------------------------------------
boolean K_CanChangeRules(void)
boolean K_CanChangeRules(boolean allowdemos)
See header file for description.
--------------------------------------------------*/
boolean K_CanChangeRules(void)
boolean K_CanChangeRules(boolean allowdemos)
{
if (demo.playback)
{
// We've already got our important settings!
return false;
}
if (grandprixinfo.gp == true && grandprixinfo.roundnum > 0)
{
// Don't cheat the rules of the GP!
@ -761,11 +756,29 @@ boolean K_CanChangeRules(void)
return false;
}
if (modeattacking == true)
if (specialStage.active == true)
{
// Don't cheat special stages!
return false;
}
if (marathonmode)
{
// Don't cheat the endurance challenge!
return false;
}
if (modeattacking != ATTACKING_NONE)
{
// Don't cheat the rules of Time Trials!
return false;
}
if (!allowdemos && demo.playback)
{
// We've already got our important settings!
return false;
}
return true;
}

View file

@ -171,19 +171,19 @@ void K_PlayerLoseLife(player_t *player);
/*--------------------------------------------------
boolean K_CanChangeRules(void);
boolean K_CanChangeRules(boolean allowdemos);
Returns whenver or not the server is allowed
to change the game rules.
Input Arguments:-
None
allowdemos - permits this behavior during demo playback
Return:-
true if can change important gameplay rules, otherwise false.
--------------------------------------------------*/
boolean K_CanChangeRules(void);
boolean K_CanChangeRules(boolean allowdemos);
#ifdef __cplusplus
} // extern "C"

View file

@ -1628,37 +1628,38 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UI
// TIME_Y = 6; // 6
tic_t worktime;
boolean dontdraw = false;
boolean overtime = false;
UINT8 *textcolor = 0;
fixed_t jitter = 0;
boolean overtime = false, fastjitter = false, countdown = false;
UINT8 *textcolor = NULL;
INT32 splitflags = 0;
if (!mode)
{
splitflags = V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_SPLITSCREEN;
#ifndef TESTOVERTIMEINFREEPLAY
if (itembreaker) // capsules override any time limit settings
;
else
#endif
if (bossinfo.boss == true)
;
else if (timelimitintics > 0 && (gametyperules & GTR_TIMELIMIT)) // TODO
{
if (drawtime >= (timelimitintics - 5*TICRATE) && ((drawtime*4)/TICRATE) % 2 == 0)
{
dontdraw = true;
}
if (timelimitintics > 0)
{
if (drawtime >= timelimitintics)
{
overtime = true;
// drawtime = 0;
jitter = 1;
}
else
{
drawtime = timelimitintics - drawtime;
countdown = true;
if (secretextratime)
;
else if (extratimeintics)
{
jitter = 2;
fastjitter = true;
}
else if (drawtime <= 5*TICRATE)
{
jitter = ((drawtime <= 3*TICRATE) && (((drawtime-1) % TICRATE) >= TICRATE-2))
? 3 : 1;
}
}
}
}
@ -1675,10 +1676,26 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UI
worktime = drawtime/(60*TICRATE);
if (drawtime != UINT32_MAX && worktime >= 100)
{
jitter = 1;
fastjitter = true;
worktime = 99;
drawtime = (100*(60*TICRATE))-1;
}
if (fastjitter)
{
jitter *= R_GetTimeFrac(RTF_LEVEL) * (leveltime & 1 ? 1 : -1);
}
else
{
if (drawtime & 2) jitter = -jitter;
jitter *= !!(drawtime & 1) != countdown ? FRACUNIT - R_GetTimeFrac(RTF_LEVEL) : R_GetTimeFrac(RTF_LEVEL);
}
if (mode && drawtime == UINT32_MAX)
V_DrawKartString(TX, TY+3, splitflags, va("--'--\"--"));
else if (dontdraw) // overtime flash
;
else if (overtime)
{
if (((drawtime*2)/TICRATE) % 2 == 0)
@ -1690,66 +1707,47 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UI
textcolor = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_ORANGE, GTC_CACHE);
}
V_DrawStringScaledEx(
(TIME_X + 33) << FRACBITS,
(TIME_Y + 3) << FRACBITS,
FRACUNIT,
FRACUNIT,
FRACUNIT,
FRACUNIT,
splitflags,
textcolor,
KART_FONT,
va("OVERTIME!")
);
}
else if (worktime < 100) // 99:99:99 only
{
// zero minute
if (worktime < 10)
const char *overtimestr = "OVERTIME";
for (UINT8 i = 0; i < strlen(overtimestr); i++)
{
V_DrawKartString(TX, TY+3, splitflags, va("0"));
// minutes time 0 __ __
V_DrawKartString(TX+12, TY+3, splitflags, va("%d", worktime));
V_DrawStringScaledEx(
(TIME_X + 33 + 12*i + (i == 6 ? -4 : i == 7 ? -1 : 0)) << FRACBITS,
((TIME_Y + 3) << FRACBITS) + (i & 1 ? jitter : -jitter),
FRACUNIT,
FRACUNIT,
FRACUNIT,
FRACUNIT,
splitflags,
textcolor,
KART_FONT,
va("%c", overtimestr[i])
);
}
// minutes time 0 __ __
else
V_DrawKartString(TX, TY+3, splitflags, va("%d", worktime));
}
else
{
// minutes time 00 __ __
V_DrawKartStringAtFixed(TX<<FRACBITS, ((TY+3)<<FRACBITS)+jitter, splitflags, va("%d", worktime/10));
V_DrawKartStringAtFixed((TX+12)<<FRACBITS, ((TY+3)<<FRACBITS)-jitter, splitflags, va("%d", worktime%10));
// apostrophe location _'__ __
V_DrawKartString(TX+24, TY+3, splitflags, va("'"));
worktime = (drawtime/TICRATE % 60);
// zero second _ 0_ __
if (worktime < 10)
{
V_DrawKartString(TX+36, TY+3, splitflags, va("0"));
// seconds time _ _0 __
V_DrawKartString(TX+48, TY+3, splitflags, va("%d", worktime));
}
// zero second _ 00 __
else
V_DrawKartString(TX+36, TY+3, splitflags, va("%d", worktime));
// seconds time _ 00 __
V_DrawKartStringAtFixed((TX+36)<<FRACBITS, ((TY+3)<<FRACBITS)+jitter, splitflags, va("%d", worktime/10));
V_DrawKartStringAtFixed((TX+48)<<FRACBITS, ((TY+3)<<FRACBITS)-jitter, splitflags, va("%d", worktime%10));
// quotation mark location _ __"__
V_DrawKartString(TX+60, TY+3, splitflags, va("\""));
worktime = G_TicsToCentiseconds(drawtime);
// zero tick _ __ 0_
if (worktime < 10)
{
V_DrawKartString(TX+72, TY+3, splitflags, va("0"));
// tics _ __ _0
V_DrawKartString(TX+84, TY+3, splitflags, va("%d", worktime));
}
// zero tick _ __ 00
else
V_DrawKartString(TX+72, TY+3, splitflags, va("%d", worktime));
// tics _ __ 00
V_DrawKartStringAtFixed((TX+72)<<FRACBITS, ((TY+3)<<FRACBITS)+jitter, splitflags, va("%d", worktime/10));
V_DrawKartStringAtFixed((TX+84)<<FRACBITS, ((TY+3)<<FRACBITS)-jitter, splitflags, va("%d", worktime%10));
}
else if ((drawtime/TICRATE) & 1)
V_DrawKartString(TX, TY+3, splitflags, va("99'59\"99"));
if ((modeattacking || (mode == 1)) && G_EmblemsEnabled()) // emblem time!
{
@ -4290,7 +4288,7 @@ static void K_drawKartMinimap(void)
mobj = players[i].mo;
if (players[i].mo->health <= 0 && players[i].pflags & PF_NOCONTEST)
if (mobj->health <= 0 && (players[i].pflags & PF_NOCONTEST))
{
workingPic = kp_nocontestminimap;
@ -4308,10 +4306,13 @@ static void K_drawKartMinimap(void)
iconoffsets.y = heighthalf - workingPic->topoffset - adjusty;
colormap = R_GetTranslationColormap(TC_DEFAULT, mobj->color, GTC_CACHE);
if (mobj->tracer && !P_MobjWasRemoved(mobj->tracer))
mobj = mobj->tracer;
}
else
{
skin = ((skin_t*)players[i].mo->skin)-skins;
skin = ((skin_t*)mobj->skin)-skins;
workingPic = faceprefix[skin][FACE_MINIMAP];
@ -4343,7 +4344,7 @@ static void K_drawKartMinimap(void)
}
#endif
colorizeplayer = players[i].mo->colorized;
colorizeplayer = mobj->colorized;
if ((players[i].invincibilitytimer) && ((K_InvincibilityGradient(players[i].invincibilitytimer) > (FRACUNIT/2)) || (K_GetKartInvinType() == KARTINVIN_LEGACY)))
{
@ -4352,10 +4353,10 @@ static void K_drawKartMinimap(void)
}
else
{
usecolor = players[i].mo->color;
usecolor = mobj->color;
}
if (players[i].mo->color)
if (usecolor)
{
if (colorizeplayer)
colormap = R_GetTranslationColormap(TC_RAINBOW, usecolor, GTC_CACHE);
@ -4524,7 +4525,7 @@ static void K_drawKartMinimap(void)
mobj = players[localplayers[i]].mo;
if (players[localplayers[i]].mo->health <= 0 && players[localplayers[i]].pflags & PF_NOCONTEST)
if (mobj->health <= 0 && (players[localplayers[i]].pflags & PF_NOCONTEST))
{
workingPic = kp_nocontestminimap;
@ -4544,10 +4545,13 @@ static void K_drawKartMinimap(void)
colormap = R_GetTranslationColormap(TC_DEFAULT, mobj->color, GTC_CACHE);
nocontest = true;
if (mobj->tracer && !P_MobjWasRemoved(mobj->tracer))
mobj = mobj->tracer;
}
else
{
skin = ((skin_t*)players[localplayers[i]].mo->skin)-skins;
skin = ((skin_t*)mobj->skin)-skins;
workingPic = faceprefix[skin][FACE_MINIMAP];
@ -4578,7 +4582,7 @@ static void K_drawKartMinimap(void)
}
#endif
colorizeplayer = players[localplayers[i]].mo->colorized;
colorizeplayer = mobj->colorized;
if ((players[localplayers[i]].invincibilitytimer) &&
((K_InvincibilityGradient(players[localplayers[i]].invincibilitytimer) >
@ -4589,10 +4593,10 @@ static void K_drawKartMinimap(void)
}
else
{
usecolor = players[localplayers[i]].mo->color;
usecolor = mobj->color;
}
if (players[localplayers[i]].mo->color)
if (usecolor)
{
if (colorizeplayer)
colormap = R_GetTranslationColormap(TC_RAINBOW, usecolor, GTC_CACHE);
@ -4701,6 +4705,56 @@ static void K_drawKartMinimap(void)
}
}
static void K_drawKartFinish(void)
{
INT32 timer, minsplitstationary, pnum = 0, splitflags = V_SPLITSCREEN;
patch_t **kptodraw;
{
timer = stplyr->karthud[khud_finish];
kptodraw = kp_racefinish;
minsplitstationary = 2;
}
if (!timer || timer > 2*TICRATE)
return;
if ((timer % (2*5)) / 5) // blink
pnum = 1;
if (r_splitscreen > 0)
pnum += (r_splitscreen > 1) ? 2 : 4;
if (r_splitscreen >= minsplitstationary) // 3/4p, stationary FIN
{
V_DrawScaledPatch(STCD_X - (SHORT(kptodraw[pnum]->width)/2), STCD_Y - (SHORT(kptodraw[pnum]->height)/2), splitflags, kptodraw[pnum]);
return;
}
//else -- 1/2p, scrolling FINISH
{
INT32 x, xval, ox, interpx, pwidth;
x = ((vid.width<<FRACBITS)/vid.dupx);
xval = (SHORT(kptodraw[pnum]->width)<<FRACBITS);
pwidth = max(xval, x);
x = ((TICRATE - timer) * pwidth) / TICRATE;
ox = ((TICRATE - (timer - 1)) * pwidth) / TICRATE;
interpx = R_InterpolateFixed(ox, x);
if (r_splitscreen && stplyrnum == 1)
interpx = -interpx;
V_DrawFixedPatch(interpx + (STCD_X<<FRACBITS) - (pwidth / 2),
(STCD_Y<<FRACBITS) - (SHORT(kptodraw[pnum]->height)<<(FRACBITS-1)),
FRACUNIT,
splitflags, kptodraw[pnum], NULL);
}
}
static void K_drawKartStartCountdown(void)
{
INT32 pnum = 0;
@ -4745,51 +4799,6 @@ static void K_drawKartStartCountdown(void)
}
}
static void K_drawKartFinish(void)
{
INT32 pnum = 0, splitflags = V_SPLITSCREEN;
if (!stplyr->karthud[khud_cardanimation] || stplyr->karthud[khud_cardanimation] >= 2*TICRATE)
return;
if ((stplyr->karthud[khud_cardanimation] % (2*5)) / 5) // blink
pnum = 1;
if (r_splitscreen > 1) // 3/4p, stationary FIN
{
pnum += 2;
V_DrawScaledPatch(STCD_X - (kp_racefinish[pnum]->width/2), STCD_Y - (kp_racefinish[pnum]->height/2), splitflags, kp_racefinish[pnum]);
return;
}
//else -- 1/2p, scrolling FINISH
{
INT32 x, xval, ox, interpx, pwidth;
if (r_splitscreen) // wide splitscreen
pnum += 4;
x = ((vid.width<<FRACBITS)/vid.dupx);
xval = (kp_racefinish[pnum]->width)<<FRACBITS;
pwidth = max(xval, x);
x = ((TICRATE - stplyr->karthud[khud_cardanimation]) * pwidth) / TICRATE;
ox = ((TICRATE - (stplyr->karthud[khud_cardanimation] - 1)) * pwidth) / TICRATE;
interpx = R_InterpolateFixed(ox, x);
if (r_splitscreen && stplyrnum == 1)
interpx = -interpx;
V_DrawFixedPatch(interpx + (STCD_X<<FRACBITS) - (pwidth / 2),
(STCD_Y<<FRACBITS) - (kp_racefinish[pnum]->height<<(FRACBITS-1)),
FRACUNIT,
splitflags, kp_racefinish[pnum], NULL);
}
}
static void K_drawBattleFullscreen(void)
{
INT32 cardanim = stplyr->karthud[khud_cardanimation] << FRACBITS;
@ -4850,7 +4859,7 @@ static void K_drawBattleFullscreen(void)
{
if (stplyrnum == 0)
V_DrawFadeScreen(0xFF00, 16);
if (stplyr->exiting < 6*TICRATE && !stplyr->spectator)
if (stplyr->exiting <= 6*TICRATE && !stplyr->spectator)
{
patch_t *p = kp_battlecool;
@ -4861,8 +4870,8 @@ static void K_drawBattleFullscreen(void)
V_DrawFixedPatch(x<<FRACBITS, y, scale, splitflags, p, NULL);
}
else
K_drawKartFinish();
K_drawKartFinish();
}
else if (stplyr->bumper <= 0 && stplyr->karmadelay && comeback && !stplyr->spectator && drawcomebacktimer)
{

View file

@ -65,6 +65,7 @@
#include "k_grandprix.h"
#include "k_cluster.hpp"
#include "k_odds.h"
#include "k_specialstage.h"
#include "h_timers.h"
#include "blan/b_soc.h"
@ -100,26 +101,34 @@ vector3_t clusterpoint, clusterdtf;
void K_TimerInit(void)
{
UINT8 i;
UINT8 numPlayers = 0;//, numspec = 0;
UINT8 numPlayers = 0;
boolean domodeattack = ((modeattacking != ATTACKING_NONE)
|| (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE));
starttime = introtime = 0;
if (!bossinfo.boss)
if (specialStage.active == true)
{
for (i = 0; i < MAXPLAYERS; i++)
K_InitSpecialStage();
}
else if (bossinfo.boss == false)
{
if (!domodeattack)
{
if (!playeringame[i])
for (i = 0; i < MAXPLAYERS; i++)
{
continue;
if (!playeringame[i] || players[i].spectator)
{
continue;
}
numPlayers++;
}
if (players[i].spectator == true)
if (numPlayers < 2)
{
//numspec++;
continue;
domodeattack = true;
}
numPlayers++;
}
introtime = (108) + 5; // 108 for rotation, + 5 for white fade
@ -131,15 +140,14 @@ void K_TimerInit(void)
}
}
// NOW you can try to setup Item Breaker, if there's not enough players for a match
K_BattleInit(numPlayers);
timelimitintics = extratimeintics = secretextratime = 0; // G: restore K_TimerReset?
K_BattleInit(domodeattack);
timelimitintics = extratimeintics = secretextratime = 0;
if ((gametyperules & GTR_TIMELIMIT) && !bossinfo.boss)
if ((gametyperules & GTR_TIMELIMIT) && !bossinfo.boss && !modeattacking)
{
if (!K_CanChangeRules())
if (!K_CanChangeRules(true))
{
if (grandprixinfo.gp)
if (itembreaker)
{
timelimitintics = (20*TICRATE);
}
@ -414,6 +422,7 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_saltyhop);
CV_RegisterVar(&cv_naturalcamera);
CV_RegisterVar(&cv_gptest);
}
//}
@ -6310,9 +6319,8 @@ void K_KartPlayerHUDUpdate(player_t *player)
if (player->karthud[khud_tauntvoices])
player->karthud[khud_tauntvoices]--;
if (gametype == GT_RACE)
if (gametyperules & GTR_RINGS)
{
if ((K_RingsActive() == true))
{
if (player->pflags & PF_RINGLOCK)
@ -6322,16 +6330,20 @@ void K_KartPlayerHUDUpdate(player_t *player)
}
}
if (player->exiting)
{
if (player->karthud[khud_finish] <= 2*TICRATE)
player->karthud[khud_finish]++;
}
else
player->karthud[khud_finish] = 0;
if ((gametyperules & GTR_BUMPERS) && (player->exiting || player->karmadelay))
{
if (player->exiting)
{
if (player->exiting < 6*TICRATE)
player->karthud[khud_cardanimation] += ((164-player->karthud[khud_cardanimation])/8)+1;
else if (player->exiting == 6*TICRATE)
player->karthud[khud_cardanimation] = 0;
else if (player->karthud[khud_cardanimation] < 2*TICRATE)
player->karthud[khud_cardanimation]++;
}
else
{
@ -6346,11 +6358,6 @@ void K_KartPlayerHUDUpdate(player_t *player)
if (player->karthud[khud_cardanimation] < 0)
player->karthud[khud_cardanimation] = 0;
}
else if ((gametyperules & GTR_CIRCUIT) && player->exiting)
{
if (player->karthud[khud_cardanimation] < 2*TICRATE)
player->karthud[khud_cardanimation]++;
}
else
player->karthud[khud_cardanimation] = 0;
@ -6599,7 +6606,15 @@ UINT8 K_RaceLapCount(INT16 mapNum)
return 0;
}
if (cv_numlaps.value == -1 || modeattacking != ATTACKING_NONE)
if ((grandprixinfo.gp == true)
&& (grandprixinfo.eventmode == GPEVENT_NONE)
&& cv_gptest.value)
{
// For testing
return 1;
}
if (cv_numlaps.value == -1 || K_CanChangeRules(true) == false)
{
// Use map default
return mapheaderinfo[mapNum]->numlaps;
@ -7330,8 +7345,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
K_UpdateTripwire(player);
K_KartPlayerHUDUpdate(player);
if (P_IsObjectOnGround(player->mo))
player->waterskip = 0;
@ -11396,7 +11409,7 @@ boolean K_NotFreePlay(void)
UINT8 i;
UINT8 nump = 0;
if (K_CanChangeRules() == false)
if (K_CanChangeRules(true) == false)
{
// Rounds with direction are never FREE PLAY.
return true;

View file

@ -152,6 +152,38 @@ static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS][2] =
{ 5, 1 } // Jawz x2
};
static UINT8 K_KartItemOddsSpecial[NUMKARTRESULTS-1][4] =
{
//M N O P
{ 1, 1, 0, 0 }, // Sneaker
{ 0, 0, 0, 0 }, // Rocket Sneaker
{ 0, 0, 0, 0 }, // Invincibility
{ 0, 0, 0, 0 }, // Banana
{ 0, 0, 0, 0 }, // Eggman Monitor
{ 1, 1, 0, 0 }, // Orbinaut
{ 1, 1, 0, 0 }, // Jawz
{ 0, 0, 0, 0 }, // Mine
{ 0, 0, 0, 0 }, // Ballhog
{ 0, 0, 0, 1 }, // Self-Propelled Bomb
{ 0, 0, 0, 0 }, // Grow
{ 0, 0, 0, 0 }, // Shrink
{ 0, 0, 0, 0 }, // Thunder Shield
{ 0, 0, 0, 0 }, // Hyudoro
{ 0, 0, 0, 0 }, // Pogo Spring
{ 0, 0, 0, 0 }, // Kitchen Sink
{ 0, 0, 0, 0 }, // Super Ring
{ 0, 0, 0, 0 }, // Land Mine
{ 0, 0, 0, 0 }, // Bubble Shield
{ 0, 0, 0, 0 }, // Flame Shield
{ 0, 1, 1, 0 }, // Sneaker x2
{ 0, 0, 1, 1 }, // Sneaker x3
{ 0, 0, 0, 0 }, // Banana x3
{ 0, 0, 0, 0 }, // Banana x10
{ 0, 1, 1, 0 }, // Orbinaut x3
{ 0, 0, 1, 1 }, // Orbinaut x4
{ 0, 0, 1, 1 } // Jawz x2
};
// Cooldown time table; contains both base (index 0) and current (index 1)
// times. Base times are in seconds, current times are in tics.
tic_t ItemBGone[NUMKARTRESULTS][2] =

139
src/k_specialstage.c Normal file
View file

@ -0,0 +1,139 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour
// Copyright (C) 2022 by Kart Krew
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file k_specialstage.c
/// \brief Special Stage game logic
#include "k_specialstage.h"
#include "doomdef.h"
#include "d_player.h"
#include "g_game.h"
#include "p_local.h"
#include "k_kart.h"
#include "s_sound.h"
#include "st_stuff.h"
#include "z_zone.h"
#include "k_waypoint.h"
struct specialStage specialStage;
/*--------------------------------------------------
void K_ResetSpecialStage(void)
See header file for description.
--------------------------------------------------*/
void K_ResetSpecialStage(void)
{
memset(&specialStage, 0, sizeof(struct specialStage));
}
/*--------------------------------------------------
void K_InitSpecialStage(void)
See header file for description.
--------------------------------------------------*/
void K_InitSpecialStage(void)
{
INT32 i;
specialStage.beamDist = UINT32_MAX; // TODO: make proper value
for (i = 0; i < MAXPLAYERS; i++)
{
player_t *player = NULL;
if (playeringame[i] == false)
{
continue;
}
player = &players[i];
if (player->spectator == true)
{
continue;
}
if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true)
{
continue;
}
// Rolling start? lol
P_InstaThrust(player->mo, player->mo->angle, K_GetKartSpeed(player, false, false));
}
}
/*--------------------------------------------------
static void K_MoveExitBeam(void)
Updates the exit beam.
--------------------------------------------------*/
static void K_MoveExitBeam(void)
{
UINT32 moveDist = 0;
INT32 i;
if (leveltime <= 2)
{
return;
}
moveDist = (8 * mapobjectscale) / FRACUNIT;
if (specialStage.beamDist <= moveDist)
{
specialStage.beamDist = 0;
// TODO: Fail Special Stage
}
else
{
specialStage.beamDist -= moveDist;
}
// Find players who are now outside of the level.
for (i = 0; i < MAXPLAYERS; i++)
{
player_t *player = NULL;
if (playeringame[i] == false)
{
continue;
}
player = &players[i];
if (player->spectator == true
|| player->exiting > 0
|| (player->pflags & PF_NOCONTEST))
{
continue;
}
if (player->distancetofinish > specialStage.beamDist)
{
P_DoTimeOver(player);
}
}
}
/*--------------------------------------------------
void K_TickSpecialStage(void)
See header file for description.
--------------------------------------------------*/
void K_TickSpecialStage(void)
{
if (specialStage.active == false)
{
return;
}
K_MoveExitBeam();
}

62
src/k_specialstage.h Normal file
View file

@ -0,0 +1,62 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour
// Copyright (C) 2022 by Kart Krew
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file k_specialstage.h
/// \brief Special Stage game logic
#ifndef __K_SPECIALSTAGE__
#define __K_SPECIALSTAGE__
#include "doomdef.h"
#include "doomstat.h"
#ifdef __cplusplus
extern "C" {
#endif
extern struct specialStage
{
boolean active; ///< If true, then we are in a special stage
boolean encore; ///< Copy of encore, just to make sure you can't cheat it with cvars
UINT32 beamDist; ///< Where the exit beam is.
mobj_t *capsule; ///< The Chaos Emerald capsule.
} specialStage;
/*--------------------------------------------------
void K_ResetSpecialStage(void);
Resets Special Stage information to a clean slate.
--------------------------------------------------*/
void K_ResetSpecialStage(void);
/*--------------------------------------------------
void K_InitSpecialStage(void);
Initializes Special Stage data on map load.
--------------------------------------------------*/
void K_InitSpecialStage(void);
/*--------------------------------------------------
void K_TickSpecialStage(void);
Updates Special Stage data each frame.
--------------------------------------------------*/
void K_TickSpecialStage(void);
#ifdef __cplusplus
} // extern "C"
#endif
#endif

View file

@ -221,7 +221,7 @@ int LUA_PushGlobals(lua_State *L, const char *word)
lua_pushinteger(L, redscore);
return 1;
} else if (fastcmp(word,"timelimit")) {
lua_pushinteger(L, cv_timelimit.value);
lua_pushinteger(L, timelimitintics);
return 1;
} else if (fastcmp(word,"pointlimit")) {
lua_pushinteger(L, cv_pointlimit.value);

View file

@ -464,10 +464,9 @@ static CV_PossibleValue_t dummybumpspark_cons_t[] = {{BUMPSPARK_NONE, "Off"},
{0, NULL}};
consvar_t cv_dummyattackingbumpspark = CVAR_INIT ("dummyattackingbumpspark", "Off", CV_HIDEN|CV_CALL|CV_NOINIT, dummybumpspark_cons_t, Nextmap_OnChange);
static CV_PossibleValue_t dummygpdifficulty_cons_t[] = {{KARTSPEED_EASY, "Easy"}, {KARTSPEED_NORMAL, "Normal"}, {KARTSPEED_HARD, "Hard"}, {KARTSPEED_EXPERT, "Expert"}, {KARTGP_MASTER, "Master"}, {KARTGP_NIGHTMARE, "Nightmare"}, {0, NULL}};
static CV_PossibleValue_t dummygpcup_cons_t[50] = {{1, "TEMP"}}; // A REALLY BIG NUMBER, SINCE THIS IS TEMP UNTIL NEW MENUS
consvar_t cv_dummygpdifficulty = CVAR_INIT ("dummygpdifficulty", "Normal", CV_HIDEN, dummygpdifficulty_cons_t, NULL);
consvar_t cv_dummygpdifficulty = CVAR_INIT ("dummygpdifficulty", "Normal", CV_HIDEN, gpdifficulty_cons_t, NULL);
consvar_t cv_dummygpencore = CVAR_INIT ("dummygpencore", "Off", CV_HIDEN, CV_OnOff, NULL);
consvar_t cv_dummygpcup = CVAR_INIT ("dummygpcup", "TEMP", CV_HIDEN, dummygpcup_cons_t, NULL);
@ -6551,8 +6550,7 @@ INT32 MR_StartServer(INT32 choice)
if (menustack[0] == MN_MP_SPLITSCREEN) // offline server
{
paused = false;
SV_StartSinglePlayerServer();
multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this...
SV_StartSinglePlayerServer(cv_newgametype.value, false);
D_MapChange(cv_nextmap.value, cv_newgametype.value, (cv_kartencore.value == 1), 1, 1, false, false);
}
else

View file

@ -3335,9 +3335,12 @@ void A_AttractChase(void *thing)
// Set attraction flag
actor->cusval = 1;
if ((actor->tracer->player->itemtype == KITEM_THUNDERSHIELD
if (
actor->tracer->player && actor->tracer->health
&& ((gametyperules & GTR_BUMPERS)
|| (actor->tracer->player->itemtype == KITEM_THUNDERSHIELD
&& RINGTOTAL(actor->tracer->player) < actor->tracer->player->ringmax
&& !(actor->tracer->player->pflags & PF_RINGLOCK))
&& !(actor->tracer->player->pflags & PF_RINGLOCK)))
//&& P_CheckSight(actor, actor->tracer)
)
{

View file

@ -991,14 +991,9 @@ void P_TouchStarPost(mobj_t *post, player_t *player, boolean snaptopost)
player->starpostnum = post->health;
}
// Easily make it so that overtime works offline
#define TESTOVERTIMEINFREEPLAY
/** Checks if the level timer is over the timelimit and the round should end,
* unless you are in overtime. In which case leveltime may stretch out beyond
* timelimitintics and overtime's status will be checked here each tick.
* Verify that the value of ::cv_timelimit is greater than zero before
* calling this function.
*
* \sa cv_timelimit, P_CheckPointLimit, P_UpdateSpecials
*/
@ -1006,25 +1001,73 @@ void P_CheckTimeLimit(void)
{
INT32 i, k;
if (!cv_timelimit.value)
if (exitcountdown)
return;
if (!(multiplayer || netgame))
if (!timelimitintics)
return;
if (!(gametyperules & GTR_TIMELIMIT))
if (leveltime < starttime)
{
if (secretextratime)
secretextratime--;
return;
}
if (itembreaker)
return;
INT32 timeleft = timelimitintics + starttime - leveltime;
if (timeleft > 0)
{
if (secretextratime)
{
secretextratime--;
timelimitintics++;
}
else if (extratimeintics)
{
timelimitintics++;
if (leveltime & 1)
;
else
{
if (extratimeintics > 20)
{
extratimeintics -= 20;
timelimitintics += 20;
}
else
{
timelimitintics += extratimeintics;
extratimeintics = 0;
}
S_StartSound(NULL, sfx_ptally);
}
}
else
{
if (timelimitintics > 60*TICRATE && !itembreaker)
{
if (timeleft == 60*TICRATE)
S_StartSound(NULL, sfx_cdfm67); // guys it's a bell it's just like splatoon guys
if (timeleft == 59*TICRATE)
S_StartSound(NULL, sfx_bhurry); // hurry up :))))
}
if (leveltime < (timelimitintics + starttime))
// last five second countdown
if (timeleft % TICRATE == 0)
{
if (timeleft <= 5*TICRATE)
S_StartSound(NULL, sfx_kc35); // 5, 4
if (timeleft <= 3*TICRATE)
S_StartSound(NULL, sfx_kc3d); // 3, 2, 1
}
}
return;
}
if (gameaction == ga_completed)
return;
if (cv_overtime.value)
if (!itembreaker && !grandprixinfo.gp && cv_overtime.value)
{
INT32 playerarray[MAXPLAYERS];
INT32 tempplayer = 0;
@ -1043,7 +1086,7 @@ void P_CheckTimeLimit(void)
#ifdef TESTOVERTIMEINFREEPLAY
if (timelimitintics > 0 && (gametyperules & GTR_TIMELIMIT) && startedInFreePlay)
{
return;
goto overtimesound;
}
#endif
@ -1085,23 +1128,33 @@ void P_CheckTimeLimit(void)
//End the round if the top players aren't tied.
if (players[playerarray[0]].roundscore == players[playerarray[1]].roundscore)
return;
goto overtimesound;
}
else
{
//In team match and CTF, determining a tie is much simpler. =P
if (redscore == bluescore)
return;
goto overtimesound;
}
}
}
P_DoAllPlayersExit(0, false);
return;
overtimesound:
if (timeleft == 0)
{
S_StartSound(NULL, sfx_s3k50); // overtime warning
S_StartSound(NULL, sfx_s3k9d);
}
if (timeleft == -2*TICRATE)
S_StartSoundAtVolume(NULL, sfx_s3k50, 128);
if (timeleft == -4*TICRATE)
S_StartSoundAtVolume(NULL, sfx_s3k50, 96);
}
/** Checks if a player's score is over the pointlimit and the round should end.
* Verify that the value of ::cv_pointlimit is greater than zero before
* calling this function.
*
* \sa cv_pointlimit, P_CheckTimeLimit, P_UpdateSpecials
*/
@ -1112,7 +1165,7 @@ void P_CheckPointLimit(void)
if (exitcountdown)
return;
if (!K_CanChangeRules())
if (!K_CanChangeRules(true))
return;
if (!cv_pointlimit.value)
@ -1158,7 +1211,7 @@ boolean P_CheckRacers(void)
{
const boolean griefed = (spectateGriefed > 0);
boolean eliminateLast = (!K_CanChangeRules() && !demo.playback) || (cv_karteliminatelast.value != 0); // temp hack until this is ported properly
boolean eliminateLast = (!K_CanChangeRules(true) || (cv_karteliminatelast.value != 0));
boolean allHumansDone = true;
//boolean allBotsDone = true;
@ -1277,7 +1330,7 @@ boolean P_CheckRacers(void)
{
tic_t countdown = 30*TICRATE; // 30 seconds left to finish, get going!
if (K_CanChangeRules() == true)
if (K_CanChangeRules(true) == true)
{
// Custom timer
countdown = cv_countdowntime.value * TICRATE;
@ -1412,15 +1465,39 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
if (itembreaker)
{
// G: uncommented this for funsies
mobj_t * ring;
angle_t dir = 0;
if (inflictor)
dir = R_PointToAngle2(inflictor->x, inflictor->y, target->x, target->y);
else
dir = R_PointToAngle2(source->x, source->y, target->x, target->y);
for (UINT8 i = 0; i < 2; i++)
{
dir += (ANGLE_MAX/3);
ring = P_SpawnMobj(target->x, target->y, target->z, MT_RING);
ring->angle = dir;
P_InstaThrust(ring, dir, 16*ring->scale);
ring->momz = 8 * target->scale * P_MobjFlip(target);
P_SetTarget(&ring->tracer, source);
source->player->pickuprings++;
}
target->flags2 |= MF2_BOSSFLEE;
target->flags2 |= MF2_DONTRESPAWN;
K_SpawnBattlePoints(source->player, NULL, 1);
// All targets busted!
if (++numtargets >= nummapboxes)
{
P_DoAllPlayersExit(0, true);
P_DoAllPlayersExit(0, (grandprixinfo.gp == true));
}
else if (timelimitintics)
{
S_StartSound(NULL, sfx_s221);
extratimeintics += 10*TICRATE;
secretextratime = TICRATE/2;
}
}
if (cv_itemrespawn.value && modeattacking == ATTACKING_NONE && !itembreaker)
@ -1591,6 +1668,9 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
kart->old_y = target->old_y;
kart->old_z = target->old_z;
if (target->player->pflags & PF_NOCONTEST)
P_SetTarget(&target->tracer, kart);
if (damagetype != DMG_TIMEOVER)
{
kart->angle = flingangle + ANGLE_180;

View file

@ -108,6 +108,7 @@
#include "doomstat.h" // MAXMUSNAMES
#include "k_mapuser.h"
#include "p_deepcopy.h"
#include "k_specialstage.h"
#include "blan/b_soc.h"

View file

@ -42,6 +42,7 @@
#include "acs/interface.h"
#include "k_bot.h" // K_BotTicker
#include "k_odds.h" // ItemBGone
#include "k_specialstage.h"
#ifdef PARANOIA
#include "deh_tables.h" // MOBJTYPE_LIST
@ -752,6 +753,7 @@ void P_Ticker(boolean run)
if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
{
P_PlayerThink(&players[i]);
K_KartPlayerHUDUpdate(&players[i]);
if (!players[i].spectator)
p++;
@ -838,48 +840,6 @@ void P_Ticker(boolean run)
}
}
if (timelimitintics > 0)
{
// last minute
if (timelimitintics > 60*TICRATE && leveltime == timelimitintics - 60*TICRATE + starttime)
{
S_StartSound(NULL, sfx_cdfm67); // guys it's a bell it's just like splatoon guys
}
if (timelimitintics > 60*TICRATE && leveltime == timelimitintics - 59*TICRATE + starttime)
{
S_StartSound(NULL, sfx_bhurry); // hurry up :))))
}
// overtime
if (gameaction != ga_completed && leveltime == timelimitintics + 1 + starttime)
{
S_StartSound(NULL, sfx_s3k50); // overtime warning
S_StartSound(NULL, sfx_s3k9d);
}
if (gameaction != ga_completed && leveltime == timelimitintics + 2*TICRATE + starttime)
{
S_StartSoundAtVolume(NULL, sfx_s3k50, 128);
}
if (gameaction != ga_completed && leveltime == timelimitintics + 4*TICRATE + starttime)
{
S_StartSoundAtVolume(NULL, sfx_s3k50, 96);
}
// last five second countdown
if (leveltime == timelimitintics - 5 * TICRATE + starttime ||
leveltime == timelimitintics - 4 * TICRATE + starttime)
{
S_StartSound(NULL, sfx_kc35); // 5, 4
}
if (leveltime == timelimitintics - TICRATE + starttime ||
leveltime == timelimitintics - 2 * TICRATE + starttime ||
leveltime == timelimitintics - 3 * TICRATE + starttime)
{
S_StartSound(NULL, sfx_kc35);
S_StartSound(NULL, sfx_kc3d); // 3, 2, 1
}
}
// Change timing of start music based on gametyperules
{
tic_t startingtime = (gametyperules & GTR_NOCOUNTDOWN) ? introtime : starttime;
@ -961,6 +921,8 @@ void P_Ticker(boolean run)
K_BossInfoTicker();
K_TickSpecialStage();
if (gametyperules & GTR_WANTED)
{
if (wantedcalcdelay && --wantedcalcdelay <= 0)

View file

@ -1311,7 +1311,8 @@ void P_DoPlayerExit(player_t *player, pflags_t flags)
S_StartSound(player->mo, playerskin->soundsid[S_sfx[sfx_id].skinsound]);
}
if (!K_CanChangeRules() || cv_inttime.value > 0)
// See Y_StartIntermission timer handling
if (!K_CanChangeRules(false) || cv_inttime.value > 0)
P_EndingMusic(player);
if (P_CheckRacers())
@ -3854,8 +3855,6 @@ void P_PlayerThink(player_t *player)
player->old_drawangle = player->drawangle;
player->pflags &= ~PF_HITFINISHLINE;
if (player->awayviewmobj && P_MobjWasRemoved(player->awayviewmobj))
{
P_SetTarget(&player->awayviewmobj, NULL); // remove awayviewmobj asap if invalid
@ -4039,29 +4038,6 @@ void P_PlayerThink(player_t *player)
}
}
// check water content, set stuff in mobj
P_MobjCheckWater(player->mo);
#ifndef SECTORSPECIALSAFTERTHINK
if (player->onconveyor != 1 || !P_IsObjectOnGround(player->mo))
player->onconveyor = 0;
// check special sectors : damage & secrets
if (!player->spectator)
P_PlayerInSpecialSector(player);
#endif
if (player->playerstate == PST_DEAD)
{
if (player->spectator)
player->mo->renderflags |= RF_GHOSTLY;
else
player->mo->renderflags &= ~RF_GHOSTLYMASK;
P_DeathThink(player);
LUA_HookPlayer(player, HOOK(PlayerThink));
return;
}
// Make sure spectators always have a score and ring count of 0.
if (player->spectator)
{
@ -4162,8 +4138,91 @@ void P_PlayerThink(player_t *player)
}
}
}
if ((netgame || multiplayer) && player->spectator && cmd->buttons & BT_ATTACK && !player->flashing)
if (cmd->flags & TICCMD_TYPING)
{
/*
typing_duration is slow to start and slow to stop.
typing_timer counts down a grace period before the player is not
actually considered typing anymore.
*/
if (cmd->flags & TICCMD_KEYSTROKE)
{
/* speed up if we are typing quickly! */
if (player->typing_duration > 0 && player->typing_timer > 12)
{
if (player->typing_duration < 16)
{
player->typing_duration = 24;
}
else
{
/* slows down a tiny bit as it approaches the next dot */
const UINT8 step = (((player->typing_duration + 15) & ~15) -
player->typing_duration) / 2;
player->typing_duration += max(step, 4);
}
}
player->typing_timer = 15;
}
else if (player->typing_timer > 0)
{
player->typing_timer--;
}
/* if we are in the grace period (including currently typing) */
if (player->typing_timer + player->typing_duration > 0)
{
/* always end the cycle on two dots */
if (player->typing_timer == 0 &&
(player->typing_duration < 16 || player->typing_duration == 40))
{
player->typing_duration = 0;
}
else if (player->typing_duration < 63)
{
player->typing_duration++;
}
else
{
player->typing_duration = 16;
}
}
}
else
{
player->typing_timer = 0;
player->typing_duration = 0;
}
player->pflags &= ~PF_HITFINISHLINE;
// check water content, set stuff in mobj
P_MobjCheckWater(player->mo);
#ifndef SECTORSPECIALSAFTERTHINK
if (player->onconveyor != 1 || !P_IsObjectOnGround(player->mo))
player->onconveyor = 0;
// check special sectors : damage & secrets
if (!player->spectator)
P_PlayerInSpecialSector(player);
#endif
if (player->playerstate == PST_DEAD)
{
if (player->spectator)
player->mo->renderflags |= RF_GHOSTLY;
else
player->mo->renderflags &= ~RF_GHOSTLYMASK;
P_DeathThink(player);
LUA_HookPlayer(player, HOOK(PlayerThink));
return;
}
if ((netgame || multiplayer) && player->spectator && !player->bot && cmd->buttons & BT_ATTACK && !player->flashing)
{
player->pflags ^= PF_WANTSTOJOIN;
player->flashing = TICRATE/2 + 1;
@ -4280,64 +4339,6 @@ void P_PlayerThink(player_t *player)
player->mo->renderflags &= ~RF_DONTDRAW;
}
if (cmd->flags & TICCMD_TYPING)
{
/*
typing_duration is slow to start and slow to stop.
typing_timer counts down a grace period before the player is not
actually considered typing anymore.
*/
if (cmd->flags & TICCMD_KEYSTROKE)
{
/* speed up if we are typing quickly! */
if (player->typing_duration > 0 && player->typing_timer > 12)
{
if (player->typing_duration < 16)
{
player->typing_duration = 24;
}
else
{
/* slows down a tiny bit as it approaches the next dot */
const UINT8 step = (((player->typing_duration + 15) & ~15) -
player->typing_duration) / 2;
player->typing_duration += max(step, 4);
}
}
player->typing_timer = 15;
}
else if (player->typing_timer > 0)
{
player->typing_timer--;
}
/* if we are in the grace period (including currently typing) */
if (player->typing_timer + player->typing_duration > 0)
{
/* always end the cycle on two dots */
if (player->typing_timer == 0 &&
(player->typing_duration < 16 || player->typing_duration == 40))
{
player->typing_duration = 0;
}
else if (player->typing_duration < 63)
{
player->typing_duration++;
}
else
{
player->typing_duration = 16;
}
}
}
else
{
player->typing_timer = 0;
player->typing_duration = 0;
}
K_KartPlayerThink(player, cmd); // SRB2kart
DoABarrelRoll(player);

View file

@ -305,6 +305,8 @@ char * V_ScaledWordWrap(
V__DrawDupxString (x,y,FRACUNIT,option,HU_FONT,string)
#define V_DrawKartString( x,y,option,string ) \
V__DrawDupxString (x,y,FRACUNIT,option,KART_FONT,string)
#define V_DrawKartStringAtFixed( x,y,option,string ) \
V__DrawOneScaleString (x,y,FRACUNIT,option,KART_FONT,string)
void V_DrawCenteredString(INT32 x, INT32 y, INT32 option, const char *string);
void V_DrawRightAlignedString(INT32 x, INT32 y, INT32 option, const char *string);

View file

@ -769,18 +769,8 @@ void Y_Ticker(void)
P_DoTeamscrambling();
}*/
// multiplayer uses timer (based on cv_inttime)
if (timer)
{
if (!--timer)
{
Y_EndIntermission();
G_AfterIntermission();
return;
}
}
// single player is hardcoded to go away after awhile
else if (intertic == endtic)
if ((timer && !--timer)
|| (intertic == endtic))
{
Y_EndIntermission();
G_AfterIntermission();
@ -869,8 +859,8 @@ void Y_Ticker(void)
S_StartSound(NULL, (kaching ? sfx_chchng : sfx_ptally));
Y_CalculateMatchData(2, Y_CompareRank);
}
else
endtic = intertic + 3*TICRATE; // 3 second pause after end of tally
/*else -- This is how to define an endtic, but we currently use timer for both SP and MP.
endtic = intertic + 3*TICRATE;*/
}
}
}
@ -934,23 +924,32 @@ void Y_StartIntermission(void)
powertype = K_UsingPowerLevels();
// determine the tic the intermission ends
if (!multiplayer || demo.playback)
// Technically cv_inttime is saved to demos... but this permits having extremely long timers for post-netgame chatting without stranding you on the intermission in netreplays.
if (!K_CanChangeRules(false))
{
timer = ((nump >= 2) ? 10 : 5)*TICRATE;
timer = 10*TICRATE;
}
else
{
timer = cv_inttime.value*TICRATE;
if (!timer)
timer = 1; // prevent a weird bug
}
// determine the tic everybody's scores/PWR starts getting sorted
sorttic = -1;
if (multiplayer || nump >= 2)
if (!timer)
{
sorttic = max((timer/2) - 2*TICRATE, 2*TICRATE); // 8 second pause after match results
// Prevent a weird bug
timer = 1;
}
else if (nump < 2 && !netgame)
{
// No PWR/global score, skip it
timer /= 2;
}
else
{
// Minimum two seconds for match results, then two second slideover approx halfway through
sorttic = max((timer/2) - 2*TICRATE, 2*TICRATE);
}
if (intermissiontypes[gametype] != int_none)
@ -971,7 +970,7 @@ void Y_StartIntermission(void)
case int_battle:
case int_battletime:
{
if (cv_inttime.value > 0)
if (timer > 1)
{
S_ChangeMusicInternal("racent", true); // loop it
S_ShowMusicCredit(-30*FRACUNIT, 5*TICRATE, 0);