Merge branch 'other-progression' into 'master'

Other Progression

Closes #561

See merge request KartKrew/Kart!1606
This commit is contained in:
Oni 2023-11-19 05:14:39 +00:00
commit 47a01d0699
44 changed files with 609 additions and 135 deletions

View file

@ -350,10 +350,13 @@ ACSVM::Word Environment::callSpecImpl
activator_t *activator = static_cast<activator_t *>(Z_Calloc(sizeof(activator_t), PU_LEVEL, nullptr));
auto __ = srb2::finally(
[activator]()
[info, activator]()
{
P_SetTarget(&activator->mo, NULL);
Z_Free(activator);
if (info->thread_era == thinker_era)
{
P_SetTarget(&activator->mo, NULL);
Z_Free(activator);
}
}
);

View file

@ -70,6 +70,28 @@ void ACS_Shutdown(void)
#endif
}
/*--------------------------------------------------
void ACS_InvalidateMapScope(size_t mapID)
See header file for description.
--------------------------------------------------*/
void ACS_InvalidateMapScope(void)
{
Environment *env = &ACSEnv;
ACSVM::GlobalScope *const global = env->getGlobalScope(0);
ACSVM::HubScope *hub = NULL;
ACSVM::MapScope *map = NULL;
// Conclude hub scope, even if we are not using it.
hub = global->getHubScope(0);
hub->reset();
// Conclude current map scope.
map = hub->getMapScope(0); // This is where you'd put in mapID if you add hub support.
map->reset();
}
/*--------------------------------------------------
void ACS_LoadLevelScripts(size_t mapID)
@ -103,14 +125,20 @@ void ACS_LoadLevelScripts(size_t mapID)
// hubs are to be implemented, this logic would need
// to be far more sophisticated.
// Reset hub scope, even if we are not using it.
// Extra note regarding the commented out ->reset()'s:
// This is too late! That needs to be done before
// PU_LEVEL is purged. Call ACS_InvalidateMapScope
// to take care of that. Those lines are left in
// only as a warning to future code spelunkers.
// Restart hub scope, even if we are not using it.
hub = global->getHubScope(0);
hub->reset();
//hub->reset();
hub->active = true;
// Start up new map scope.
map = hub->getMapScope(0); // This is where you'd put in mapID if you add hub support.
map->reset();
//map->reset();
map->active = true;
// Insert BEHAVIOR lump into the list.

View file

@ -44,6 +44,23 @@ void ACS_Init(void);
void ACS_Shutdown(void);
/*--------------------------------------------------
void ACS_InvalidateMapScope(size_t mapID);
Resets the ACS hub and map scopes to remove
existing running scripts, without starting
any new scripts.
Input Arguments:-
None
Return:-
None
--------------------------------------------------*/
void ACS_InvalidateMapScope(void);
/*--------------------------------------------------
void ACS_LoadLevelScripts(size_t mapID);

View file

@ -59,6 +59,7 @@ enum acs_tagType_e
class ThreadInfo : public ACSVM::ThreadInfo
{
public:
UINT32 thread_era; // If equal to thinker_era, mobj pointers are safe.
mobj_t *mo; // Object that activated this thread.
line_t *line; // Linedef that activated this thread.
UINT8 side; // Front / back side of said linedef.
@ -67,6 +68,7 @@ public:
bool fromLineSpecial; // Called from P_ProcessLineSpecial.
ThreadInfo() :
thread_era { thinker_era },
mo{ nullptr },
line{ nullptr },
side{ 0 },
@ -77,6 +79,7 @@ public:
}
ThreadInfo(const ThreadInfo &info) :
thread_era { thinker_era },
mo{ nullptr },
line{ info.line },
side{ info.side },
@ -88,6 +91,7 @@ public:
}
ThreadInfo(const activator_t *activator) :
thread_era { thinker_era },
mo{ nullptr },
line{ activator->line },
side{ activator->side },
@ -100,11 +104,15 @@ public:
~ThreadInfo()
{
P_SetTarget(&mo, nullptr);
if (thread_era == thinker_era)
{
P_SetTarget(&mo, nullptr);
}
}
ThreadInfo &operator = (const ThreadInfo &info)
{
thread_era = thinker_era;
P_SetTarget(&mo, info.mo);
line = info.line;
side = info.side;

View file

@ -1110,7 +1110,7 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
M_Memcpy(netbuffer->u.serverinfo.mapmd5, mapmd5, 16);
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) && !(mapheaderinfo[prevmap]->zonttl[0]))
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) && !(mapheaderinfo[gamemap-1]->zonttl[0]))
netbuffer->u.serverinfo.iszone = 1;
else
netbuffer->u.serverinfo.iszone = 0;
@ -1443,6 +1443,7 @@ static void CL_LoadReceivedSavegame(boolean reloading)
demo.playback = false;
demo.title = false;
titlemapinaction = false;
tutorialchallenge = TUTORIALSKIP_NONE;
automapactive = false;
// load a base level
@ -3992,7 +3993,9 @@ void SV_StopServer(void)
Y_EndIntermission();
if (gamestate == GS_VOTING)
Y_EndVote();
gamestate = wipegamestate = GS_NULL;
G_SetGamestate(GS_NULL);
wipegamestate = GS_NULL;
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
((UINT16*)localtextcmd[i])[0] = 0;

View file

@ -1062,8 +1062,11 @@ void D_ClearState(void)
memset(&grandprixinfo, 0, sizeof(struct grandprixinfo));
memset(&roundqueue, 0, sizeof(struct roundqueue));
// empty maptol so mario/etc sounds don't play in sound test when they shouldn't
// empty some other semi-important state
maptol = 0;
nextmapoverride = 0;
skipstats = 0;
tutorialchallenge = TUTORIALSKIP_NONE;
gameaction = ga_nothing;
memset(displayplayers, 0, sizeof(displayplayers));

View file

@ -2587,7 +2587,7 @@ static void Command_Map_f(void)
SplitScreen_OnChange();
}
if (!newnetgame && option_match == 0)
if (!newnetgame && (newgametype != GT_TUTORIAL) && option_match == 0)
{
grandprixinfo.gp = true;
grandprixinfo.initalize = true;
@ -3022,7 +3022,7 @@ static void Command_QueueMap_f(void)
return;
}
if ((/*newmapnum != 1 &&*/ M_MapLocked(newmapnum)))
if (/*newmapnum != 1 &&*/ M_MapLocked(newmapnum))
{
ischeating = true;
}

View file

@ -355,6 +355,7 @@ struct respawnvars_t
fixed_t pointx; // Respawn position coords to go towards
fixed_t pointy;
fixed_t pointz;
angle_t pointangle; // Only used when wp is NULL
boolean flip; // Flip upside down or not
tic_t timer; // Time left on respawn animation once you're there
tic_t airtimer; // Time spent in the air before respawning

View file

@ -2791,7 +2791,8 @@ static void readcondition(UINT16 set, UINT32 id, char *word2)
else if ((offset=0) || fastcmp(params[0], "ADDON")
|| (++offset && fastcmp(params[0], "CREDITS"))
|| (++offset && fastcmp(params[0], "REPLAY"))
|| (++offset && fastcmp(params[0], "CRASH")))
|| (++offset && fastcmp(params[0], "CRASH"))
|| (++offset && fastcmp(params[0], "TUTORIALSKIP")))
{
//PARAMCHECK(1);
ty = UC_ADDON + offset;
@ -3469,6 +3470,11 @@ void readmaincfg(MYFILE *f, boolean mainfile)
titlemap = Z_StrDup(word2);
titlechanged = true;
}
else if (fastcmp(word, "TUTORIALCHALLENGEMAP"))
{
Z_Free(tutorialchallengemap);
tutorialchallengemap = Z_StrDup(word2);
}
else if (fastcmp(word, "HIDETITLEPICS") || fastcmp(word, "TITLEPICSHIDE"))
{
hidetitlepics = (boolean)(value != 0 || word2[0] == 'T' || word2[0] == 'Y');

View file

@ -246,11 +246,16 @@ extern INT32 g_localplayers[MAXSPLITSCREENPLAYERS];
extern char * titlemap;
extern boolean hidetitlepics;
extern char * bootmap; //bootmap for loading a map on startup
extern boolean looptitle;
extern char * bootmap; //bootmap for loading a map on startup
extern char * podiummap; // map to load for podium
extern boolean looptitle;
extern char * tutorialchallengemap; // map to load for tutorial skip
extern UINT8 tutorialchallenge;
#define TUTORIALSKIP_NONE 0
#define TUTORIALSKIP_FAILED 1
#define TUTORIALSKIP_INPROGRESS 2
// CTF colors.
extern UINT16 skincolor_redteam, skincolor_blueteam, skincolor_redring, skincolor_bluering;

View file

@ -83,7 +83,7 @@
gameaction_t gameaction;
gamestate_t gamestate = GS_NULL;
UINT8 ultimatemode = false;
boolean ultimatemode = false;
JoyType_t Joystick[MAXSPLITSCREENPLAYERS];
@ -170,11 +170,13 @@ tic_t timeinmap; // Ticker for time spent in level (used for levelcard display)
char * titlemap = NULL;
boolean hidetitlepics = false;
char * bootmap = NULL; //bootmap for loading a map on startup
boolean looptitle = true;
char * bootmap = NULL; //bootmap for loading a map on startup
char * podiummap = NULL; // map to load for podium
boolean looptitle = true;
char * tutorialchallengemap = NULL; // map to load for tutorial skip
UINT8 tutorialchallenge = TUTORIALSKIP_NONE;
UINT16 skincolor_redteam = SKINCOLOR_RED;
UINT16 skincolor_blueteam = SKINCOLOR_BLUE;
@ -717,14 +719,18 @@ INT32 G_MapNumber(const char * name)
name += 8;
if (strcasecmp("TITLE", name) == 0)
return NEXTMAP_TITLE;
if (strcasecmp("EVALUATION", name) == 0)
return NEXTMAP_EVALUATION;
if (strcasecmp("CREDITS", name) == 0)
return NEXTMAP_CREDITS;
if (strcasecmp("CEREMONY", name) == 0)
return NEXTMAP_CEREMONY;
if (strcasecmp("TITLE", name) == 0)
return NEXTMAP_TITLE;
if (strcasecmp("VOTING", name) == 0)
return NEXTMAP_VOTING;
if (strcasecmp("TUTORIALCHALLENGE", name) == 0)
return NEXTMAP_TUTORIALCHALLENGE;
return NEXTMAP_INVALID;
}
@ -2499,8 +2505,7 @@ void G_MovePlayerToSpawnOrCheatcheck(INT32 playernum)
rsp->pointx = pos.x;
rsp->pointy = pos.y;
rsp->pointz = pos.z;
players[playernum].mo->angle = Obj_GetCheckpointRespawnAngle(checkpoint);
rsp->pointangle = Obj_GetCheckpointRespawnAngle(checkpoint);
Obj_ActivateCheckpointInstantly(checkpoint);
@ -3354,7 +3359,7 @@ UINT32 G_TOLFlag(INT32 pgametype)
return 0;
}
UINT16 G_GetFirstMapOfGametype(UINT8 pgametype)
UINT16 G_GetFirstMapOfGametype(UINT16 pgametype)
{
UINT8 i = 0;
UINT16 mapnum = NEXTMAP_INVALID;
@ -3364,7 +3369,7 @@ UINT16 G_GetFirstMapOfGametype(UINT8 pgametype)
templevelsearch.typeoflevel = G_TOLFlag(pgametype);
templevelsearch.cupmode = (!(gametypes[pgametype]->rules & GTR_NOCUPSELECT));
templevelsearch.timeattack = false;
templevelsearch.tutorial = false;
templevelsearch.tutorial = (pgametype == GT_TUTORIAL);
templevelsearch.checklocked = true;
if (templevelsearch.cupmode)
@ -3618,6 +3623,14 @@ void G_UpdateVisited(void)
if (demo.playback)
return;
// For some reason, we don't want to update visitation flags.
if (prevmap != gamemap-1)
return;
// Neither for tutorial skip material
if (nextmapoverride == NEXTMAP_TUTORIALCHALLENGE+1 || tutorialchallenge != TUTORIALSKIP_NONE)
return;
// Check if every local player wiped out.
for (i = 0; i < MAXPLAYERS; i++)
{
@ -3627,7 +3640,7 @@ void G_UpdateVisited(void)
if (!P_IsLocalPlayer(&players[i])) // Not local.
continue;
if (players[i].spectator) // Not playing.
if (players[i].spectator == true) // Not playing.
continue;
if (players[i].pflags & PF_NOCONTEST) // Sonic after not surviving.
@ -3658,7 +3671,7 @@ void G_UpdateVisited(void)
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
M_UpdateUnlockablesAndExtraEmblems(true, true);
G_SaveGameData();
gamedata->deferredsave = true;
}
void G_HandleSaveLevel(boolean removecondition)
@ -4148,11 +4161,6 @@ static void G_DoCompleted(void)
gamedata->deferredsave = true;
}
// This isn't in the above block because other
// mechanisms can queue up a gamedata save.
if (gamedata->deferredsave)
G_SaveGameData();
// Then, update some important game state.
{
legitimateexit = false;
@ -4172,14 +4180,18 @@ static void G_DoCompleted(void)
G_SetGamestate(GS_NULL);
wipegamestate = GS_NULL;
prevmap = (INT16)(gamemap-1);
}
// Finally, if you're not exiting, guarantee NO CONTEST.
// We do this seperately from the loop above Challenges,
// so NOCONTEST-related Challenges don't fire on exitlevel.
for (i = 0; i < MAXPLAYERS; i++)
if (gametype == GT_TUTORIAL)
{
// Maybe one day there'll be another context in which
// there's no way to progress other than ACS, but for
// now, Tutorial is a hardcoded exception.
}
else for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] == false)
{
@ -4195,6 +4207,40 @@ static void G_DoCompleted(void)
}
// And lastly, everything in anticipation for Intermission/level change.
if (tutorialchallenge == TUTORIALSKIP_INPROGRESS)
{
if (
!legitimateexit
|| !players[consoleplayer].exiting
|| K_IsPlayerLosing(&players[consoleplayer])
)
{
// Return to whence you came with your tail between your legs
tutorialchallenge = TUTORIALSKIP_FAILED;
G_SetGametype(GT_TUTORIAL);
nextmapoverride = prevmap+1;
}
else
{
// Proceed.
nextmapoverride = NEXTMAP_TITLE+1;
gamedata->finishedtutorialchallenge = true;
M_UpdateUnlockablesAndExtraEmblems(true, true);
gamedata->deferredsave = true;
}
}
else
{
// The "else" might not be strictly needed, but I don't
// want the "challenge" map to be considered visited before it's your time.
// ~toast 161123 (5 years of srb2kart, woooouuuu)
prevmap = gamemap-1;
tutorialchallenge = TUTORIALSKIP_NONE;
}
if (!demo.playback)
{
// Set up power level gametype scrambles
@ -4219,6 +4265,11 @@ static void G_DoCompleted(void)
Y_StartIntermission();
G_UpdateVisited();
}
// This isn't in the above blocks because many
// mechanisms can queue up a gamedata save.
if (gamedata->deferredsave)
G_SaveGameData();
}
// See also F_EndCutscene, the only other place which handles intra-map/ending transitions
@ -4265,6 +4316,27 @@ void G_AfterIntermission(void)
//
void G_NextLevel(void)
{
if (
gametype == GT_TUTORIAL
&& nextmap == NEXTMAP_TUTORIALCHALLENGE
&& !(gamedata && gamedata->enteredtutorialchallenge)
)
{
nextmap = G_MapNumber(tutorialchallengemap);
if (
nextmap < nummapheaders
&& mapheaderinfo[nextmap] != NULL
&& mapheaderinfo[nextmap]->typeoflevel != 0
)
{
tutorialchallenge = TUTORIALSKIP_INPROGRESS;
G_SetGametype(G_GuessGametypeByTOL(mapheaderinfo[nextmap]->typeoflevel));
gamedata->enteredtutorialchallenge = true;
// A gamedata save will happen on successful level enter
}
}
if (nextmap >= NEXTMAP_SPECIAL)
{
G_EndGame();
@ -4467,6 +4539,8 @@ typedef enum
GDEVER_SPECIAL = 1<<3,
GDEVER_KEYTUTORIAL = 1<<4,
GDEVER_KEYMAJORSKIP = 1<<5,
GDEVER_TUTORIALSKIP = 1<<6,
GDEVER_ENTERTUTSKIP = 1<<7,
} gdeverdone_t;
static const char *G_GameDataFolder(void)
@ -4606,6 +4680,8 @@ void G_LoadGameData(void)
gamedata->everseenspecial = !!(everflags & GDEVER_SPECIAL);
gamedata->chaokeytutorial = !!(everflags & GDEVER_KEYTUTORIAL);
gamedata->majorkeyskipattempted = !!(everflags & GDEVER_KEYMAJORSKIP);
gamedata->finishedtutorialchallenge = !!(everflags & GDEVER_TUTORIALSKIP);
gamedata->enteredtutorialchallenge = !!(everflags & GDEVER_ENTERTUTSKIP);
}
else
{
@ -5297,6 +5373,10 @@ void G_SaveGameData(void)
everflags |= GDEVER_KEYTUTORIAL;
if (gamedata->majorkeyskipattempted)
everflags |= GDEVER_KEYMAJORSKIP;
if (gamedata->finishedtutorialchallenge)
everflags |= GDEVER_TUTORIALSKIP;
if (gamedata->enteredtutorialchallenge)
everflags |= GDEVER_ENTERTUTSKIP;
WRITEUINT32(save.p, everflags); // 4
}
@ -5990,7 +6070,9 @@ INT32 G_FindMap(const char *mapname, char **foundmapnamep,
aprop = realmapname;
/* Now that we found a perfect match no need to fucking guess. */
if (strnicmp(realmapname, mapname, mapnamelen) == 0)
if (strnicmp(realmapname, mapname, mapnamelen) == 0
|| (mapheaderinfo[i]->menuttl[0]
&& strnicmp(mapheaderinfo[i]->menuttl, mapname, mapnamelen) == 0))
{
if (wanttable)
{
@ -6024,15 +6106,42 @@ INT32 G_FindMap(const char *mapname, char **foundmapnamep,
realmapname = 0;
}
}
else
if (mapheaderinfo[i]->menuttl[0] && ( aprop = strcasestr(mapheaderinfo[i]->menuttl, mapname) ))
{
if (wanttable)
{
writesimplefreq(freq, &freqc,
mapnum, aprop - mapheaderinfo[i]->menuttl, mapnamelen);
}
if (apromapnum == 0)
{
apromapnum = mapnum;
apromapname = realmapname;
realmapname = 0;
}
}
else/* ...match individual keywords */
{
freq[freqc].mapnum = mapnum;
measurekeywords(&freq[freqc],
&freq[freqc].matchd, &freq[freqc].matchc,
realmapname, mapname, wanttable);
measurekeywords(&freq[freqc],
&freq[freqc].keywhd, &freq[freqc].keywhc,
mapheaderinfo[i]->keywords, mapname, wanttable);
if (mapheaderinfo[i]->menuttl[0])
{
measurekeywords(&freq[freqc],
&freq[freqc].keywhd, &freq[freqc].keywhc,
mapheaderinfo[i]->menuttl, mapname, wanttable);
}
if (mapheaderinfo[i]->keywords[0])
{
measurekeywords(&freq[freqc],
&freq[freqc].keywhd, &freq[freqc].keywhc,
mapheaderinfo[i]->keywords, mapname, wanttable);
}
if (freq[freqc].total)
freqc++;
}

View file

@ -49,7 +49,8 @@ typedef enum
NEXTMAP_CREDITS = INT16_MAX-3,
NEXTMAP_CEREMONY = INT16_MAX-4,
NEXTMAP_VOTING = INT16_MAX-5,
NEXTMAP_INVALID = INT16_MAX-6, // Always last
NEXTMAP_TUTORIALCHALLENGE = INT16_MAX-6,
NEXTMAP_INVALID = INT16_MAX-7, // Always last
NEXTMAP_SPECIAL = NEXTMAP_INVALID
} nextmapspecial_t;
@ -279,7 +280,7 @@ FUNCMATH INT32 G_TicsToMilliseconds(tic_t tics);
// Don't split up TOL handling
UINT32 G_TOLFlag(INT32 pgametype);
UINT16 G_GetFirstMapOfGametype(UINT8 pgametype);
UINT16 G_GetFirstMapOfGametype(UINT16 pgametype);
UINT16 G_RandMap(UINT32 tolflags, UINT16 pprevmap, boolean ignoreBuffers, boolean callAgainSoon, UINT16 *extBuffer);
void G_AddMapToBuffer(UINT16 map);

View file

@ -58,7 +58,7 @@ typedef enum
extern gamestate_t gamestate;
extern boolean titlemapinaction;
extern UINT8 ultimatemode; // was sk_insane
extern boolean ultimatemode; // was sk_insane
extern gameaction_t gameaction;
void G_SetGamestate(gamestate_t newstate);

View file

@ -39,6 +39,9 @@ UINT8 numtargets = 0; // Capsules busted
INT32 K_StartingBumperCount(void)
{
if (tutorialchallenge == TUTORIALSKIP_INPROGRESS)
return 0;
if (battleprisons || K_CheckBossIntro())
{
if (grandprixinfo.gp)

View file

@ -71,8 +71,50 @@ void K_SetBot(UINT8 newplayernum, UINT8 skinnum, UINT8 difficulty, botStyle_e st
// For each subsequent round of GP, K_UpdateGrandPrixBots will handle this.
players[newplayernum].spectator = grandprixinfo.gp && grandprixinfo.initalize && K_BotDefaultSpectator();
players[newplayernum].skincolor = skins[skinnum].prefcolor;
sprintf(player_names[newplayernum], "%s", skins[skinnum].realname);
skincolornum_t color = static_cast<skincolornum_t>(skins[skinnum].prefcolor);
const char *realname = skins[skinnum].realname;
if (tutorialchallenge == TUTORIALSKIP_INPROGRESS)
{
// The ROYGBIV Rangers
switch (newplayernum)
{
case 1:
color = SKINCOLOR_RED;
realname = "Champ";
break;
case 2:
color = SKINCOLOR_ORANGE;
realname = "Pharaoh";
break;
case 3:
color = SKINCOLOR_YELLOW;
realname = "Caesar";
break;
case 4:
color = SKINCOLOR_GREEN;
realname = "General";
break;
case 5:
color = SKINCOLOR_CYAN; // blue (lighter than _BLUE)
realname = "Shogun";
break;
case 6:
color = SKINCOLOR_BLUEBERRY; // indigo
realname = "Emperor";
break;
case 7:
color = SKINCOLOR_VIOLET;
realname = "King";
break;
default:
color = SKINCOLOR_BLACK;
realname = "Vizier"; // working in the shadows
break;
}
}
players[newplayernum].skincolor = color;
sprintf(player_names[newplayernum], "%s", realname);
SetPlayerSkinByNum(newplayernum, skinnum);
playerconsole[newplayernum] = newplayernum;
@ -128,8 +170,8 @@ boolean K_AddBot(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *p)
void K_UpdateMatchRaceBots(void)
{
const UINT8 defaultbotskin = R_BotDefaultSkin();
const UINT8 difficulty = cv_kartbot.value;
UINT8 pmax = std::min<UINT8>((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), static_cast<UINT8>(cv_maxconnections.value));
UINT8 difficulty;
UINT8 pmax = (dedicated ? MAXPLAYERS-1 : MAXPLAYERS);
UINT8 numplayers = 0;
UINT8 numbots = 0;
UINT8 numwaiting = 0;
@ -145,9 +187,27 @@ void K_UpdateMatchRaceBots(void)
}
grabskins[usableskins] = MAXSKINS;
if (cv_maxplayers.value > 0)
if ((gametyperules & GTR_BOTS) == 0)
{
pmax = std::min<UINT8>(pmax, static_cast<UINT8>(cv_maxplayers.value));
difficulty = 0;
}
else if (tutorialchallenge == TUTORIALSKIP_INPROGRESS)
{
pmax = 8; // can you believe this is a nerf
difficulty = MAXBOTDIFFICULTY;
}
else if (K_CanChangeRules(true) == false)
{
difficulty = 0;
}
else
{
difficulty = cv_kartbot.value;
pmax = std::min<UINT8>(pmax, static_cast<UINT8>(cv_maxconnections.value));
if (cv_maxplayers.value > 0)
{
pmax = std::min<UINT8>(pmax, static_cast<UINT8>(cv_maxplayers.value));
}
}
for (i = 0; i < MAXPLAYERS; i++)
@ -180,9 +240,7 @@ void K_UpdateMatchRaceBots(void)
}
}
if (K_CanChangeRules(true) == false
|| (gametyperules & GTR_BOTS) == 0
|| difficulty == 0)
if (difficulty == 0)
{
// Remove bots if there are any.
wantedbots = 0;
@ -209,7 +267,11 @@ void K_UpdateMatchRaceBots(void)
}
// Rearrange usable bot skins list to prevent gaps for randomised selection
for (i = 0; i < usableskins; i++)
if (tutorialchallenge == TUTORIALSKIP_INPROGRESS)
{
usableskins = 0; // force a crack team of Eggrobo
}
else for (i = 0; i < usableskins; i++)
{
if (!(grabskins[i] == MAXSKINS || !R_SkinUsable(-1, grabskins[i], true)))
{

View file

@ -831,7 +831,7 @@ boolean K_CanChangeRules(boolean allowdemos)
return false;
}
if (gametype == GT_TUTORIAL)
if (gametype == GT_TUTORIAL || tutorialchallenge == TUTORIALSKIP_INPROGRESS)
{
// Tutorials are locked down.
return false;

View file

@ -2352,6 +2352,8 @@ static boolean K_drawKartPositionFaces(void)
if ((gametyperules & GTR_BUMPERS) && (players[rankplayer[i]].pflags & PF_ELIMINATED))
V_DrawScaledPatch(FACE_X-4, Y-3, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_ranknobumpers);
else if (K_Cooperative())
;
else if (gametyperules & GTR_CIRCUIT)
{
INT32 pos = players[rankplayer[i]].position;
@ -2360,7 +2362,7 @@ static boolean K_drawKartPositionFaces(void)
// Draws the little number over the face
V_DrawScaledPatch(FACE_X-5, Y+10, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_facenum[pos]);
}
else
else if (gametyperules & GTR_POINTLIMIT)
{
INT32 flags = V_HUDTRANS | V_SLIDEIN | V_SNAPTOLEFT;
@ -5526,7 +5528,7 @@ void K_drawKartHUD(void)
if (!battleprisons)
K_drawKartEmeralds();
}
else if (!islonesome)
else if (!islonesome && !K_Cooperative())
K_DrawKartPositionNum(stplyr->position);
}

View file

@ -396,6 +396,9 @@ boolean K_IsPlayerLosing(player_t *player)
if (specialstageinfo.valid == true)
return false; // anything short of DNF is COOL
if (tutorialchallenge == TUTORIALSKIP_INPROGRESS)
return true; // anything short of perfect is SUCK
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
@ -582,6 +585,15 @@ boolean K_TimeAttackRules(void)
return true;
}
if (gametype == GT_TUTORIAL)
{
// Tutorials are special. By default only one
// player will be playing... but sometimes bots
// can be spawned! So we still guarantee the
// changed behaviour for consistency.
return true;
}
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] == false || players[i].spectator == true)
@ -13076,6 +13088,12 @@ boolean K_Cooperative(void)
return true;
}
if (gametype == GT_TUTORIAL)
{
// Maybe this should be a rule. Eventually?
return true;
}
return false;
}

View file

@ -451,6 +451,8 @@ typedef enum
#endif
mpause_admin,
mpause_callvote,
mpause_giveup,
mpause_restartmap,
mpause_tryagain,
@ -1104,6 +1106,7 @@ extern consvar_t cv_dummyspectator;
// Bunch of funny functions for the pause menu...~
void M_RestartMap(INT32 choice); // Restart level (MP)
void M_TryAgain(INT32 choice); // Try again (SP)
void M_GiveUp(INT32 choice); // Give up (SP)
void M_ConfirmSpectate(INT32 choice); // Spectate confirm when you're alone
void M_ConfirmEnterGame(INT32 choice); // Enter game confirm when you're alone
void M_ConfirmSpectateChange(INT32 choice); // Splitscreen spectate/play menu func
@ -1133,7 +1136,9 @@ char *M_AddonsHeaderPath(void);
extern consvar_t cv_dummyaddonsearch;
extern consvar_t cv_dummyextraspassword;
#ifdef TODONEWMANUAL
void M_Manual(INT32 choice);
#endif
void M_HandleImageDef(INT32 choice);
// K_MENUDRAW.C

View file

@ -775,6 +775,7 @@ void M_Drawer(void)
if (menuwipe)
F_WipeStartScreen();
// background layer
if (menuactive)
{
if (gamestate == GS_MENU)
@ -785,7 +786,17 @@ void M_Drawer(void)
{
V_DrawFadeScreen(122, 3);
}
}
// draw pause pic
if (paused && !demo.playback && (menuactive || cv_showhud.value))
{
M_DrawPausedText(0);
}
// foreground layer
if (menuactive)
{
if (currentMenu->drawroutine)
currentMenu->drawroutine(); // call current menu Draw routine
@ -813,18 +824,6 @@ void M_Drawer(void)
menuwipe = false;
}
// draw pause pic
if (paused && !demo.playback && (menuactive || cv_showhud.value))
{
// Don't cover the Stereo player!
boolean stereo_open = menuactive && currentMenu == &MISC_SoundTestDef;
if (stereo_open == false)
{
M_DrawPausedText(0);
}
}
if (netgame && Playing())
{
boolean mainpause_open = menuactive && currentMenu == &PAUSE_MainDef;
@ -2909,7 +2908,7 @@ static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map)
V_DrawLSTitleLowString(x2, y+28, 0, word2);
}
static void M_DrawLevelSelectBlock(INT16 x, INT16 y, INT16 map, boolean redblink, boolean greyscale)
static void M_DrawLevelSelectBlock(INT16 x, INT16 y, UINT16 map, boolean redblink, boolean greyscale)
{
UINT8 *colormap = NULL;
@ -2928,6 +2927,20 @@ static void M_DrawLevelSelectBlock(INT16 x, INT16 y, INT16 map, boolean redblink
map,
colormap);
M_DrawHighLowLevelTitle(98+x, y+8, map);
if (levellist.levelsearch.tutorial && !(mapheaderinfo[map]->records.mapvisited & MV_BEATEN))
{
V_DrawScaledPatch(
x + 80 + 3, y + 50, 0,
W_CachePatchName(
va(
"CUPBKUP%c",
(greyscale ? '1' : '2')
),
PU_CACHE
)
);
}
}
void M_DrawLevelSelect(void)

View file

@ -980,9 +980,11 @@ void K_FinishCeremony(void)
g_podiumData.ranking = true;
// Play the noise now (via G_UpdateVisited's concluding gamedata save)
// Play the noise now (via G_UpdateVisited's concluding challenge check)
prevmap = gamemap-1;
G_UpdateVisited();
if (gamedata->deferredsave)
G_SaveGameData();
}
/*--------------------------------------------------

View file

@ -14,6 +14,7 @@
#include "d_player.h"
#include "k_kart.h"
#include "k_battle.h"
#include "k_objects.h" // Obj_FindCheckpoint, etc
#include "g_game.h"
#include "p_local.h"
#include "p_tick.h"
@ -163,6 +164,9 @@ void K_DoIngameRespawn(player_t *player)
K_TumbleInterrupt(player);
P_ResetPlayer(player);
mobj_t *checkpoint;
vector3_t pos;
// Set up respawn position if invalid
if (player->respawn.manual == true)
{
@ -194,6 +198,21 @@ void K_DoIngameRespawn(player_t *player)
K_RespawnAtWaypoint(player, player->respawn.wp);
}
}
else if ((gametyperules & GTR_CHECKPOINTS)
&& player->checkpointId
&& (checkpoint = Obj_FindCheckpoint(player->checkpointId))
&& Obj_GetCheckpointRespawnPosition(checkpoint, &pos))
{
player->respawn.wp = NULL;
player->respawn.flip = (checkpoint->flags2 & MF2_OBJECTFLIP) ? true : false; // K_RespawnOffset wants a boolean!
player->respawn.pointx = pos.x;
player->respawn.pointy = pos.y;
player->respawn.pointz = pos.z + K_RespawnOffset(player, player->respawn.flip);
player->respawn.pointangle = Obj_GetCheckpointRespawnAngle(checkpoint);
player->respawn.distanceleft = 0;
}
else
{
UINT32 bestdist = UINT32_MAX;
@ -244,6 +263,7 @@ void K_DoIngameRespawn(player_t *player)
player->respawn.pointx = 0;
player->respawn.pointy = 0;
player->respawn.pointz = 0;
player->respawn.pointangle = 0;
player->respawn.flip = false;
}
else
@ -254,7 +274,7 @@ void K_DoIngameRespawn(player_t *player)
player->respawn.pointx = beststart->x << FRACBITS;
player->respawn.pointy = beststart->y << FRACBITS;
player->mo->angle = ( beststart->angle * ANG1 );
player->respawn.pointangle = ( beststart->angle * ANG1 );
s = R_PointInSubsector(beststart->x << FRACBITS, beststart->y << FRACBITS)->sector;
@ -476,6 +496,11 @@ static void K_MovePlayerToRespawnPoint(player_t *player)
else
{
// We can now drop!
if (gametyperules & GTR_CHECKPOINTS)
{
// Of course, in gametypes where there's a clear and intended progression, set our direction.
P_SetPlayerAngle(player, (player->drawangle = player->respawn.pointangle));
}
player->respawn.state = RESPAWNST_DROP;
return;
}

View file

@ -183,7 +183,7 @@ static kartitems_t K_KartItemReelSpecialEnd[] =
KITEM_NONE
};
static kartitems_t K_KartItemReelTimeAttack[] =
static kartitems_t K_KartItemReelRingSneaker[] =
{
KITEM_SNEAKER,
KITEM_SUPERRING,
@ -1228,7 +1228,7 @@ static void K_CalculateRouletteSpeed(itemroulette_t *const roulette)
fixed_t progress = 0;
fixed_t total = 0;
if (bossinfo.valid == true)
if (K_CheckBossIntro() == true)
{
// Boss in action, use a speed controlled by boss health
total = FixedDiv(bossinfo.healthbar, BOSSHEALTHBARLEN);
@ -1345,7 +1345,7 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet
return;
}
}
else if (gametyperules & GTR_BOSS)
else if (K_CheckBossIntro() == true)
{
for (i = 0; K_KartItemReelBoss[i] != KITEM_NONE; i++)
{
@ -1356,10 +1356,10 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet
}
else if (K_TimeAttackRules() == true)
{
kartitems_t *presetlist = K_KartItemReelTimeAttack;
kartitems_t *presetlist = K_KartItemReelRingSneaker;
// If the objective is not to go fast, it's to cause serious damage.
if (gametyperules & GTR_PRISONS)
if (battleprisons == true)
{
presetlist = K_KartItemReelBreakTheCapsules;
}

View file

@ -291,7 +291,11 @@ void level_tally_t::Init(player_t *player)
owner = player;
gt = gametype;
const boolean game_over = ((player->pflags & PF_LOSTLIFE) == PF_LOSTLIFE);
const boolean game_over = (
G_GametypeUsesLives()
? ((player->pflags & PF_LOSTLIFE) == PF_LOSTLIFE)
: (tutorialchallenge == TUTORIALSKIP_INPROGRESS && K_IsPlayerLosing(player))
);
time = std::min(static_cast<INT32>(player->realtime), (100 * 60 * TICRATE) - 1);
ringPool = player->totalring;
@ -384,7 +388,14 @@ void level_tally_t::Init(player_t *player)
{
if (game_over == true)
{
if (player->lives <= 0)
if (tutorialchallenge == TUTORIALSKIP_INPROGRESS)
{
snprintf(
header, sizeof header,
"NICE TRY"
);
}
else if (G_GametypeUsesLives() && player->lives <= 0)
{
snprintf(
header, sizeof header,
@ -991,7 +1002,7 @@ void level_tally_t::Draw(void)
|| state == TALLY_ST_GAMEOVER_LIVES
|| state == TALLY_ST_GAMEOVER_DONE)
{
if (owner->lives > 0)
if (G_GametypeUsesLives() && owner->lives > 0)
{
srb2::Draw lives_drawer = drawer
.xy(

View file

@ -219,6 +219,9 @@ int LUA_PushGlobals(lua_State *L, const char *word)
} else if (fastcmp(word,"podiummap")) {
lua_pushstring(L, podiummap);
return 1;
} else if (fastcmp(word,"tutorialchallengemap")) {
lua_pushstring(L, tutorialchallengemap);
return 1;
// end map vars
// begin CTF colors
} else if (fastcmp(word,"skincolor_redteam")) {

View file

@ -661,6 +661,8 @@ void M_ClearStats(void)
gamedata->evercrashed = false;
gamedata->chaokeytutorial = false;
gamedata->majorkeyskipattempted = false;
gamedata->enteredtutorialchallenge = false;
gamedata->finishedtutorialchallenge = false;
gamedata->musicstate = GDMUSIC_NONE;
gamedata->importprofilewins = false;
@ -1499,6 +1501,8 @@ boolean M_CheckCondition(condition_t *cn, player_t *player)
return true;
}
return false;
case UC_TUTORIALSKIP:
return (gamedata->finishedtutorialchallenge == true);
case UC_PASSWORD:
return (cn->stringvar == NULL);
@ -2307,6 +2311,8 @@ static const char *M_GetConditionString(condition_t *cn)
if (gamedata->evercrashed)
return "launch \"Dr. Robotnik's Ring Racers\" again after a game crash";
return NULL;
case UC_TUTORIALSKIP:
return "successfully skip the Tutorial";
case UC_PASSWORD:
return "enter a secret password";

View file

@ -63,6 +63,7 @@ typedef enum
UC_CREDITS, // Finish watching the credits
UC_REPLAY, // Save a replay
UC_CRASH, // Hee ho !
UC_TUTORIALSKIP, // Complete the Tutorial Challenge
UC_PASSWORD, // Type in something funny
@ -363,6 +364,8 @@ struct gamedata_t
boolean evercrashed;
boolean chaokeytutorial;
boolean majorkeyskipattempted;
boolean enteredtutorialchallenge;
boolean finishedtutorialchallenge;
gdmusic_t musicstate;
// BACKWARDS COMPAT ASSIST

View file

@ -90,18 +90,7 @@ void M_InitExtras(INT32 choice)
// Tutorial
{
levelsearch_t templevelsearch;
UINT8 i = 0;
INT16 map;
templevelsearch.cup = NULL;
templevelsearch.typeoflevel = G_TOLFlag(GT_TUTORIAL);
templevelsearch.cupmode = false;
templevelsearch.timeattack = false;
templevelsearch.tutorial = true;
templevelsearch.checklocked = true;
map = M_GetFirstLevelInList(&i, &templevelsearch);
UINT16 map = G_GetFirstMapOfGametype(GT_TUTORIAL);
EXTRAS_Main[extras_tutorial].status = (IT_STRING |
((map == NEXTMAP_INVALID) ? IT_TRANSTEXT : IT_CALL));

View file

@ -80,7 +80,11 @@ static void M_StatisticsMaps(void)
headerexists = false;
for (i = 0; i < nummapheaders; i++)
{
M_StatisticsAddMap(i, NULL, &headerexists, true);
if (M_StatisticsAddMap(i, NULL, &headerexists, true))
{
if (!(mapheaderinfo[i]->records.mapvisited & MV_BEATEN))
break;
}
}
if ((i = statisticsmenu.numextramedals) != 0)

View file

@ -31,8 +31,10 @@ menuitem_t OPTIONS_Main[] =
{IT_STRING | IT_SUBMENU, "Data Options", "Miscellaneous data options such as the screenshot format.",
NULL, {.submenu = &OPTIONS_DataDef}, 0, 0},
#ifdef TODONEWMANUAL
{IT_STRING | IT_CALL, "Tricks & Secrets", "Those who bother reading a game manual always get the edge over those who don't!",
NULL, {.routine = M_Manual}, 0, 0},
#endif
};
// For options menu, the 'extra1' field will determine the background colour to use for... the background! (What a concept!)

View file

@ -110,7 +110,6 @@ void M_MPSetupNetgameMapSelect(INT32 choice)
levellist.netgame = true;
// Make sure we reset those
levellist.levelsearch.timeattack = false;
levellist.levelsearch.tutorial = false;
levellist.levelsearch.checklocked = true;
cupgrid.grandprix = false;

View file

@ -125,9 +125,25 @@ UINT16 M_CountLevelsToShowInList(levelsearch_t *levelsearch)
}
for (i = 0; i < nummapheaders; i++)
{
if (M_CanShowLevelInList(i, levelsearch))
{
count++;
// Tutorial will only show what you've made your way to
if (!levelsearch->checklocked)
continue;
if (!levelsearch->tutorial)
continue;
if (i >= basenummapheaders)
continue;
if (mapheaderinfo[i]->records.mapvisited & MV_BEATEN)
continue;
break;
}
}
return count;
}
@ -188,6 +204,13 @@ UINT16 M_GetNextLevelInList(UINT16 mapnum, UINT8 *i, levelsearch_t *levelsearch)
}
else
{
// Tutorial will only show what you've made your way to
if (levelsearch->checklocked
&& levelsearch->tutorial
&& mapnum < basenummapheaders
&& !(mapheaderinfo[mapnum]->records.mapvisited & MV_BEATEN))
return NEXTMAP_INVALID;
mapnum++;
while (!M_CanShowLevelInList(mapnum, levelsearch) && mapnum < nummapheaders)
mapnum++;
@ -215,6 +238,7 @@ boolean M_LevelListFromGametype(INT16 gt)
{
static boolean first = true;
UINT8 temp = 0;
boolean invalidatedcursor = false;
if (gt != -1)
{
@ -244,6 +268,15 @@ boolean M_LevelListFromGametype(INT16 gt)
}
levellist.levelsearch.cupmode = (!(gametypes[gt]->rules & GTR_NOCUPSELECT));
if (!levellist.levelsearch.cupmode)
{
invalidatedcursor = (
levellist.levelsearch.cup != NULL
|| levellist.levelsearch.tutorial != (gt == GT_TUTORIAL)
);
}
levellist.levelsearch.tutorial = (gt == GT_TUTORIAL);
CV_SetValue(&cv_dummyspbattack, 0);
}
@ -412,19 +445,36 @@ boolean M_LevelListFromGametype(INT16 gt)
// Okay, just a list of maps then.
if (M_GetFirstLevelInList(&temp, &levellist.levelsearch) == NEXTMAP_INVALID)
levellist.levelsearch.cup = NULL;
UINT16 test = M_GetFirstLevelInList(&temp, &levellist.levelsearch);
if (test == NEXTMAP_INVALID)
{
return false;
}
// Reset position properly if you go back & forth between gametypes
if (levellist.levelsearch.cup)
levellist.mapcount = M_CountLevelsToShowInList(&levellist.levelsearch);
if (levellist.levelsearch.tutorial && levellist.levelsearch.checklocked)
{
// Find the first level we haven't played.
UINT16 possiblecursor = 0;
while (test < nummapheaders && (mapheaderinfo[test]->records.mapvisited & MV_BEATEN))
{
test = M_GetNextLevelInList(test, &temp, &levellist.levelsearch);
possiblecursor++;
}
if (test != NEXTMAP_INVALID)
levellist.cursor = possiblecursor;
}
else if (invalidatedcursor)
{
levellist.cursor = 0;
levellist.levelsearch.cup = NULL;
}
levellist.mapcount = M_CountLevelsToShowInList(&levellist.levelsearch);
M_LevelSelectScrollDest();
levellist.y = levellist.dest;
@ -450,7 +500,6 @@ void M_LevelSelectInit(INT32 choice)
// Make sure this is reset as we'll only be using this function for offline games!
levellist.netgame = false;
levellist.levelsearch.checklocked = true;
levellist.levelsearch.tutorial = (gt == GT_TUTORIAL);
switch (currentMenu->menuitems[itemOn].mvar1)
{

View file

@ -64,6 +64,7 @@ void M_HandleImageDef(INT32 choice)
}
// Opening manual
#ifdef TODONEWMANUAL
void M_Manual(INT32 choice)
{
(void)choice;
@ -71,3 +72,4 @@ void M_Manual(INT32 choice)
MISC_ManualDef.prevMenu = (choice == INT32_MAX ? NULL : currentMenu);
M_SetupNextMenu(&MISC_ManualDef, true);
}
#endif

View file

@ -40,6 +40,9 @@ menuitem_t PAUSE_Main[] =
{IT_STRING | IT_ARROWS, "CALL VOTE", "M_ICOVOT",
NULL, {.routine = M_HandlePauseMenuCallVote}, 0, 0},
{IT_STRING | IT_CALL, "GIVE UP", "M_ICOGUP",
NULL, {.routine = M_GiveUp}, 0, 0},
{IT_STRING | IT_CALL, "RESTART MAP", "M_ICORE",
NULL, {.routine = M_RestartMap}, 0, 0},
@ -130,6 +133,8 @@ void M_OpenPauseMenu(void)
#ifdef HAVE_DISCORDRPC
PAUSE_Main[mpause_discordrequests].status = IT_DISABLED;
#endif
PAUSE_Main[mpause_giveup].status = IT_DISABLED;
PAUSE_Main[mpause_restartmap].status = IT_DISABLED;
PAUSE_Main[mpause_tryagain].status = IT_DISABLED;
@ -172,19 +177,33 @@ void M_OpenPauseMenu(void)
}
else if (!netgame && !demo.playback)
{
boolean retryallowed = (modeattacking != ATTACKING_NONE || gametype == GT_TUTORIAL);
if (
retryallowed == false
&& gamestate == GS_LEVEL
&& G_GametypeUsesLives()
)
boolean retryallowed = (modeattacking != ATTACKING_NONE);
boolean giveup = (
grandprixinfo.gp == true
&& grandprixinfo.eventmode != GPEVENT_NONE
&& roundqueue.size != 0
);
if (tutorialchallenge == TUTORIALSKIP_INPROGRESS)
{
for (i = 0; i <= splitscreen; i++)
// NO RETRY, ONLY GIVE UP
giveup = true;
}
else if (gamestate == GS_LEVEL && !retryallowed)
{
if (gametype == GT_TUTORIAL)
{
if (players[g_localplayers[i]].lives <= 1)
continue;
retryallowed = true;
break;
}
else if (G_GametypeUsesLives())
{
for (i = 0; i <= splitscreen; i++)
{
if (players[g_localplayers[i]].lives <= 1)
continue;
retryallowed = true;
break;
}
}
}
@ -192,6 +211,11 @@ void M_OpenPauseMenu(void)
{
PAUSE_Main[mpause_tryagain].status = IT_STRING | IT_CALL;
}
if (giveup)
{
PAUSE_Main[mpause_giveup].status = IT_STRING | IT_CALL;
}
}
if (netgame) // && (PAUSE_Main[mpause_admin].status == IT_DISABLED))
@ -398,6 +422,35 @@ void M_TryAgain(INT32 choice)
}
}
static void M_GiveUpResponse(INT32 ch)
{
if (ch != MA_YES)
return;
if (exitcountdown != 1)
{
G_BeginLevelExit();
exitcountdown = 1;
if (server)
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
M_ClearMenus(false);
}
void M_GiveUp(INT32 choice)
{
(void)choice;
if (demo.playback)
return;
if (!Playing())
return;
M_StartMessage("Give up", M_GetText("Are you sure you want to\ngive up on this challenge?\n"), &M_GiveUpResponse, MM_YESNO, NULL, NULL);
}
// Pause spectate / join functions
void M_ConfirmSpectate(INT32 choice)
{

View file

@ -1913,7 +1913,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
// spectating. Because in Free Play, this player
// can enter the game again, and these flags would
// make them intangible.
if (K_Cooperative() && !target->player->spectator)
if (!(gametyperules & GTR_CHECKPOINTS) && K_Cooperative() && !target->player->spectator)
{
target->player->pflags |= PF_ELIMINATED;
@ -2570,7 +2570,7 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source,
player->roundconditions.checkthisframe = true;
}
if (gametyperules & GTR_BUMPERS)
if (gametyperules & (GTR_BUMPERS|GTR_CHECKPOINTS))
{
player->mo->health--;
}

View file

@ -80,6 +80,7 @@ extern thinker_t thlist[];
extern mobj_t *mobjcache;
void P_InitThinkers(void);
void P_InvalidateThinkersWithoutInit(void);
void P_AddThinker(const thinklistnum_t n, thinker_t *thinker);
void P_RemoveThinker(thinker_t *thinker);
void P_UnlinkThinker(thinker_t *thinker);

View file

@ -12812,7 +12812,7 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing)
else if (mobj->z == mobj->floorz)
mobj->eflags |= MFE_ONGROUND;
mobj->angle = angle;
mobj->angle = p->drawangle = angle;
// FAULT
if (gamestate == GS_LEVEL && leveltime > introtime && !p->spectator)
@ -12827,6 +12827,7 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing)
p->respawn.pointx = x;
p->respawn.pointy = y;
p->respawn.pointz = z;
p->respawn.pointangle = angle;
}
P_AfterPlayerSpawn(playernum);
@ -12881,7 +12882,7 @@ void P_MovePlayerToCheatcheck(INT32 playernum)
}
}
else
p->drawangle = mobj->angle; // default to the camera angle
p->drawangle = mobj->angle = p->respawn.pointangle;
K_DoIngameRespawn(p);
p->respawn.truedeath = true;
@ -13055,6 +13056,9 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj)
skincolornum_t emcolor;
INT16 tagnum = mthing->tid;
if (tutorialchallenge == TUTORIALSKIP_INPROGRESS)
return false; // No out-of-sequence goodies
while (emblem)
{
if (emblem->type == ET_GLOBAL && emblem->tag == tagnum)
@ -13622,7 +13626,7 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj)
}
case MT_SPRAYCAN:
{
if (nummapspraycans == UINT8_MAX)
if (nummapspraycans == UINT8_MAX || tutorialchallenge == TUTORIALSKIP_INPROGRESS)
{
P_RemoveMobj(mobj);
return false;

View file

@ -7609,7 +7609,10 @@ static void P_InitLevelSettings(void)
gamespeed = grandprixinfo.gamespeed;
}
}
else if (modeattacking)
else if (
modeattacking != ATTACKING_NONE
|| tutorialchallenge == TUTORIALSKIP_INPROGRESS
)
{
if (gametyperules & GTR_CIRCUIT)
{
@ -8286,7 +8289,14 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
}
}
if (K_PodiumHasEmerald())
// Default
levelfadecol = 31;
if (gamestate == GS_TITLESCREEN)
{
;
}
else if (K_PodiumHasEmerald())
{
// Special Stage out
if (ranspecialwipe != 2)
@ -8316,11 +8326,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
levelfadecol = 0;
wipetype = wipe_encore_towhite;
}
else
{
// Default
levelfadecol = 31;
}
if (rendermode != render_none)
{
@ -8343,6 +8348,8 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
K_UnsetDialogue();
ACS_InvalidateMapScope();
LUA_InvalidateLevel();
for (ss = sectors; sectors+numsectors != ss; ss++)

View file

@ -4132,14 +4132,17 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha
}
else
{
// args[2]: cap rings to -20 instead of 0
SINT8 baseline = (args[2] ? -20 : 0);
// Don't push you below baseline
if (mo->player->rings <= 0)
if (mo->player->rings <= baseline)
return false;
rings = -(rings);
if (rings > mo->player->rings)
rings = mo->player->rings;
if (rings > (mo->player->rings - baseline))
rings = (mo->player->rings - baseline);
mo->player->rings -= rings;
S_StartSound(mo, sfx_antiri);

View file

@ -55,6 +55,8 @@
tic_t leveltime;
boolean thinkersCompleted;
UINT32 thinker_era = 0;
static boolean g_freezeCheat;
static boolean g_freezeLevel;
@ -278,6 +280,8 @@ void P_InitThinkers(void)
{
UINT8 i;
P_InvalidateThinkersWithoutInit();
for (i = 0; i < NUM_THINKERLISTS; i++)
{
thlist[i].prev = thlist[i].next = &thlist[i];
@ -299,6 +303,15 @@ void P_InitThinkers(void)
Obj_ResetCheckpoints();
}
//
// P_InvalidateThinkersWithoutInit
//
void P_InvalidateThinkersWithoutInit(void)
{
thinker_era++;
}
// Adds a new thinker at the end of the list.
void P_AddThinker(const thinklistnum_t n, thinker_t *thinker)
{
@ -855,15 +868,22 @@ void P_Ticker(boolean run)
ps_playerthink_time = I_GetPreciseTime() - ps_playerthink_time;
// TODO would this be laggy with more conditions in play...
if (((!demo.playback && leveltime > introtime && M_UpdateUnlockablesAndExtraEmblems(true, false))
|| (gamedata && gamedata->deferredsave)))
G_SaveGameData();
}
if (gamedata && gamestate == GS_LEVEL && !demo.playback)
{
// Keep track of how long they've been playing!
gamedata->totalplaytime++;
// Keep track of how long they've been playing!
if (!demo.playback) // Don't increment if a demo is playing.
gamedata->totalplaytime++;
// TODO would this be laggy with more conditions in play...
if (
(leveltime > introtime
&& M_UpdateUnlockablesAndExtraEmblems(true, false))
|| gamedata->deferredsave
)
{
G_SaveGameData();
}
}
}
if (run)
{
@ -1049,7 +1069,7 @@ void P_Ticker(boolean run)
}
player_t *player = &players[i];
if (K_PlayerTallyActive(player) == true && player->tally.done == false)
if (player->spectator == false && K_PlayerTallyActive(player) == true && player->tally.done == false)
{
run_exit_countdown = false;
break;
@ -1153,7 +1173,7 @@ void P_Ticker(boolean run)
P_RunChaseCameras();
}
if (run)
if (run && !levelloading && leveltime)
{
K_TickDialogue();
}

View file

@ -43,6 +43,8 @@ void P_PreTicker(INT32 frames);
void P_DoTeamscrambling(void);
void P_RemoveThinkerDelayed(thinker_t *thinker); //killed
extern UINT32 thinker_era;
mobj_t *P_SetTarget2(mobj_t **mo, mobj_t *target
#ifdef PARANOIA
, const char *source_file, int source_line

View file

@ -2824,7 +2824,7 @@ static void P_DeathThink(player_t *player)
}
}
if ((player->pflags & PF_ELIMINATED) && (gametyperules & GTR_BUMPERS))
if ((player->pflags & PF_ELIMINATED) /*&& (gametyperules & GTR_BUMPERS)*/)
{
playerGone = true;
}

View file

@ -1250,8 +1250,6 @@ static void ST_overlayDrawer(void)
}
K_DrawMidVote();
K_DrawDialogue();
}
void ST_DrawDemoTitleEntry(void)
@ -1558,6 +1556,8 @@ void ST_Drawer(void)
if (stagetitle)
ST_drawTitleCard();
K_DrawDialogue();
// Replay manual-save stuff
if (demo.recording && multiplayer && demo.savebutton && demo.savebutton + 3*TICRATE < leveltime)
{

View file

@ -1905,7 +1905,9 @@ void Y_DetermineIntermissionType(void)
// or for failing in time attack mode
|| (modeattacking && (players[consoleplayer].pflags & PF_NOCONTEST))
// or for explicit requested skip (outside of modeattacking)
|| (modeattacking == ATTACKING_NONE && skipstats != 0))
|| (modeattacking == ATTACKING_NONE && skipstats != 0)
// or tutorial skip material
|| (nextmapoverride == NEXTMAP_TUTORIALCHALLENGE+1 || tutorialchallenge != TUTORIALSKIP_NONE))
{
intertype = int_none;
return;