Implement lives system

Lose a life & restart the current race if you place below the top half. Lose all of your lives, and you get kicked to the title screen.
This commit is contained in:
Sally Coolatta 2020-05-13 02:14:39 -04:00
parent 63797b35f7
commit fa5fccffc5
16 changed files with 303 additions and 219 deletions

View file

@ -580,6 +580,7 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i)
// Score is resynched in the rspfirm resync packet // Score is resynched in the rspfirm resync packet
rsp->health = 0; // resynched with mo health rsp->health = 0; // resynched with mo health
rsp->lives = players[i].lives; rsp->lives = players[i].lives;
rsp->lostlife = players[i].lostlife;
rsp->continues = players[i].continues; rsp->continues = players[i].continues;
rsp->scoreadd = players[i].scoreadd; rsp->scoreadd = players[i].scoreadd;
rsp->xtralife = players[i].xtralife; rsp->xtralife = players[i].xtralife;
@ -710,6 +711,7 @@ static void resynch_read_player(resynch_pak *rsp)
// Score is resynched in the rspfirm resync packet // Score is resynched in the rspfirm resync packet
players[i].health = rsp->health; players[i].health = rsp->health;
players[i].lives = rsp->lives; players[i].lives = rsp->lives;
players[i].lostlife = rsp->lostlife;
players[i].continues = rsp->continues; players[i].continues = rsp->continues;
players[i].scoreadd = rsp->scoreadd; players[i].scoreadd = rsp->scoreadd;
players[i].xtralife = rsp->xtralife; players[i].xtralife = rsp->xtralife;

View file

@ -219,6 +219,7 @@ typedef struct
// Score is resynched in the confirm resync packet // Score is resynched in the confirm resync packet
INT32 health; INT32 health;
SINT8 lives; SINT8 lives;
boolean lostlife;
SINT8 continues; SINT8 continues;
UINT8 scoreadd; UINT8 scoreadd;
SINT8 xtralife; SINT8 xtralife;

View file

@ -5710,14 +5710,14 @@ void Command_ExitGame_f(void)
void Command_Retry_f(void) void Command_Retry_f(void)
{ {
if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING)) if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION))
{
CONS_Printf(M_GetText("You must be in a level to use this.\n")); CONS_Printf(M_GetText("You must be in a level to use this.\n"));
else if (netgame || multiplayer) }
CONS_Printf(M_GetText("This only works in single player.\n")); else if (netgame || grandprixinfo.roundnum == 0)
/*else if (!&players[consoleplayer] || players[consoleplayer].lives <= 1) {
CONS_Printf(M_GetText("You can't retry without any lives remaining!\n")); CONS_Printf(M_GetText("This only works in Grand Prix.\n"));
else if (G_IsSpecialStage(gamemap)) }
CONS_Printf(M_GetText("You can't retry special stages!\n"));*/
else else
{ {
M_ClearMenus(true); M_ClearMenus(true);

View file

@ -506,6 +506,7 @@ typedef struct player_s
UINT32 charflags; // Extra abilities/settings for skins (combinable stuff) UINT32 charflags; // Extra abilities/settings for skins (combinable stuff)
// See SF_ flags // See SF_ flags
SINT8 lives; SINT8 lives;
boolean lostlife;
SINT8 continues; // continues that player has acquired SINT8 continues; // continues that player has acquired
SINT8 xtralife; // Ring Extra Life counter SINT8 xtralife; // Ring Extra Life counter

View file

@ -2354,15 +2354,17 @@ void G_Ticker(boolean run)
if (gamestate == GS_LEVEL) if (gamestate == GS_LEVEL)
{ {
// Or, alternatively, retry. // Or, alternatively, retry.
if (!(netgame || multiplayer) && G_GetRetryFlag()) if (G_GetRetryFlag())
{ {
G_ClearRetryFlag(); G_ClearRetryFlag();
// Costs a life to retry ... unless the player in question is dead already. for (i = 0; i < MAXPLAYERS; i++)
/*if (G_GametypeUsesLives() && players[consoleplayer].playerstate == PST_LIVE) {
players[consoleplayer].lives -= 1; if (playeringame[i])
{
G_DoReborn(consoleplayer);*/ K_PlayerLoseLife(&players[i]);
}
}
D_MapChange(gamemap, gametype, (cv_kartencore.value == 1), true, 1, false, false); D_MapChange(gamemap, gametype, (cv_kartencore.value == 1), true, 1, false, false);
} }
@ -2569,6 +2571,7 @@ void G_PlayerReborn(INT32 player)
player_t *p; player_t *p;
INT32 score, marescore; INT32 score, marescore;
INT32 lives; INT32 lives;
boolean lostlife;
INT32 continues; INT32 continues;
// SRB2kart // SRB2kart
UINT8 kartspeed; UINT8 kartspeed;
@ -2613,6 +2616,7 @@ void G_PlayerReborn(INT32 player)
score = players[player].score; score = players[player].score;
marescore = players[player].marescore; marescore = players[player].marescore;
lives = players[player].lives; lives = players[player].lives;
lostlife = players[player].lostlife;
continues = players[player].continues; continues = players[player].continues;
ctfteam = players[player].ctfteam; ctfteam = players[player].ctfteam;
exiting = players[player].exiting; exiting = players[player].exiting;
@ -2697,6 +2701,7 @@ void G_PlayerReborn(INT32 player)
p->score = score; p->score = score;
p->marescore = marescore; p->marescore = marescore;
p->lives = lives; p->lives = lives;
p->lostlife = lostlife;
p->continues = continues; p->continues = continues;
p->pflags = pflags; p->pflags = pflags;
p->ctfteam = ctfteam; p->ctfteam = ctfteam;
@ -3217,6 +3222,47 @@ void G_ExitLevel(void)
{ {
if (gamestate == GS_LEVEL) if (gamestate == GS_LEVEL)
{ {
if (grandprixinfo.roundnum > 0 && grandprixinfo.wonround != true)
{
UINT8 i;
// You didn't win...
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator && !players[i].bot)
{
if (players[i].lives > 0)
{
break;
}
}
}
if (i == MAXPLAYERS)
{
// GAME OVER, try again from the start!
if (netgame)
{
; // restart cup here if we do online GP
}
else
{
D_QuitNetGame();
CL_Reset();
D_StartTitle();
}
}
else
{
// Go redo this course.
G_SetRetryFlag();
}
return;
}
gameaction = ga_completed; gameaction = ga_completed;
lastdraw = true; lastdraw = true;
@ -3292,18 +3338,15 @@ boolean G_IsSpecialStage(INT32 mapnum)
// //
boolean G_GametypeUsesLives(void) boolean G_GametypeUsesLives(void)
{ {
// SRB2kart NEEDS no lives if ((grandprixinfo.roundnum > 0) // In Grand Prix
#if 0 && (gametype == GT_RACE) // NOT in bonus round
// Coop, Competitive && !(modeattacking) // NOT in Record Attack
if ((gametype == GT_COOP || gametype == GT_COMPETITION) && !G_IsSpecialStage(gamemap)) // NOT in special stage
&& !modeattacking // No lives in Time Attack {
//&& !G_IsSpecialStage(gamemap)
&& !(maptol & TOL_NIGHTS)) // No lives in NiGHTS
return true; return true;
}
return false; return false;
#else
return false;
#endif
} }
// //
@ -4570,43 +4613,42 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, bool
if (!demo.playback && !netgame) // Netgame sets random seed elsewhere, demo playback sets seed just before us! if (!demo.playback && !netgame) // Netgame sets random seed elsewhere, demo playback sets seed just before us!
P_SetRandSeed(M_RandomizedSeed()); // Use a more "Random" random seed P_SetRandSeed(M_RandomizedSeed()); // Use a more "Random" random seed
//SRB2Kart - Score is literally the only thing you SHOULDN'T reset at all times // Clear a bunch of variables
//if (resetplayer) tokenlist = token = sstimer = redscore = bluescore = lastmap = 0;
racecountdown = exitcountdown = mapreset = 0;
for (i = 0; i < MAXPLAYERS; i++)
{ {
// Clear a bunch of variables players[i].playerstate = PST_REBORN;
tokenlist = token = sstimer = redscore = bluescore = lastmap = 0; players[i].starpostangle = players[i].starpostnum = players[i].starposttime = 0;
racecountdown = exitcountdown = mapreset = 0; players[i].starpostx = players[i].starposty = players[i].starpostz = 0;
for (i = 0; i < MAXPLAYERS; i++) // The latter two should clear by themselves, but just in case
players[i].pflags &= ~(PF_TAGIT|PF_TAGGED|PF_FULLSTASIS);
// Clear cheatcodes too, just in case.
players[i].pflags &= ~(PF_GODMODE|PF_NOCLIP|PF_INVIS);
players[i].marescore = 0;
if (resetplayer && !(multiplayer && demo.playback)) // SRB2Kart
{ {
players[i].playerstate = PST_REBORN; players[i].score = 0;
players[i].starpostangle = players[i].starpostnum = players[i].starposttime = 0;
players[i].starpostx = players[i].starposty = players[i].starpostz = 0;
players[i].lives = 3; // SRB2Kart if (grandprixinfo.roundnum == 0 || grandprixinfo.initalize == true)
// The latter two should clear by themselves, but just in case
players[i].pflags &= ~(PF_TAGIT|PF_TAGGED|PF_FULLSTASIS);
// Clear cheatcodes too, just in case.
players[i].pflags &= ~(PF_GODMODE|PF_NOCLIP|PF_INVIS);
players[i].marescore = 0;
if (resetplayer && !(multiplayer && demo.playback)) // SRB2Kart
{ {
players[i].score = 0; players[i].lives = 3;
} }
} }
// Reset unlockable triggers
unlocktriggers = 0;
// clear itemfinder, just in case
if (!dedicated) // except in dedicated servers, where it is not registered and can actually I_Error debug builds
CV_StealthSetValue(&cv_itemfinder, 0);
} }
// Reset unlockable triggers
unlocktriggers = 0;
// clear itemfinder, just in case
if (!dedicated) // except in dedicated servers, where it is not registered and can actually I_Error debug builds
CV_StealthSetValue(&cv_itemfinder, 0);
// internal game map // internal game map
// well this check is useless because it is done before (d_netcmd.c::command_map_f) // well this check is useless because it is done before (d_netcmd.c::command_map_f)
// but in case of for demos.... // but in case of for demos....

View file

@ -1,15 +1,3 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2007-2016 by John "JTE" Muniz.
// Copyright (C) 2011-2018 by Sonic Team Junior.
//
// 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_bot.c
/// \brief Basic bot handling
#include "doomdef.h" #include "doomdef.h"
#include "d_player.h" #include "d_player.h"
#include "g_game.h" #include "g_game.h"

View file

@ -1,14 +1,5 @@
// SONIC ROBO BLAST 2 #ifndef __K_BOT__
//----------------------------------------------------------------------------- #define __K_BOT__
// Copyright (C) 2007-2016 by John "JTE" Muniz.
// Copyright (C) 2012-2018 by Sonic Team Junior.
//
// 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_bot.h
/// \brief Basic bot handling
#include "k_waypoint.h" #include "k_waypoint.h"
#include "d_player.h" #include "d_player.h"
@ -28,3 +19,5 @@ boolean K_PlayerUsesBotMovement(player_t *player);
boolean K_BotCanTakeCut(player_t *player); boolean K_BotCanTakeCut(player_t *player);
fixed_t K_BotRubberband(player_t *player); fixed_t K_BotRubberband(player_t *player);
void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd); void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd);
#endif

View file

@ -230,3 +230,30 @@ void K_FakeBotResults(player_t *bot)
bot->realtime = bot->realtime + (bot->distancetofinish / distfactor); bot->realtime = bot->realtime + (bot->distancetofinish / distfactor);
bot->distancetofinish = 0; bot->distancetofinish = 0;
} }
void K_PlayerLoseLife(player_t *player)
{
if (!G_GametypeUsesLives())
{
return;
}
if (player->spectator || player->exiting || player->bot || player->lostlife)
{
return;
}
player->lives--;
player->lostlife = true;
#if 0
if (player->lives <= 0)
{
if (P_IsLocalPlayer(player))
{
S_StopMusic();
S_ChangeMusicInternal("gmover", false);
}
}
#endif
}

View file

@ -11,11 +11,12 @@ extern struct grandprixinfo
UINT8 gamespeed; ///< Copy of gamespeed, just to make sure you can't cheat it with cvars UINT8 gamespeed; ///< Copy of gamespeed, just to make sure you can't cheat it with cvars
boolean encore; ///< Ditto, but for encore mode boolean encore; ///< Ditto, but for encore mode
boolean masterbots; ///< If true, all bots should be max difficulty (Master Mode) boolean masterbots; ///< If true, all bots should be max difficulty (Master Mode)
boolean initbots; ///< If true, we need to initialize the bots that are competing. boolean initalize; ///< If true, we need to initialize a new cup.
boolean wonround; ///< If false, then we retry the map instead of going to the next.
} grandprixinfo; } grandprixinfo;
void K_InitGrandPrixBots(void); void K_InitGrandPrixBots(void);
void K_FakeBotResults(player_t *bot); void K_FakeBotResults(player_t *bot);
void K_PlayerLoseLife(player_t *player);
#endif #endif

View file

@ -9891,6 +9891,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
static void K_drawKartLapsAndRings(void) static void K_drawKartLapsAndRings(void)
{ {
const boolean uselives = G_GametypeUsesLives();
SINT8 ringanim_realframe = stplyr->karthud[khud_ringframe]; SINT8 ringanim_realframe = stplyr->karthud[khud_ringframe];
INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT); INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT);
UINT8 rn[2]; UINT8 rn[2];
@ -9975,7 +9976,7 @@ static void K_drawKartLapsAndRings(void)
} }
// Rings // Rings
if (netgame) if (!uselives)
{ {
V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy-10, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[1]); V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy-10, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[1]);
if (flipflag) if (flipflag)
@ -9997,7 +9998,7 @@ static void K_drawKartLapsAndRings(void)
V_DrawScaledPatch(fr-12, fy-23, V_HUDTRANS|splitflags, kp_ringspblocksmall[stplyr->karthud[khud_ringspblock]]); V_DrawScaledPatch(fr-12, fy-23, V_HUDTRANS|splitflags, kp_ringspblocksmall[stplyr->karthud[khud_ringspblock]]);
// Lives // Lives
if (!netgame) if (uselives)
{ {
UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE); UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE);
V_DrawMappedPatch(fr+21, fy-13, V_HUDTRANS|splitflags, facemmapprefix[stplyr->skin], colormap); V_DrawMappedPatch(fr+21, fy-13, V_HUDTRANS|splitflags, facemmapprefix[stplyr->skin], colormap);
@ -10015,7 +10016,7 @@ static void K_drawKartLapsAndRings(void)
V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->laps, cv_numlaps.value)); V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->laps, cv_numlaps.value));
// Rings // Rings
if (netgame) if (!uselives)
V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringsticker[1]); V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringsticker[1]);
else else
V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringsticker[0]); V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringsticker[0]);
@ -10039,7 +10040,7 @@ static void K_drawKartLapsAndRings(void)
V_DrawScaledPatch(LAPS_X-5, LAPS_Y-28, V_HUDTRANS|splitflags, kp_ringspblock[stplyr->karthud[khud_ringspblock]]); V_DrawScaledPatch(LAPS_X-5, LAPS_Y-28, V_HUDTRANS|splitflags, kp_ringspblock[stplyr->karthud[khud_ringspblock]]);
// Lives // Lives
if (!netgame) if (uselives)
{ {
UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE); UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE);
V_DrawMappedPatch(LAPS_X+46, LAPS_Y-16, V_HUDTRANS|splitflags, facerankprefix[stplyr->skin], colormap); V_DrawMappedPatch(LAPS_X+46, LAPS_Y-16, V_HUDTRANS|splitflags, facerankprefix[stplyr->skin], colormap);

View file

@ -241,6 +241,8 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->charflags); lua_pushinteger(L, plr->charflags);
else if (fastcmp(field,"lives")) else if (fastcmp(field,"lives"))
lua_pushinteger(L, plr->lives); lua_pushinteger(L, plr->lives);
else if (fastcmp(field,"lostlife"))
lua_pushboolean(L, plr->lostlife);
else if (fastcmp(field,"continues")) else if (fastcmp(field,"continues"))
lua_pushinteger(L, plr->continues); lua_pushinteger(L, plr->continues);
else if (fastcmp(field,"xtralife")) else if (fastcmp(field,"xtralife"))
@ -489,6 +491,8 @@ static int player_set(lua_State *L)
plr->charflags = (UINT32)luaL_checkinteger(L, 3); plr->charflags = (UINT32)luaL_checkinteger(L, 3);
else if (fastcmp(field,"lives")) else if (fastcmp(field,"lives"))
plr->lives = (SINT8)luaL_checkinteger(L, 3); plr->lives = (SINT8)luaL_checkinteger(L, 3);
else if (fastcmp(field,"lostlife"))
plr->lostlife = luaL_checkboolean(L, 3);
else if (fastcmp(field,"continues")) else if (fastcmp(field,"continues"))
plr->continues = (SINT8)luaL_checkinteger(L, 3); plr->continues = (SINT8)luaL_checkinteger(L, 3);
else if (fastcmp(field,"xtralife")) else if (fastcmp(field,"xtralife"))

View file

@ -7649,7 +7649,9 @@ static void M_StartGrandPrix(INT32 choice)
grandprixinfo.cup = gpcup; grandprixinfo.cup = gpcup;
grandprixinfo.roundnum = 1; grandprixinfo.roundnum = 1;
grandprixinfo.initbots = true; grandprixinfo.wonround = false;
grandprixinfo.initalize = true;
G_DeferedInitNew( G_DeferedInitNew(
false, false,

View file

@ -29,6 +29,7 @@
#include "k_kart.h" // SRB2kart #include "k_kart.h" // SRB2kart
#include "k_battle.h" #include "k_battle.h"
#include "k_pwrlv.h" #include "k_pwrlv.h"
#include "k_grandprix.h"
// CTF player names // CTF player names
#define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : "" #define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : ""
@ -1972,71 +1973,133 @@ void P_CheckPointLimit(void)
// Checks whether or not to end a race netgame. // Checks whether or not to end a race netgame.
boolean P_CheckRacers(void) boolean P_CheckRacers(void)
{ {
INT32 i, j, numplayersingame = 0, numexiting = 0; UINT8 i;
UINT8 numplayersingame = 0;
UINT8 numexiting = 0;
boolean eliminatelast = cv_karteliminatelast.value;
boolean canexit = true;
boolean griefed = false; boolean griefed = false;
// Check if all the players in the race have finished. If so, end the level. // Check if all the players in the race have finished. If so, end the level.
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
if (!playeringame[i] || players[i].spectator || players[i].exiting || players[i].bot || !players[i].lives) if (nospectategrief[i] != -1) // prevent spectate griefing
continue; {
griefed = true;
}
break; if (!playeringame[i] || players[i].spectator || players[i].lives <= 0) // Not playing
{
// Y'all aren't even playing
continue;
}
numplayersingame++;
if (players[i].exiting || (players[i].pflags & PF_TIMEOVER))
{
numexiting++;
}
else
{
if (players[i].bot)
{
// Isn't a human, thus doesn't matter. (Sorry, robots.)
continue;
}
canexit = false;
}
} }
if (i == MAXPLAYERS) // finished if (canexit)
{ {
// Everyone's finished, we're done here!
racecountdown = exitcountdown = 0; racecountdown = exitcountdown = 0;
return true; return true;
} }
for (j = 0; j < MAXPLAYERS; j++) if (numplayersingame <= 1)
{ {
if (nospectategrief[j] != -1) // prevent spectate griefing // Never do this without enough players.
griefed = true; eliminatelast = false;
if (!playeringame[j] || players[j].spectator) }
continue; else
numplayersingame++; {
if (players[j].exiting) if (grandprixinfo.roundnum > 0)
numexiting++; {
// Always do this in GP
eliminatelast = true;
}
else if (griefed)
{
// Don't do this if someone spectated
eliminatelast = false;
}
} }
if (cv_karteliminatelast.value && numplayersingame > 1 && !griefed) if (eliminatelast == true && (numplayersingame <= numexiting-1))
{ {
// check if we just got unlucky and there was only one guy who was a problem // Everyone's done playing but one guy apparently.
for (j = i+1; j < MAXPLAYERS; j++) // Just kill everyone who is still playing.
for (i = 0; i < MAXPLAYERS; i++)
{ {
if (!playeringame[j] || players[j].spectator || players[j].exiting || !players[j].lives) if (!playeringame[i] || players[i].spectator || players[i].lives <= 0) // Not playing
{
// Y'all aren't even playing
continue; continue;
break; }
if (players[i].exiting || (players[i].pflags & PF_TIMEOVER))
{
// You're done, you're free to go.
continue;
}
P_DoTimeOver(&players[i]);
} }
if (j == MAXPLAYERS) // finish anyways, force a time over // Everyone should be done playing at this point now.
{ racecountdown = exitcountdown = 0;
P_DoTimeOver(&players[i]); return true;
racecountdown = exitcountdown = 0;
return true;
}
} }
if (!racecountdown) // Check to see if the winners have finished, to set countdown. // SO, we're not done playing.
// Let's see if it's time to start the death counter!
if (!racecountdown)
{ {
// If the winners are all done, then start the death timer.
UINT8 winningpos = 1; UINT8 winningpos = 1;
winningpos = max(1, numplayersingame/2); winningpos = max(1, numplayersingame/2);
if (numplayersingame % 2) // any remainder? if (numplayersingame % 2) // any remainder?
{
winningpos++; winningpos++;
}
if (numexiting >= winningpos) if (numexiting >= winningpos)
racecountdown = (((netgame || multiplayer) ? cv_countdowntime.value : 30)*TICRATE) + 1; // 30 seconds to finish, get going! {
tic_t countdown = 30*TICRATE; // 30 seconds left to finish, get going!
if (netgame)
{
// Custom timer
countdown = cv_countdowntime.value * TICRATE;
}
racecountdown = countdown + 1;
}
} }
if (numplayersingame < 2) // reset nospectategrief in free play // We're still playing, but no one else is, so we need to reset spectator griefing.
if (numplayersingame <= 1)
{ {
for (j = 0; j < MAXPLAYERS; j++) memset(nospectategrief, -1, sizeof (nospectategrief));
nospectategrief[j] = -1;
} }
// Turns out we're still having a good time & playing the game, we didn't have to do anything :)
return false; return false;
} }
@ -2250,20 +2313,6 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source)
target->flags |= MF_NOBLOCKMAP|MF_NOCLIPHEIGHT; target->flags |= MF_NOBLOCKMAP|MF_NOCLIPHEIGHT;
P_SetThingPosition(target); P_SetThingPosition(target);
if (!target->player->bot && !G_IsSpecialStage(gamemap) && G_GametypeUsesLives())
{
target->player->lives -= 1; // Lose a life Tails 03-11-2000
if (target->player->lives <= 0) // Tails 03-14-2000
{
if (P_IsLocalPlayer(target->player)/* && target->player == &players[consoleplayer] */)
{
S_StopMusic(); // Stop the Music! Tails 03-14-2000
S_ChangeMusicInternal("gmover", false); // Yousa dead now, Okieday? Tails 03-14-2000
}
}
}
target->player->playerstate = PST_DEAD; target->player->playerstate = PST_DEAD;
if (target->player == &players[consoleplayer]) if (target->player == &players[consoleplayer])

View file

@ -142,6 +142,7 @@ static void P_NetArchivePlayers(void)
WRITEFIXED(save_p, players[i].dashspeed); WRITEFIXED(save_p, players[i].dashspeed);
WRITEINT32(save_p, players[i].dashtime); WRITEINT32(save_p, players[i].dashtime);
WRITESINT8(save_p, players[i].lives); WRITESINT8(save_p, players[i].lives);
WRITEUINT8(save_p, players[i].lostlife);
WRITESINT8(save_p, players[i].continues); WRITESINT8(save_p, players[i].continues);
WRITESINT8(save_p, players[i].xtralife); WRITESINT8(save_p, players[i].xtralife);
WRITEUINT8(save_p, players[i].gotcontinue); WRITEUINT8(save_p, players[i].gotcontinue);
@ -327,6 +328,7 @@ static void P_NetUnArchivePlayers(void)
players[i].dashspeed = READFIXED(save_p); // dashing speed players[i].dashspeed = READFIXED(save_p); // dashing speed
players[i].dashtime = READINT32(save_p); // dashing speed players[i].dashtime = READINT32(save_p); // dashing speed
players[i].lives = READSINT8(save_p); players[i].lives = READSINT8(save_p);
players[i].lostlife = (boolean)READUINT8(save_p);
players[i].continues = READSINT8(save_p); // continues that player has acquired players[i].continues = READSINT8(save_p); // continues that player has acquired
players[i].xtralife = READSINT8(save_p); // Ring Extra Life counter players[i].xtralife = READSINT8(save_p); // Ring Extra Life counter
players[i].gotcontinue = READUINT8(save_p); // got continue from stage players[i].gotcontinue = READUINT8(save_p); // got continue from stage

View file

@ -2350,6 +2350,8 @@ static void P_LevelInitStuff(void)
memset(localaiming, 0, sizeof(localaiming)); memset(localaiming, 0, sizeof(localaiming));
grandprixinfo.wonround = false;
// special stage tokens, emeralds, and ring total // special stage tokens, emeralds, and ring total
tokenbits = 0; tokenbits = 0;
runemeraldmanager = false; runemeraldmanager = false;
@ -2399,6 +2401,7 @@ static void P_LevelInitStuff(void)
players[i].realtime = racecountdown = exitcountdown = 0; players[i].realtime = racecountdown = exitcountdown = 0;
curlap = bestlap = 0; // SRB2Kart curlap = bestlap = 0; // SRB2Kart
players[i].lostlife = false;
players[i].gotcontinue = false; players[i].gotcontinue = false;
players[i].xtralife = players[i].deadtimer = players[i].numboxes = players[i].totalring = players[i].laps = 0; players[i].xtralife = players[i].deadtimer = players[i].numboxes = players[i].totalring = players[i].laps = 0;
@ -3362,6 +3365,23 @@ boolean P_SetupLevel(boolean skipprecip)
} }
#endif #endif
// NOW you can try to spawn in the Battle capsules, if there's not enough players for a match
K_SpawnBattleCapsules();
if (grandprixinfo.roundnum != 0)
{
if (grandprixinfo.initalize == true)
{
K_InitGrandPrixBots();
grandprixinfo.initalize = false;
}
}
else if (!modeattacking)
{
// We're in a Match Race, use simplistic randomized bots.
K_UpdateMatchRaceBots();
}
P_MapEnd(); P_MapEnd();
// Remove the loading shit from the screen // Remove the loading shit from the screen
@ -3413,23 +3433,6 @@ boolean P_SetupLevel(boolean skipprecip)
#endif #endif
} }
// NOW you can try to spawn in the Battle capsules, if there's not enough players for a match
K_SpawnBattleCapsules();
if (grandprixinfo.roundnum != 0)
{
if (grandprixinfo.initbots == true)
{
K_InitGrandPrixBots();
grandprixinfo.initbots = false;
}
}
else if (!modeattacking)
{
// We're in a Match Race, use simplistic randomized bots.
K_UpdateMatchRaceBots();
}
return true; return true;
} }

View file

@ -40,13 +40,14 @@
#include "st_stuff.h" #include "st_stuff.h"
#include "lua_script.h" #include "lua_script.h"
#include "lua_hook.h" #include "lua_hook.h"
#include "k_bot.h"
// Objectplace // Objectplace
#include "m_cheat.h" #include "m_cheat.h"
// SRB2kart // SRB2kart
#include "m_cond.h" // M_UpdateUnlockablesAndExtraEmblems #include "m_cond.h" // M_UpdateUnlockablesAndExtraEmblems
#include "k_kart.h" #include "k_kart.h"
#include "console.h" // CON_LogMessage #include "console.h" // CON_LogMessage
#include "k_bot.h"
#include "k_grandprix.h"
#ifdef HW3SOUND #ifdef HW3SOUND
#include "hardware/hw3sound.h" #include "hardware/hw3sound.h"
@ -1681,12 +1682,20 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
// Player exits the map via sector trigger // Player exits the map via sector trigger
void P_DoPlayerExit(player_t *player) void P_DoPlayerExit(player_t *player)
{ {
const boolean losing = K_IsPlayerLosing(player);
if (player->exiting || mapreset) if (player->exiting || mapreset)
return; return;
if (P_IsLocalPlayer(player) && (!player->spectator && !demo.playback)) if (P_IsLocalPlayer(player) && (!player->spectator && !demo.playback))
legitimateexit = true; legitimateexit = true;
if (G_GametypeUsesLives() && losing)
{
// Remove a life from the losing player
K_PlayerLoseLife(player);
}
if (G_RaceGametype()) // If in Race Mode, allow if (G_RaceGametype()) // If in Race Mode, allow
{ {
player->exiting = raceexittime+2; player->exiting = raceexittime+2;
@ -1697,7 +1706,7 @@ void P_DoPlayerExit(player_t *player)
if (P_IsDisplayPlayer(player)) if (P_IsDisplayPlayer(player))
{ {
sfxenum_t sfx_id; sfxenum_t sfx_id;
if (K_IsPlayerLosing(player)) if (losing)
sfx_id = ((skin_t *)player->mo->skin)->soundsid[S_sfx[sfx_klose].skinsound]; sfx_id = ((skin_t *)player->mo->skin)->soundsid[S_sfx[sfx_klose].skinsound];
else else
sfx_id = ((skin_t *)player->mo->skin)->soundsid[S_sfx[sfx_kwin].skinsound]; sfx_id = ((skin_t *)player->mo->skin)->soundsid[S_sfx[sfx_kwin].skinsound];
@ -1705,7 +1714,7 @@ void P_DoPlayerExit(player_t *player)
} }
else else
{ {
if (K_IsPlayerLosing(player)) if (losing)
S_StartSound(player->mo, sfx_klose); S_StartSound(player->mo, sfx_klose);
else else
S_StartSound(player->mo, sfx_kwin); S_StartSound(player->mo, sfx_kwin);
@ -1715,10 +1724,6 @@ void P_DoPlayerExit(player_t *player)
if (cv_inttime.value > 0) if (cv_inttime.value > 0)
P_EndingMusic(player); P_EndingMusic(player);
// SRB2kart 120217
//if (!exitcountdown)
//exitcountdown = racecountdown + 8*TICRATE;
if (P_CheckRacers()) if (P_CheckRacers())
player->exiting = raceexittime+1; player->exiting = raceexittime+1;
} }
@ -1730,24 +1735,18 @@ void P_DoPlayerExit(player_t *player)
else else
player->exiting = raceexittime+2; // Accidental death safeguard??? player->exiting = raceexittime+2; // Accidental death safeguard???
//player->pflags &= ~PF_GLIDING; if (grandprixinfo.roundnum > 0 && !losing && !player->bot)
/* // SRB2kart - don't need
if (player->climbing)
{ {
player->climbing = 0; // YOU WIN
player->pflags |= PF_JUMPED; grandprixinfo.wonround = true;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
} }
*/
player->powers[pw_underwater] = 0; player->powers[pw_underwater] = 0;
player->powers[pw_spacetime] = 0; player->powers[pw_spacetime] = 0;
player->karthud[khud_cardanimation] = 0; // srb2kart: reset battle animation player->karthud[khud_cardanimation] = 0; // srb2kart: reset battle animation
if (player == &players[consoleplayer]) if (player == &players[consoleplayer])
demo.savebutton = leveltime; demo.savebutton = leveltime;
/*if (playeringame[player-players] && netgame && !circuitmap)
CONS_Printf(M_GetText("%s has completed the level.\n"), player_names[player-players]);*/
} }
#define SPACESPECIAL 12 #define SPACESPECIAL 12
@ -6995,14 +6994,9 @@ static void P_DeathThink(player_t *player)
K_KartPlayerHUDUpdate(player); K_KartPlayerHUDUpdate(player);
// Force respawn if idle for more than 30 seconds in shooter modes. if (player->lives > 0 && !(player->pflags & PF_TIMEOVER) && player->deadtimer > TICRATE)
if (player->lives > 0 /*&& leveltime >= starttime*/) // *could* you respawn?
{ {
// SRB2kart - spawn automatically after 1 second player->playerstate = PST_REBORN;
if (player->deadtimer > ((netgame || multiplayer)
? cv_respawntime.value*TICRATE
: TICRATE)) // don't let them change it in record attack
player->playerstate = PST_REBORN;
} }
// Keep time rolling // Keep time rolling
@ -8256,13 +8250,28 @@ static void P_CalcPostImg(player_t *player)
void P_DoTimeOver(player_t *player) void P_DoTimeOver(player_t *player)
{ {
if (netgame && player->health > 0) if (player->pflags & PF_TIMEOVER)
{
// NO! Don't do this!
return;
}
if (P_IsLocalPlayer(player) && !demo.playback)
{
legitimateexit = true; // SRB2kart: losing a race is still seeing it through to the end :p
}
if (netgame && !player->bot)
{
CON_LogMessage(va(M_GetText("%s ran out of time.\n"), player_names[player-players])); CON_LogMessage(va(M_GetText("%s ran out of time.\n"), player_names[player-players]));
}
player->pflags |= PF_TIMEOVER; player->pflags |= PF_TIMEOVER;
if (P_IsLocalPlayer(player) && !demo.playback) if (G_GametypeUsesLives())
legitimateexit = true; // SRB2kart: losing a race is still seeing it through to the end :p {
K_PlayerLoseLife(player);
}
if (player->mo) if (player->mo)
{ {
@ -8384,7 +8393,7 @@ void P_PlayerThink(player_t *player)
{ {
if (playeringame[i] && !players[i].spectator) if (playeringame[i] && !players[i].spectator)
{ {
if (!players[i].exiting && players[i].lives > 0) if (!players[i].exiting && !(players[i].pflags & PF_TIMEOVER) && players[i].lives > 0)
break; break;
} }
} }
@ -8392,24 +8401,26 @@ void P_PlayerThink(player_t *player)
if (i == MAXPLAYERS && player->exiting == raceexittime+2) // finished if (i == MAXPLAYERS && player->exiting == raceexittime+2) // finished
player->exiting = raceexittime+1; player->exiting = raceexittime+1;
#if 0
// If 10 seconds are left on the timer, // If 10 seconds are left on the timer,
// begin the drown music for countdown! // begin the drown music for countdown!
if (racecountdown == 11*TICRATE - 1)
// SRB2Kart: despite how perfect this is, it's disabled FOR A REASON
/*if (racecountdown == 11*TICRATE - 1)
{ {
if (P_IsLocalPlayer(player)) if (P_IsLocalPlayer(player))
S_ChangeMusicInternal("drown", false); S_ChangeMusicInternal("drown", false);
}*/ }
#endif
// If you've hit the countdown and you haven't made // If you've hit the countdown and you haven't made
// it to the exit, you're a goner! // it to the exit, you're a goner!
else if (racecountdown == 1 && !player->exiting && !player->spectator && player->lives > 0) if (racecountdown == 1 && !player->spectator && !player->exiting && !(player->pflags & PF_TIMEOVER) && player->lives > 0)
{ {
P_DoTimeOver(player); P_DoTimeOver(player);
if (player->playerstate == PST_DEAD) if (player->playerstate == PST_DEAD)
{
return; return;
}
} }
} }
@ -8423,33 +8434,9 @@ void P_PlayerThink(player_t *player)
if (player->exiting == 2 || exitcountdown == 2) if (player->exiting == 2 || exitcountdown == 2)
{ {
if (cv_playersforexit.value) // Count to be sure everyone's exited if (server)
{ {
INT32 i; SendNetXCmd(XD_EXITLEVEL, NULL, 0);
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator || players[i].bot)
continue;
if (players[i].lives <= 0)
continue;
if (!players[i].exiting || players[i].exiting > 3)
break;
}
if (i == MAXPLAYERS)
{
if (server)
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
else
player->exiting = 3;
}
else
{
if (server)
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
} }
} }
} }
@ -8487,28 +8474,9 @@ void P_PlayerThink(player_t *player)
player->health = 1; player->health = 1;
} }
#if 0
if ((netgame || multiplayer) && player->lives <= 0)
{
// In Co-Op, replenish a user's lives if they are depleted.
// of course, this is just a cheap hack, meh...
player->lives = cv_startinglives.value;
}
#else
player->lives = 1; // SRB2Kart
#endif
// SRB2kart 010217 // SRB2kart 010217
if (leveltime < starttime) if (leveltime < starttime)
player->powers[pw_nocontrol] = 2; player->powers[pw_nocontrol] = 2;
/*
if ((gametype == GT_RACE || gametype == GT_COMPETITION) && leveltime < 4*TICRATE)
{
cmd->buttons &= BT_BRAKE; // Remove all buttons except BT_BRAKE
cmd->forwardmove = 0;
cmd->sidemove = 0;
}
*/
// Synchronizes the "real" amount of time spent in the level. // Synchronizes the "real" amount of time spent in the level.
if (!player->exiting) if (!player->exiting)