Merge branch 'master' into trick-panels

This commit is contained in:
Sally Coolatta 2020-06-14 20:46:01 -04:00
commit 59add5abd4
51 changed files with 3486 additions and 1606 deletions

View file

@ -18,6 +18,7 @@ set(SRB2_CORE_SOURCES
g_game.c
g_input.c
g_splitscreen.c
font.c
hu_stuff.c
i_tcp.c
info.c
@ -73,6 +74,7 @@ set(SRB2_CORE_HEADERS
g_game.h
g_input.h
g_state.h
font.h
hu_stuff.h
i_joy.h
i_net.h
@ -167,6 +169,7 @@ set(SRB2_CORE_GAME_SOURCES
k_botitem.c
k_botsearch.c
k_respawn.c
k_grandprix.c
p_local.h
p_maputl.h
@ -188,6 +191,7 @@ set(SRB2_CORE_GAME_SOURCES
k_color.h
k_bot.h
k_respawn.h
k_grandprix.h
)
if(NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))

View file

@ -502,6 +502,7 @@ OBJS:=$(i_main_o) \
$(OBJDIR)/am_map.o \
$(OBJDIR)/command.o \
$(OBJDIR)/console.o \
$(OBJDIR)/font.o \
$(OBJDIR)/hu_stuff.o \
$(OBJDIR)/y_inter.o \
$(OBJDIR)/st_stuff.o \
@ -566,6 +567,7 @@ OBJS:=$(i_main_o) \
$(OBJDIR)/k_bot.o \
$(OBJDIR)/k_botitem.o \
$(OBJDIR)/k_botsearch.o \
$(OBJDIR)/k_grandprix.o\
$(i_cdmus_o) \
$(i_net_o) \
$(i_system_o) \
@ -813,7 +815,7 @@ $(OBJDIR)/v_video.o: v_video.c doomdef.h doomtype.h g_state.h m_swap.h r_local.h
tables.h m_fixed.h screen.h command.h m_bbox.h r_main.h d_player.h \
p_pspr.h info.h d_think.h sounds.h p_mobj.h doomdata.h d_ticcmd.h \
r_data.h r_defs.h r_state.h r_bsp.h r_segs.h r_plane.h r_sky.h \
r_things.h r_draw.h v_video.h hu_stuff.h d_event.h w_wad.h console.h \
r_things.h r_draw.h v_video.h font.h hu_stuff.h d_event.h w_wad.h console.h \
i_video.h z_zone.h doomstat.h d_clisrv.h d_netcmd.h
$(CC) $(CFLAGS) -fno-omit-frame-pointer $(WFLAGS) -c $< -o $@
endif

View file

@ -49,6 +49,7 @@
#include "k_battle.h"
#include "k_pwrlv.h"
#include "k_bot.h"
#include "k_grandprix.h"
#ifdef CLIENT_LOADINGSCREEN
// cl loading screen
@ -575,6 +576,7 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i)
// Score is resynched in the rspfirm resync packet
rsp->health = 0; // resynched with mo health
rsp->lives = players[i].lives;
rsp->lostlife = players[i].lostlife;
rsp->continues = players[i].continues;
rsp->scoreadd = players[i].scoreadd;
rsp->xtralife = players[i].xtralife;
@ -659,6 +661,8 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i)
// botvars_t
rsp->bot = players[i].bot;
rsp->bot_difficulty = players[i].botvars.difficulty;
rsp->bot_diffincrease = players[i].botvars.diffincrease;
rsp->bot_rival = players[i].botvars.rival;
rsp->bot_itemdelay = players[i].botvars.itemdelay;
rsp->bot_itemconfirm = players[i].botvars.itemconfirm;
rsp->bot_turnconfirm = players[i].botvars.turnconfirm;
@ -716,6 +720,7 @@ static void resynch_read_player(resynch_pak *rsp)
// Score is resynched in the rspfirm resync packet
players[i].health = rsp->health;
players[i].lives = rsp->lives;
players[i].lostlife = rsp->lostlife;
players[i].continues = rsp->continues;
players[i].scoreadd = rsp->scoreadd;
players[i].xtralife = rsp->xtralife;
@ -799,6 +804,8 @@ static void resynch_read_player(resynch_pak *rsp)
// botvars_t
players[i].bot = rsp->bot;
players[i].botvars.difficulty = rsp->bot_difficulty;
players[i].botvars.diffincrease = rsp->bot_diffincrease;
players[i].botvars.rival = rsp->bot_rival;
players[i].botvars.itemdelay = rsp->bot_itemdelay;
players[i].botvars.itemconfirm = rsp->bot_itemconfirm;
players[i].botvars.turnconfirm = rsp->bot_turnconfirm;
@ -2770,8 +2777,8 @@ void CL_RemovePlayer(INT32 playernum, INT32 reason)
RemoveAdminPlayer(playernum); // don't stay admin after you're gone
}
if (playernum == g_localplayers[0] && !demo.playback)
g_localplayers[0] = consoleplayer; // don't look through someone's view who isn't there
if (playernum == displayplayers[0] && !demo.playback)
displayplayers[0] = consoleplayer; // don't look through someone's view who isn't there
#ifdef HAVE_BLUA
LUA_InvalidatePlayer(&players[playernum]);
@ -5271,20 +5278,29 @@ static void Local_Maketic(INT32 realtics)
// game responder calls HU_Responder, AM_Responder, F_Responder,
// and G_MapEventsToControls
if (!dedicated) rendergametic = gametic;
// translate inputs (keyboard/mouse/joystick) into game controls
G_BuildTiccmd(&localcmds, realtics, 1);
localcmds.angleturn |= TICCMD_RECEIVED;
if (splitscreen)
{
G_BuildTiccmd(&localcmds2, realtics, 2);
localcmds2.angleturn |= TICCMD_RECEIVED;
if (splitscreen > 1)
{
G_BuildTiccmd(&localcmds3, realtics, 3);
localcmds3.angleturn |= TICCMD_RECEIVED;
if (splitscreen > 2)
{
G_BuildTiccmd(&localcmds4, realtics, 4);
localcmds4.angleturn |= TICCMD_RECEIVED;
}
}
}
localcmds.angleturn |= TICCMD_RECEIVED;
}
void SV_SpawnPlayer(INT32 playernum, INT32 x, INT32 y, angle_t angle)
@ -5321,11 +5337,30 @@ void SV_SpawnPlayer(INT32 playernum, INT32 x, INT32 y, angle_t angle)
static void SV_Maketic(void)
{
INT32 j;
boolean b[MAXPLAYERS];
memset(b, false, sizeof (b));
for (j = 0; j < MAXPLAYERS; j++)
{
if (K_PlayerUsesBotMovement(&players[j]))
{
b[j] = true;
K_BuildBotTiccmd(&players[j], &netcmds[maketic%TICQUEUE][j]);
}
}
for (j = 0; j < MAXNETNODES; j++)
{
if (playerpernode[j])
{
INT32 player = nodetoplayer[j];
if (b[player])
{
continue;
}
if ((netcmds[maketic%TICQUEUE][player].angleturn & TICCMD_RECEIVED) == 0)
{ // we didn't receive this tic
INT32 i;
@ -5346,6 +5381,7 @@ static void SV_Maketic(void)
}
}
}
}
// all tic are now proceed make the next
maketic++;

View file

@ -216,6 +216,7 @@ typedef struct
// Score is resynched in the confirm resync packet
INT32 health;
SINT8 lives;
boolean lostlife;
SINT8 continues;
UINT8 scoreadd;
SINT8 xtralife;
@ -297,6 +298,8 @@ typedef struct
// botvars_t
boolean bot;
UINT8 bot_difficulty;
UINT8 bot_diffincrease;
boolean bot_rival;
tic_t bot_itemdelay;
tic_t bot_itemconfirm;
SINT8 bot_turnconfirm;

View file

@ -75,6 +75,7 @@ int snprintf(char *str, size_t n, const char *fmt, ...);
#include "fastcmp.h"
#include "keys.h"
#include "filesrch.h" // refreshdirmenu
#include "k_grandprix.h"
#ifdef CMAKECONFIG
#include "config.h"
@ -753,6 +754,9 @@ void D_StartTitle(void)
// reset modeattacking
modeattacking = ATTACKING_NONE;
// Reset GP
memset(&grandprixinfo, 0, sizeof(struct grandprixinfo));
// empty maptol so mario/etc sounds don't play in sound test when they shouldn't
maptol = 0;
@ -1132,7 +1136,24 @@ void D_SRB2Main(void)
else
{
if (!M_CheckParm("-server"))
{
G_SetGameModified(true, true);
// Start up a "minor" grand prix session
memset(&grandprixinfo, 0, sizeof(struct grandprixinfo));
grandprixinfo.gamespeed = KARTSPEED_NORMAL;
grandprixinfo.encore = false;
grandprixinfo.masterbots = false;
grandprixinfo.gp = true;
grandprixinfo.roundnum = 0;
grandprixinfo.cup = NULL;
grandprixinfo.wonround = false;
grandprixinfo.initalize = true;
}
autostart = true;
}
}
@ -1515,18 +1536,46 @@ void D_SRB2Main(void)
INT16 newskill = -1;
const char *sskill = M_GetNextParm();
for (j = 0; kartspeed_cons_t[j].strvalue; j++)
if (!strcasecmp(kartspeed_cons_t[j].strvalue, sskill))
const UINT8 master = KARTSPEED_HARD+1;
const char *masterstr = "Master";
if (!strcasecmp(masterstr, sskill))
{
newskill = master;
}
else
{
for (j = 0; kartspeed_cons_t[j].strvalue; j++)
{
newskill = (INT16)kartspeed_cons_t[j].value;
break;
if (!strcasecmp(kartspeed_cons_t[j].strvalue, sskill))
{
newskill = (INT16)kartspeed_cons_t[j].value;
break;
}
}
if (!kartspeed_cons_t[j].strvalue) // reached end of the list with no match
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 <= master)
newskill = (INT16)j;
}
}
if (grandprixinfo.gp == true)
{
j = atoi(sskill); // assume they gave us a skill number, which is okay too
if (j >= KARTSPEED_EASY && j <= KARTSPEED_HARD)
newskill = (INT16)j;
if (newskill == master)
{
grandprixinfo.masterbots = true;
newskill = KARTSPEED_HARD;
}
grandprixinfo.gamespeed = newskill;
}
else if (newskill == master)
{
grandprixinfo.masterbots = true;
newskill = KARTSPEED_HARD;
}
if (newskill != -1)

View file

@ -52,6 +52,7 @@
#include "y_inter.h"
#include "k_color.h"
#include "k_respawn.h"
#include "k_grandprix.h"
#ifdef NETGAME_DEVMODE
#define CV_RESTRICT CV_NETVAR
@ -690,8 +691,6 @@ void D_RegisterServerCommands(void)
AddMServCommands();
// p_mobj.c
CV_RegisterVar(&cv_itemrespawntime);
CV_RegisterVar(&cv_itemrespawn);
CV_RegisterVar(&cv_flagtime);
CV_RegisterVar(&cv_suddendeath);
@ -2770,6 +2769,12 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r
if (netgame || multiplayer)
FLS = false;
if (grandprixinfo.gp == true)
{
// Too lazy to change the input value for every instance of this function.......
pencoremode = grandprixinfo.encore;
}
if (delay != 2)
{
UINT8 flags = 0;
@ -2917,6 +2922,7 @@ static void Command_Map_f(void)
INT32 newmapnum;
boolean newresetplayers, newencoremode;
INT32 newgametype = gametype;
boolean startgp = false;
// max length of command: map map03 -gametype race -noresetplayers -force -encore
// 1 2 3 4 5 6 7
@ -2947,6 +2953,7 @@ static void Command_Map_f(void)
if (COM_CheckParm("-force"))
{
G_SetGameModified(false, true);
startgp = true;
}
else
{
@ -2993,7 +3000,6 @@ static void Command_Map_f(void)
// new encoremode value
// use cvar by default
newencoremode = (cv_kartencore.value == 1);
if (COM_CheckParm("-encore"))
@ -3040,6 +3046,69 @@ static void Command_Map_f(void)
return;
}
if (startgp)
{
i = COM_CheckParm("-skill");
grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value);
grandprixinfo.masterbots = false;
if (i)
{
const UINT8 master = KARTSPEED_HARD+1;
const char *masterstr = "Master";
const char *skillname = COM_Argv(i+1);
INT32 newskill = -1;
INT32 j;
if (!strcasecmp(masterstr, skillname))
{
newskill = master;
}
else
{
for (j = 0; kartspeed_cons_t[j].strvalue; j++)
{
if (!strcasecmp(kartspeed_cons_t[j].strvalue, skillname))
{
newskill = (INT16)kartspeed_cons_t[j].value;
break;
}
}
if (!kartspeed_cons_t[j].strvalue) // reached end of the list with no match
{
j = atoi(COM_Argv(i+1)); // assume they gave us a skill number, which is okay too
if (j >= KARTSPEED_EASY && j <= master)
newskill = (INT16)j;
}
}
if (newskill != -1)
{
if (newskill == master)
{
grandprixinfo.gamespeed = KARTSPEED_HARD;
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;
grandprixinfo.initalize = true;
}
fromlevelselect = false;
D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, false);
}
@ -5063,14 +5132,16 @@ static void PointLimit_OnChange(void)
static void NumLaps_OnChange(void)
{
if (!G_RaceGametype() || (modeattacking || demo.playback))
if (K_CanChangeRules() == false)
{
return;
}
if (server && Playing()
&& (netgame || multiplayer)
&& (mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE)
&& (cv_numlaps.value > mapheaderinfo[gamemap - 1]->numlaps))
if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE)
&& (cv_numlaps.value > mapheaderinfo[gamemap - 1]->numlaps))
{
CV_StealthSetValue(&cv_numlaps, mapheaderinfo[gamemap - 1]->numlaps);
}
// Just don't be verbose
CONS_Printf(M_GetText("Number of laps set to %d\n"), cv_numlaps.value);
@ -5155,11 +5226,6 @@ void D_GameTypeChanged(INT32 lastgametype)
// There will always be a server, and this only needs to be done once.
if (server && (multiplayer || netgame))
{
if (gametype == GT_COMPETITION || gametype == GT_COOP)
CV_SetValue(&cv_itemrespawn, 0);
else if (!cv_itemrespawn.changed)
CV_SetValue(&cv_itemrespawn, 1);
switch (gametype)
{
case GT_MATCH:
@ -5170,8 +5236,6 @@ void D_GameTypeChanged(INT32 lastgametype)
CV_SetValue(&cv_pointlimit, 0);
CV_SetValue(&cv_timelimit, 120);
}
if (!cv_itemrespawntime.changed)
CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally
break;
case GT_TAG:
case GT_HIDEANDSEEK:
@ -5182,8 +5246,6 @@ void D_GameTypeChanged(INT32 lastgametype)
CV_SetValue(&cv_timelimit, 5);
CV_SetValue(&cv_pointlimit, 0);
}
if (!cv_itemrespawntime.changed)
CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally
break;
case GT_CTF:
if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits
@ -5192,17 +5254,12 @@ void D_GameTypeChanged(INT32 lastgametype)
CV_SetValue(&cv_timelimit, 0);
CV_SetValue(&cv_pointlimit, 5);
}
if (!cv_itemrespawntime.changed)
CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally
break;
}
}
else if (!multiplayer && !netgame)
{
gametype = GT_RACE; // SRB2kart
// These shouldn't matter anymore
//CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue);
//CV_SetValue(&cv_itemrespawn, 0);
gametype = GT_RACE;
}
// reset timelimit and pointlimit in race/coop, prevent stupid cheats
@ -5713,14 +5770,14 @@ void Command_ExitGame_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"));
else if (netgame || multiplayer)
CONS_Printf(M_GetText("This only works in single player.\n"));
/*else if (!&players[consoleplayer] || players[consoleplayer].lives <= 1)
CONS_Printf(M_GetText("You can't retry without any lives remaining!\n"));
else if (G_IsSpecialStage(gamemap))
CONS_Printf(M_GetText("You can't retry special stages!\n"));*/
}
else if (grandprixinfo.gp == false)
{
CONS_Printf(M_GetText("This only works in Grand Prix.\n"));
}
else
{
M_ClearMenus(true);
@ -6211,24 +6268,35 @@ static void Command_ShowTime_f(void)
// SRB2Kart: On change messages
static void BaseNumLaps_OnChange(void)
{
if (gamestate == GS_LEVEL)
if (K_CanChangeRules() == true)
{
if (cv_basenumlaps.value)
CONS_Printf(M_GetText("Number of laps will be changed to %d next round.\n"), cv_basenumlaps.value);
else
CONS_Printf(M_GetText("Number of laps will be changed to map defaults next round.\n"));
const char *str = va("%d", cv_basenumlaps.value);
if (cv_basenumlaps.value == 0)
{
str = "map defaults";
}
CONS_Printf(M_GetText("Number of laps will be changed to %s next round.\n"), str);
}
}
static void KartFrantic_OnChange(void)
{
if ((boolean)cv_kartfrantic.value != franticitems && gamestate == GS_LEVEL && leveltime > starttime)
CONS_Printf(M_GetText("Frantic items will be turned %s next round.\n"), cv_kartfrantic.value ? M_GetText("on") : M_GetText("off"));
if (K_CanChangeRules() == false)
{
return;
}
if (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;
}
else
{
CONS_Printf(M_GetText("Frantic items has been turned %s.\n"), cv_kartfrantic.value ? M_GetText("on") : M_GetText("off"));
franticitems = (boolean)cv_kartfrantic.value;
CONS_Printf(M_GetText("Frantic items will be turned %s next round.\n"), cv_kartfrantic.value ? M_GetText("on") : M_GetText("off"));
}
}
@ -6241,47 +6309,59 @@ static void KartSpeed_OnChange(void)
return;
}
if (G_RaceGametype())
if (K_CanChangeRules() == false)
{
if ((gamestate == GS_LEVEL && leveltime < starttime) && (cv_kartspeed.value != KARTSPEED_AUTO))
{
CONS_Printf(M_GetText("Game speed has been changed to \"%s\".\n"), cv_kartspeed.string);
gamespeed = (UINT8)cv_kartspeed.value;
}
else if (cv_kartspeed.value != (signed)gamespeed)
{
CONS_Printf(M_GetText("Game speed will be changed to \"%s\" next round.\n"), cv_kartspeed.string);
}
return;
}
if (leveltime < starttime && cv_kartspeed.value != KARTSPEED_AUTO)
{
CONS_Printf(M_GetText("Game speed has been changed to \"%s\".\n"), cv_kartspeed.string);
gamespeed = (UINT8)cv_kartspeed.value;
}
else
{
CONS_Printf(M_GetText("Game speed will be changed to \"%s\" next round.\n"), cv_kartspeed.string);
}
}
static void KartEncore_OnChange(void)
{
if (G_RaceGametype())
if (K_CanChangeRules() == false)
{
if ((cv_kartencore.value == 1) != encoremode && gamestate == GS_LEVEL /*&& leveltime > starttime*/)
CONS_Printf(M_GetText("Encore Mode will be set to %s next round.\n"), cv_kartencore.string);
else
CONS_Printf(M_GetText("Encore Mode has been set to %s.\n"), cv_kartencore.string);
return;
}
CONS_Printf(M_GetText("Encore Mode will be set to %s next round.\n"), cv_kartencore.string);
}
static void KartComeback_OnChange(void)
{
if (G_BattleGametype())
if (K_CanChangeRules() == false)
{
if ((boolean)cv_kartcomeback.value != comeback && gamestate == GS_LEVEL && leveltime > starttime)
CONS_Printf(M_GetText("Karma Comeback will be turned %s next round.\n"), cv_kartcomeback.value ? M_GetText("on") : M_GetText("off"));
else
{
CONS_Printf(M_GetText("Karma Comeback has been turned %s.\n"), cv_kartcomeback.value ? M_GetText("on") : M_GetText("off"));
comeback = (boolean)cv_kartcomeback.value;
}
return;
}
if (leveltime < starttime)
{
CONS_Printf(M_GetText("Karma Comeback has been turned %s.\n"), cv_kartcomeback.value ? M_GetText("on") : M_GetText("off"));
comeback = (boolean)cv_kartcomeback.value;
}
else
{
CONS_Printf(M_GetText("Karma Comeback will be turned %s next round.\n"), cv_kartcomeback.value ? M_GetText("on") : M_GetText("off"));
}
}
static void KartEliminateLast_OnChange(void)
{
if (G_RaceGametype() && cv_karteliminatelast.value)
if (K_CanChangeRules() == false)
{
CV_StealthSet(&cv_karteliminatelast, cv_karteliminatelast.defaultvalue);
}
if (G_RaceGametype())
{
P_CheckRacers();
}
}

View file

@ -60,10 +60,6 @@ extern consvar_t cv_usemouse2;
extern consvar_t cv_mouse2opt;
#endif
// normally in p_mobj but the .h is not read
extern consvar_t cv_itemrespawntime;
extern consvar_t cv_itemrespawn;
extern consvar_t cv_flagtime;
extern consvar_t cv_suddendeath;

View file

@ -315,8 +315,8 @@ typedef enum
k_stealingtimer, // You are stealing an item, this is your timer
k_stolentimer, // You are being stolen from, this is your timer
k_superring, // Spawn rings on top of you every tic!
k_sneakertimer, // Duration of the Sneaker Boost itself
k_levelbooster, // Duration of a level booster's boost (same as sneaker, but separated for boost stacking)
k_sneakertimer, // Duration of a Sneaker Boost (from Sneakers or level boosters)
k_numsneakers, // Number of stacked sneaker effects
k_growshrinktimer, // > 0 = Big, < 0 = small
k_squishedtimer, // Squished frame timer
k_rocketsneakertimer, // Rocket Sneaker duration timer
@ -390,9 +390,6 @@ typedef enum
NUMKARTHUD
} karthudtype_t;
// QUICKLY GET EITHER SNEAKER OR LEVEL BOOSTER SINCE THEY ARE FUNCTIONALLY IDENTICAL
#define EITHERSNEAKER(p) (p->kartstuff[k_sneakertimer] || p->kartstuff[k_levelbooster])
// QUICKLY GET RING TOTAL, INCLUDING RINGS CURRENTLY IN THE PICKUP ANIMATION
#define RINGTOTAL(p) (p->kartstuff[k_rings] + p->kartstuff[k_pickuprings])
@ -433,12 +430,14 @@ typedef struct respawnvars_s
// player_t struct for all bot variables
typedef struct botvars_s
{
UINT8 difficulty;
UINT8 difficulty; // Bot's difficulty setting
UINT8 diffincrease; // In GP: bot difficulty will increase this much next round
boolean rival; // If true, they're the GP rival
tic_t itemdelay;
tic_t itemconfirm;
tic_t itemdelay; // Delay before using item at all
tic_t itemconfirm; // When high enough, they will use their item
SINT8 turnconfirm;
SINT8 turnconfirm; // Confirm turn direction
} botvars_t;
// ========================================================================
@ -520,6 +519,7 @@ typedef struct player_s
UINT32 charflags; // Extra abilities/settings for skins (combinable stuff)
// See SF_ flags
SINT8 lives;
boolean lostlife;
SINT8 continues; // continues that player has acquired
SINT8 xtralife; // Ring Extra Life counter

View file

@ -1255,6 +1255,113 @@ static void readlevelheader(MYFILE *f, INT32 num)
Z_Free(s);
}
static void readcupheader(MYFILE *f, cupheader_t *cup)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
char *word;
char *word2;
char *tmp;
INT32 i;
do
{
if (myfgets(s, MAXLINELEN, f))
{
if (s[0] == '\n')
break;
// First remove trailing newline, if there is one
tmp = strchr(s, '\n');
if (tmp)
*tmp = '\0';
tmp = strchr(s, '#');
if (tmp)
*tmp = '\0';
if (s == tmp)
continue; // Skip comment lines, but don't break.
// Set / reset word, because some things (Lua.) move it
word = s;
// Get the part before the " = "
tmp = strchr(s, '=');
if (tmp)
*(tmp-1) = '\0';
else
break;
strupr(word);
// Now get the part after
word2 = tmp += 2;
i = atoi(word2); // used for numerical settings
strupr(word2);
if (fastcmp(word, "ICON"))
{
deh_strlcpy(cup->icon, word2,
sizeof(cup->icon), va("%s Cup: icon", cup->name));
}
else if (fastcmp(word, "LEVELLIST"))
{
cup->numlevels = 0;
tmp = strtok(word2,",");
do {
INT32 map = atoi(tmp);
if (tmp[0] >= 'A' && tmp[0] <= 'Z' && tmp[2] == '\0')
map = M_MapNumber(tmp[0], tmp[1]);
if (!map)
break;
if (cup->numlevels >= MAXLEVELLIST)
{
deh_warning("%s Cup: reached max levellist (%d)\n", cup->name, MAXLEVELLIST);
break;
}
cup->levellist[cup->numlevels] = map - 1;
cup->numlevels++;
} while((tmp = strtok(NULL,",")) != NULL);
}
else if (fastcmp(word, "BONUSGAME"))
{
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0')
i = M_MapNumber(word2[0], word2[1]);
cup->bonusgame = (INT16)i - 1;
}
else if (fastcmp(word, "SPECIALSTAGE"))
{
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0')
i = M_MapNumber(word2[0], word2[1]);
cup->specialstage = (INT16)i - 1;
}
else if (fastcmp(word, "EMERALDNUM"))
{
if (i >= 0 && i <= 14)
cup->emeraldnum = (UINT8)i;
else
deh_warning("%s Cup: invalid emerald number %d", cup->name, i);
}
else if (fastcmp(word, "UNLOCKABLE"))
{
if (i >= 0 && i <= MAXUNLOCKABLES) // 0 for no unlock required, anything else requires something
cup->unlockrequired = (SINT8)i - 1;
else
deh_warning("%s Cup: invalid unlockable number %d", cup->name, i);
}
else
deh_warning("%s Cup: unknown word '%s'", cup->name, word);
}
} while (!myfeof(f)); // finish when the line is empty
Z_Free(s);
}
static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum)
{
char *s = Z_Calloc(MAXLINELEN, PU_STATIC, NULL);
@ -3396,6 +3503,42 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
}
DEH_WriteUndoline(word, word2, UNDO_HEADER);
}
else if (fastcmp(word, "CUP"))
{
cupheader_t *cup = kartcupheaders;
cupheader_t *prev = NULL;
while (cup)
{
if (fastcmp(cup->name, word2))
{
// mark as a major mod if it replaces an already-existing cup
G_SetGameModified(multiplayer, true);
break;
}
prev = cup;
cup = cup->next;
}
// Nothing found, add to the end.
if (!cup)
{
cup = Z_Calloc(sizeof (cupheader_t), PU_STATIC, NULL);
cup->id = numkartcupheaders;
deh_strlcpy(cup->name, word2,
sizeof(cup->name), va("Cup header %s: name", word2));
if (prev != NULL)
prev->next = cup;
if (kartcupheaders == NULL)
kartcupheaders = cup;
numkartcupheaders++;
CONS_Printf("Added cup %d ('%s')\n", cup->id, cup->name);
}
readcupheader(f, cup);
DEH_WriteUndoline(word, word2, UNDO_HEADER);
}
else if (fastcmp(word, "CUTSCENE"))
{
if (i > 0 && i < 129)
@ -8388,7 +8531,7 @@ static const char *const KARTSTUFF_LIST[] = {
"STOLENTIMER",
"SUPERRING",
"SNEAKERTIMER",
"LEVELBOOSTER",
"NUMSNEAKERS",
"GROWSHRINKTIMER",
"SQUISHEDTIMER",
"ROCKETSNEAKERTIMER",

View file

@ -325,6 +325,27 @@ typedef struct
extern mapheader_t* mapheaderinfo[NUMMAPS];
// This could support more, but is that a good idea?
// Keep in mind that it may encourage people making overly long cups just because they "can", and would be a waste of memory.
#define MAXLEVELLIST 5
typedef struct cupheader_s
{
UINT16 id; ///< Cup ID
char name[15]; ///< Cup title (14 chars)
char icon[9]; ///< Name of the icon patch
INT16 levellist[MAXLEVELLIST]; ///< List of levels that belong to this cup
UINT8 numlevels; ///< Number of levels defined in levellist
INT16 bonusgame; ///< Map number to use for bonus game
INT16 specialstage; ///< Map number to use for special stage
UINT8 emeraldnum; ///< ID of Emerald to use for special stage (1-7 for Chaos Emeralds, 8-14 for Super Emeralds, 0 for no emerald)
SINT8 unlockrequired; ///< An unlockable is required to select this cup. -1 for no unlocking required.
struct cupheader_s *next; ///< Next cup in linked list
} cupheader_t;
extern cupheader_t *kartcupheaders; // Start of cup linked list
extern UINT16 numkartcupheaders;
enum TypeOfLevel
{
TOL_SP = 0x01, ///< Single Player

77
src/font.c Normal file
View file

@ -0,0 +1,77 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1999-2018 by Sonic Team Junior.
// Copyright (C) 2019 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 font.c
/// \brief Font setup
#include "doomdef.h"
#include "hu_stuff.h"
#include "font.h"
#include "z_zone.h"
font_t fontv[MAX_FONTS];
int fontc;
static void
FontCache (font_t *fnt)
{
int i;
int c;
c = fnt->start;
for (i = 0; i < fnt->size; ++i, ++c)
{
fnt->font[i] = HU_CachePatch(
"%s%.*d",
fnt->prefix,
fnt->digits,
c);
}
}
void
Font_Load (void)
{
int i;
for (i = 0; i < fontc; ++i)
{
FontCache(&fontv[i]);
}
}
int
Font_DumbRegister (const font_t *sfnt)
{
font_t *fnt;
if (fontc == MAX_FONTS)
return -1;
fnt = &fontv[fontc];
memcpy(fnt, sfnt, sizeof (font_t));
if (!( fnt->font = ZZ_Alloc(sfnt->size * sizeof (patch_t *)) ))
return -1;
return fontc++;
}
int
Font_Register (const font_t *sfnt)
{
int d;
d = Font_DumbRegister(sfnt);
if (d >= 0)
FontCache(&fontv[d]);
return d;
}

49
src/font.h Normal file
View file

@ -0,0 +1,49 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1999-2018 by Sonic Team Junior.
// Copyright (C) 2019 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 font.h
/// \brief Font setup
#ifndef __FONT_H__
#define __FONT_H__
#define MAX_FONTS 32
typedef struct font font_t;
struct font
{
patch_t **font;
UINT8 start;
UINT8 size;
char prefix[8];/* 7 used at most */
unsigned digits : 2;
};
extern font_t fontv[MAX_FONTS];
extern int fontc;
/*
Reloads already registered fonts.
*/
void Font_Load (void);
/*
Registers and loads a new font.
*/
int Font_Register (const font_t *);
/*
Register a new font, but do not load it yet.
*/
int Font_DumbRegister (const font_t *);
#endif

View file

@ -54,6 +54,7 @@
#include "k_pwrlv.h"
#include "k_color.h"
#include "k_respawn.h"
#include "k_grandprix.h"
gameaction_t gameaction;
gamestate_t gamestate = GS_NULL;
@ -170,6 +171,10 @@ struct quake quake;
// Map Header Information
mapheader_t* mapheaderinfo[NUMMAPS] = {NULL};
// Kart cup definitions
cupheader_t *kartcupheaders = NULL;
UINT16 numkartcupheaders = 0;
static boolean exitgame = false;
static boolean retrying = false;
@ -1430,7 +1435,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
{
// forward with key or button // SRB2kart - we use an accel/brake instead of forward/backward.
axis = JoyAxis(AXISMOVE, ssplayer);
if (InputDown(gc_accelerate, ssplayer) || (gamepadjoystickmove && axis > 0) || EITHERSNEAKER(player))
if (InputDown(gc_accelerate, ssplayer) || (gamepadjoystickmove && axis > 0) || player->kartstuff[k_sneakertimer])
{
cmd->buttons |= BT_ACCELERATE;
forward = forwardmove[1]; // 50
@ -1765,14 +1770,6 @@ void G_DoLoadLevel(boolean resetplayer)
if (!resetplayer)
P_FindEmerald();
g_localplayers[0] = consoleplayer; // view the guy you are playing
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (i > 0 && r_splitscreen < i)
g_localplayers[i] = consoleplayer;
}
gameaction = ga_nothing;
#ifdef PARANOIA
Z_CheckHeap(-2);
@ -2352,17 +2349,19 @@ void G_Ticker(boolean run)
if (gamestate == GS_LEVEL)
{
// Or, alternatively, retry.
if (!(netgame || multiplayer) && G_GetRetryFlag())
if (G_GetRetryFlag())
{
G_ClearRetryFlag();
// Costs a life to retry ... unless the player in question is dead already.
/*if (G_GametypeUsesLives() && players[consoleplayer].playerstate == PST_LIVE)
players[consoleplayer].lives -= 1;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
{
K_PlayerLoseLife(&players[i]);
}
}
G_DoReborn(consoleplayer);*/
D_MapChange(gamemap, gametype, (cv_kartencore.value == 1), true, 1, false, false);
D_MapChange(gamemap, gametype, (cv_kartencore.value == 1), false, 1, false, false);
}
for (i = 0; i < MAXPLAYERS; i++)
@ -2394,16 +2393,18 @@ void G_Ticker(boolean run)
if (playeringame[i])
{
if (K_PlayerUsesBotMovement(&players[i]))
G_CopyTiccmd(cmd, &netcmds[buf][i], 1);
// Use the leveltime sent in the player's ticcmd to determine control lag
if (modeattacking || K_PlayerUsesBotMovement(&players[i]))
{
K_BuildBotTiccmd(&players[i], cmd);
// Never has lag
cmd->latency = 0;
}
else
{
G_CopyTiccmd(cmd, &netcmds[buf][i], 1);
// Use the leveltime sent in the player's ticcmd to determine control lag
cmd->latency = modeattacking ? 0 : min(((leveltime & 0xFF) - cmd->latency) & 0xFF, MAXPREDICTTICS-1); //@TODO add a cvar to allow setting this max
//@TODO add a cvar to allow setting this max
cmd->latency = min(((leveltime & 0xFF) - cmd->latency) & 0xFF, MAXPREDICTTICS-1);
}
}
}
@ -2564,6 +2565,7 @@ void G_PlayerReborn(INT32 player)
player_t *p;
INT32 score, marescore;
INT32 lives;
boolean lostlife;
INT32 continues;
// SRB2kart
UINT8 kartspeed;
@ -2585,7 +2587,10 @@ void G_PlayerReborn(INT32 player)
boolean spectator;
boolean bot;
UINT8 botdifficulty;
UINT8 botdiffincrease;
boolean botrival;
SINT8 pity;
SINT8 xtralife;
// SRB2kart
respawnvars_t respawn;
@ -2603,6 +2608,7 @@ void G_PlayerReborn(INT32 player)
score = players[player].score;
marescore = players[player].marescore;
lives = players[player].lives;
lostlife = players[player].lostlife;
continues = players[player].continues;
ctfteam = players[player].ctfteam;
exiting = players[player].exiting;
@ -2632,7 +2638,10 @@ void G_PlayerReborn(INT32 player)
mare = players[player].mare;
bot = players[player].bot;
botdifficulty = players[player].botvars.difficulty;
botdiffincrease = players[player].botvars.diffincrease;
botrival = players[player].botvars.rival;
pity = players[player].pity;
xtralife = players[player].xtralife;
// SRB2kart
if (leveltime <= starttime)
@ -2683,6 +2692,7 @@ void G_PlayerReborn(INT32 player)
p->score = score;
p->marescore = marescore;
p->lives = lives;
p->lostlife = lostlife;
p->continues = continues;
p->pflags = pflags;
p->ctfteam = ctfteam;
@ -2709,7 +2719,10 @@ void G_PlayerReborn(INT32 player)
p->mare = mare;
p->bot = bot;
p->botvars.difficulty = botdifficulty;
p->botvars.diffincrease = botdiffincrease;
p->botvars.rival = botrival;
p->pity = pity;
p->xtralife = xtralife;
// SRB2kart
p->kartstuff[k_itemroulette] = itemroulette;
@ -3201,6 +3214,47 @@ void G_ExitLevel(void)
{
if (gamestate == GS_LEVEL)
{
if (grandprixinfo.gp == true && 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;
lastdraw = true;
@ -3276,18 +3330,15 @@ boolean G_IsSpecialStage(INT32 mapnum)
//
boolean G_GametypeUsesLives(void)
{
// SRB2kart NEEDS no lives
#if 0
// Coop, Competitive
if ((gametype == GT_COOP || gametype == GT_COMPETITION)
&& !modeattacking // No lives in Time Attack
//&& !G_IsSpecialStage(gamemap)
&& !(maptol & TOL_NIGHTS)) // No lives in NiGHTS
if ((grandprixinfo.gp == true) // In Grand Prix
&& (gametype == GT_RACE) // NOT in bonus round
&& !(modeattacking) // NOT in Record Attack
&& !G_IsSpecialStage(gamemap)) // NOT in special stage
{
return true;
}
return false;
#else
return false;
#endif
}
//
@ -3623,8 +3674,7 @@ void G_AddMapToBuffer(INT16 map)
static void G_DoCompleted(void)
{
INT32 i, j = 0;
boolean gottoken = false;
SINT8 powertype = PWRLV_DISABLED;
SINT8 powertype = K_UsingPowerLevels();
tokenlist = 0; // Reset the list
@ -3641,10 +3691,21 @@ static void G_DoCompleted(void)
// SRB2Kart: exitlevel shouldn't get you the points
if (!players[i].exiting && !(players[i].pflags & PF_TIMEOVER))
{
players[i].pflags |= PF_TIMEOVER;
if (P_IsLocalPlayer(&players[i]))
j++;
if (players[i].bot)
{
K_FakeBotResults(&players[i]);
}
else
{
players[i].pflags |= PF_TIMEOVER;
if (P_IsLocalPlayer(&players[i]))
{
j++;
}
}
}
G_PlayerFinishLevel(i); // take away cards and stuff
}
@ -3664,11 +3725,33 @@ static void G_DoCompleted(void)
// go to next level
// nextmap is 0-based, unlike gamemap
if (nextmapoverride != 0)
{
nextmap = (INT16)(nextmapoverride-1);
else if (mapheaderinfo[gamemap-1]->nextlevel == 1101) // SRB2Kart: !!! WHENEVER WE GET GRAND PRIX, GO TO AWARDS MAP INSTEAD !!!
nextmap = (INT16)(mapheaderinfo[gamemap] ? gamemap : (spstage_start-1)); // (gamemap-1)+1 == gamemap :V
}
else if (grandprixinfo.gp == true)
{
if (grandprixinfo.roundnum == 0 || grandprixinfo.cup == NULL) // Single session
{
nextmap = prevmap; // Same map
}
else
{
if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) // On final map
{
nextmap = 1101; // ceremonymap
}
else
{
// Proceed to next map
nextmap = grandprixinfo.cup->levellist[grandprixinfo.roundnum];
grandprixinfo.roundnum++;
}
}
}
else
{
nextmap = (INT16)(mapheaderinfo[gamemap-1]->nextlevel-1);
}
// Remember last map for when you come out of the special stage.
if (!G_IsSpecialStage(gamemap))
@ -3678,8 +3761,7 @@ static void G_DoCompleted(void)
// a map of the proper gametype -- skip levels that don't support
// the current gametype. (Helps avoid playing boss levels in Race,
// for instance).
if (!token && !G_IsSpecialStage(gamemap) && !modeattacking
&& (nextmap >= 0 && nextmap < NUMMAPS))
if (!modeattacking && grandprixinfo.gp == false && (nextmap >= 0 && nextmap < NUMMAPS))
{
register INT16 cm = nextmap;
INT16 tolflag = G_TOLFlag(gametype);
@ -3723,44 +3805,18 @@ static void G_DoCompleted(void)
if (nextmap < 0 || (nextmap >= NUMMAPS && nextmap < 1100-1) || nextmap > 1102-1)
I_Error("Followed map %d to invalid map %d\n", prevmap + 1, nextmap + 1);
// wrap around in race
if (nextmap >= 1100-1 && nextmap <= 1102-1 && G_RaceGametype())
nextmap = (INT16)(spstage_start-1);
if (gametype == GT_COOP && token)
{
token--;
gottoken = true;
if (!(emeralds & EMERALD1))
nextmap = (INT16)(sstage_start - 1); // Special Stage 1
else if (!(emeralds & EMERALD2))
nextmap = (INT16)(sstage_start); // Special Stage 2
else if (!(emeralds & EMERALD3))
nextmap = (INT16)(sstage_start + 1); // Special Stage 3
else if (!(emeralds & EMERALD4))
nextmap = (INT16)(sstage_start + 2); // Special Stage 4
else if (!(emeralds & EMERALD5))
nextmap = (INT16)(sstage_start + 3); // Special Stage 5
else if (!(emeralds & EMERALD6))
nextmap = (INT16)(sstage_start + 4); // Special Stage 6
else if (!(emeralds & EMERALD7))
nextmap = (INT16)(sstage_start + 5); // Special Stage 7
else
gottoken = false;
}
if (G_IsSpecialStage(gamemap) && !gottoken)
nextmap = lastmap; // Exiting from a special stage? Go back to the game. Tails 08-11-2001
automapactive = false;
if (gametype != GT_COOP)
{
if (cv_advancemap.value == 0) // Stay on same map.
{
nextmap = prevmap;
}
else if (cv_advancemap.value == 2) // Go to random map.
{
nextmap = G_RandMap(G_TOLFlag(gametype), prevmap, false, 0, false, NULL);
}
}
// We are committed to this map now.
@ -3770,14 +3826,6 @@ static void G_DoCompleted(void)
P_AllocMapHeader(nextmap);
// Set up power level gametype scrambles
if (netgame && cv_kartusepwrlv.value)
{
if (G_RaceGametype())
powertype = PWRLV_RACE;
else if (G_BattleGametype())
powertype = PWRLV_BATTLE;
}
K_SetPowerLevelScrambles(powertype);
demointermission:
@ -3837,7 +3885,7 @@ void G_NextLevel(void)
{
if (gamestate != GS_VOTING)
{
if ((cv_advancemap.value == 3) && !modeattacking && !skipstats && (multiplayer || netgame))
if ((cv_advancemap.value == 3) && grandprixinfo.gp == false && !modeattacking && !skipstats && (multiplayer || netgame))
{
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
@ -3934,7 +3982,7 @@ static void G_DoContinued(void)
token = 0;
// Reset # of lives
pl->lives = (ultimatemode) ? 1 : 3;
pl->lives = 3;
D_MapChange(gamemap, gametype, false, false, 0, false, false);
@ -4503,7 +4551,7 @@ void G_SaveGame(UINT32 savegameslot)
void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar, UINT8 ssplayers, boolean FLS)
{
INT32 i;
UINT8 color = 0;
//UINT8 color = 0;
paused = false;
if (demo.playback)
@ -4523,23 +4571,20 @@ void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar
// this leave the actual game if needed
SV_StartSinglePlayerServer();
if (savedata.lives > 0)
{
color = savedata.skincolor;
}
else if (splitscreen != ssplayers)
if (splitscreen != ssplayers)
{
splitscreen = ssplayers;
SplitScreen_OnChange();
}
if (!color && !modeattacking)
color = skins[pickedchar].prefcolor;
//if (!color)
//color = skins[pickedchar].prefcolor;
SetPlayerSkinByNum(consoleplayer, pickedchar);
CV_StealthSet(&cv_skin, skins[pickedchar].name);
if (color)
CV_StealthSetValue(&cv_playercolor, color);
//if (color)
//CV_StealthSetValue(&cv_playercolor, color);
if (mapname)
D_MapChange(M_MapNumber(mapname[3], mapname[4]), gametype, pencoremode, true, 1, false, FLS);
@ -4568,63 +4613,40 @@ 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!
P_SetRandSeed(M_RandomizedSeed()); // Use a more "Random" random seed
//SRB2Kart - Score is literally the only thing you SHOULDN'T reset at all times
//if (resetplayer)
// Clear a bunch of variables
tokenlist = token = sstimer = redscore = bluescore = lastmap = 0;
racecountdown = exitcountdown = mapreset = 0;
for (i = 0; i < MAXPLAYERS; i++)
{
// Clear a bunch of variables
tokenlist = token = sstimer = redscore = bluescore = lastmap = 0;
racecountdown = exitcountdown = mapreset = 0;
players[i].playerstate = PST_REBORN;
players[i].starpostnum = 0;
memset(&players[i].respawn, 0, sizeof (players[i].respawn));
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].starpostnum = 0;
memset(&players[i].respawn, 0, sizeof (players[i].respawn));
#if 0
if (netgame || multiplayer)
{
players[i].lives = cv_startinglives.value;
players[i].continues = 0;
}
else if (pultmode)
{
players[i].lives = 1;
players[i].continues = 0;
}
else
{
players[i].lives = 3;
players[i].continues = 1;
}
players[i].lives = 3;
players[i].xtralife = 0;
#else
players[i].lives = 1; // SRB2Kart
#endif
// 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].totalring = 0;
players[i].score = 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);
}
// 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
// well this check is useless because it is done before (d_netcmd.c::command_map_f)
// but in case of for demos....
@ -5104,7 +5126,7 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
|| (players[displayplayers[0]].respawn.state != RESPAWNST_NONE) // Respawning
|| (players[displayplayers[0]].spectator || objectplacing)) // Not a physical player
&& !(players[displayplayers[0]].kartstuff[k_spinouttimer]
&& (players[displayplayers[0]].kartstuff[k_sneakertimer] || players[displayplayers[0]].kartstuff[k_levelbooster]))) // Spinning and boosting cancels out spinout
&& players[displayplayers[0]].kartstuff[k_sneakertimer])) // Spinning and boosting cancels out spinout
localangle[0] += (cmd->angleturn<<16);
if (!(demoflags & DF_GHOST) && *demo_p == DEMOMARKER)
@ -8077,7 +8099,7 @@ boolean G_DemoTitleResponder(event_t *ev)
return true;
}
if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && hu_font[ch-HU_FONTSTART])
if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && fontv[HU_FONT].font[ch-HU_FONTSTART])
|| ch == ' ') // Allow spaces, of course
{
len = strlen(demo.titlename);

View file

@ -14,6 +14,7 @@
#include "doomdef.h"
#include "byteptr.h"
#include "hu_stuff.h"
#include "font.h"
#include "m_menu.h" // gametype_cons_t
#include "m_cond.h" // emblems
@ -53,6 +54,7 @@
#include "s_sound.h" // song credits
#include "k_kart.h"
#include "k_color.h"
// coords are scaled
#define HU_INPUTX 0
@ -64,19 +66,9 @@
//-------------------------------------------
// heads up font
//-------------------------------------------
patch_t *hu_font[HU_FONTSIZE];
patch_t *kart_font[KART_FONTSIZE]; // SRB2kart
patch_t *tny_font[HU_FONTSIZE];
patch_t *tallnum[10]; // 0-9
patch_t *nightsnum[10]; // 0-9
// Level title and credits fonts
patch_t *lt_font[LT_FONTSIZE];
patch_t *cred_font[CRED_FONTSIZE];
// ping font
// Note: I'd like to adress that at this point we might *REALLY* want to work towards a common drawString function that can take any font we want because this is really turning into a MESS. :V -Lat'
patch_t *pingnum[10];
patch_t *pinggfx[5]; // small ping graphic
patch_t *mping[5]; // smaller ping graphic
@ -188,137 +180,53 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum);
void HU_LoadGraphics(void)
{
char buffer[9];
INT32 i, j;
INT32 i;
if (dedicated)
return;
j = HU_FONTSTART;
for (i = 0; i < HU_FONTSIZE; i++, j++)
{
// cache the heads-up font for entire game execution
sprintf(buffer, "STCFN%.3d", j);
if (W_CheckNumForName(buffer) == LUMPERROR)
hu_font[i] = NULL;
else
hu_font[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX);
// tiny version of the heads-up font
sprintf(buffer, "TNYFN%.3d", j);
if (W_CheckNumForName(buffer) == LUMPERROR)
tny_font[i] = NULL;
else
tny_font[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX);
}
// cache the level title font for entire game execution
lt_font[0] = (patch_t *)W_CachePatchName("LTFNT039", PU_HUDGFX); /// \note fake start hack
// Number support
lt_font[9] = (patch_t *)W_CachePatchName("LTFNT048", PU_HUDGFX);
lt_font[10] = (patch_t *)W_CachePatchName("LTFNT049", PU_HUDGFX);
lt_font[11] = (patch_t *)W_CachePatchName("LTFNT050", PU_HUDGFX);
lt_font[12] = (patch_t *)W_CachePatchName("LTFNT051", PU_HUDGFX);
lt_font[13] = (patch_t *)W_CachePatchName("LTFNT052", PU_HUDGFX);
lt_font[14] = (patch_t *)W_CachePatchName("LTFNT053", PU_HUDGFX);
lt_font[15] = (patch_t *)W_CachePatchName("LTFNT054", PU_HUDGFX);
lt_font[16] = (patch_t *)W_CachePatchName("LTFNT055", PU_HUDGFX);
lt_font[17] = (patch_t *)W_CachePatchName("LTFNT056", PU_HUDGFX);
lt_font[18] = (patch_t *)W_CachePatchName("LTFNT057", PU_HUDGFX);
// SRB2kart
j = KART_FONTSTART;
for (i = 0; i < KART_FONTSIZE; i++, j++)
{
// cache the heads-up font for entire game execution
sprintf(buffer, "MKFNT%.3d", j);
if (W_CheckNumForName(buffer) == LUMPERROR)
kart_font[i] = NULL;
else
kart_font[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX);
}
//
j = LT_FONTSTART;
for (i = 0; i < LT_FONTSIZE; i++)
{
sprintf(buffer, "LTFNT%.3d", j);
j++;
if (W_CheckNumForName(buffer) == LUMPERROR)
lt_font[i] = NULL;
else
lt_font[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX);
}
// cache the credits font for entire game execution (why not?)
j = CRED_FONTSTART;
for (i = 0; i < CRED_FONTSIZE; i++)
{
sprintf(buffer, "CRFNT%.3d", j);
j++;
if (W_CheckNumForName(buffer) == LUMPERROR)
cred_font[i] = NULL;
else
cred_font[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX);
}
//cache numbers too!
for (i = 0; i < 10; i++)
{
sprintf(buffer, "STTNUM%d", i);
tallnum[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX);
sprintf(buffer, "NGTNUM%d", i);
nightsnum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
sprintf(buffer, "PINGN%d", i);
pingnum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
}
Font_Load();
// minus for negative tallnums
tallminus = (patch_t *)W_CachePatchName("STTMINUS", PU_HUDGFX);
tallminus = HU_CachePatch("STTMINUS");
// cache the crosshairs, don't bother to know which one is being used,
// just cache all 3, they're so small anyway.
for (i = 0; i < HU_CROSSHAIRS; i++)
{
sprintf(buffer, "CROSHAI%c", '1'+i);
crosshair[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX);
crosshair[i] = HU_CachePatch("CROSHAI%c", '1'+i);
}
emblemicon = W_CachePatchName("EMBLICON", PU_HUDGFX);
tokenicon = W_CachePatchName("TOKNICON", PU_HUDGFX);
emblemicon = HU_CachePatch("EMBLICON");
tokenicon = HU_CachePatch("TOKNICON");
emeraldpics[0] = W_CachePatchName("CHAOS1", PU_HUDGFX);
emeraldpics[1] = W_CachePatchName("CHAOS2", PU_HUDGFX);
emeraldpics[2] = W_CachePatchName("CHAOS3", PU_HUDGFX);
emeraldpics[3] = W_CachePatchName("CHAOS4", PU_HUDGFX);
emeraldpics[4] = W_CachePatchName("CHAOS5", PU_HUDGFX);
emeraldpics[5] = W_CachePatchName("CHAOS6", PU_HUDGFX);
emeraldpics[6] = W_CachePatchName("CHAOS7", PU_HUDGFX);
tinyemeraldpics[0] = W_CachePatchName("TEMER1", PU_HUDGFX);
tinyemeraldpics[1] = W_CachePatchName("TEMER2", PU_HUDGFX);
tinyemeraldpics[2] = W_CachePatchName("TEMER3", PU_HUDGFX);
tinyemeraldpics[3] = W_CachePatchName("TEMER4", PU_HUDGFX);
tinyemeraldpics[4] = W_CachePatchName("TEMER5", PU_HUDGFX);
tinyemeraldpics[5] = W_CachePatchName("TEMER6", PU_HUDGFX);
tinyemeraldpics[6] = W_CachePatchName("TEMER7", PU_HUDGFX);
emeraldpics[0] = HU_CachePatch("CHAOS1");
emeraldpics[1] = HU_CachePatch("CHAOS2");
emeraldpics[2] = HU_CachePatch("CHAOS3");
emeraldpics[3] = HU_CachePatch("CHAOS4");
emeraldpics[4] = HU_CachePatch("CHAOS5");
emeraldpics[5] = HU_CachePatch("CHAOS6");
emeraldpics[6] = HU_CachePatch("CHAOS7");
tinyemeraldpics[0] = HU_CachePatch("TEMER1");
tinyemeraldpics[1] = HU_CachePatch("TEMER2");
tinyemeraldpics[2] = HU_CachePatch("TEMER3");
tinyemeraldpics[3] = HU_CachePatch("TEMER4");
tinyemeraldpics[4] = HU_CachePatch("TEMER5");
tinyemeraldpics[5] = HU_CachePatch("TEMER6");
tinyemeraldpics[6] = HU_CachePatch("TEMER7");
songcreditbg = W_CachePatchName("K_SONGCR", PU_HUDGFX);
songcreditbg = HU_CachePatch("K_SONGCR");
// cache ping gfx:
for (i = 0; i < 5; i++)
{
sprintf(buffer, "PINGGFX%d", i+1);
pinggfx[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX);
sprintf(buffer, "MPING%d", i+1);
mping[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX);
pinggfx[i] = HU_CachePatch("PINGGFX%d", i+1);
mping[i] = HU_CachePatch("MPING%d", i+1);
}
// fps stuff
framecounter = W_CachePatchName("FRAMER", PU_HUDGFX);
frameslash = W_CachePatchName("FRAMESL", PU_HUDGFX);;
framecounter = HU_CachePatch("FRAMER");
frameslash = HU_CachePatch("FRAMESL");;
}
// Initialise Heads up
@ -326,6 +234,8 @@ void HU_LoadGraphics(void)
//
void HU_Init(void)
{
font_t font;
#ifndef NONET
COM_AddCommand("say", Command_Say_f);
COM_AddCommand("sayto", Command_Sayto_f);
@ -337,9 +247,78 @@ void HU_Init(void)
// set shift translation table
shiftxform = english_shiftxform;
/*
Setup fonts
*/
if (!dedicated)
{
#define DIM( s, n ) ( font.start = s, font.size = n )
#define ADIM( name ) DIM (name ## _FONTSTART, name ## _FONTSIZE)
#define PR( s ) strcpy(font.prefix, s)
#define DIG( n ) ( font.digits = n )
#define REG Font_DumbRegister(&font)
DIG (3);
ADIM (HU);
PR ("STCFN");
REG;
PR ("TNYFN");
REG;
ADIM (KART);
PR ("MKFNT");
REG;
ADIM (LT);
PR ("LTFNT");
REG;
ADIM (CRED);
PR ("CRFNT");
REG;
DIG (1);
DIM (0, 10);
PR ("STTNUM");
REG;
PR ("NGTNUM");
REG;
PR ("PINGN");
REG;
#undef REG
#undef DIG
#undef PR
#undef ADMIN
#undef DIM
}
HU_LoadGraphics();
}
patch_t *HU_CachePatch(const char *format, ...)
{
va_list ap;
char buffer[9];
va_start (ap, format);
vsprintf(buffer, format, ap);
va_end (ap);
if (W_CheckNumForName(buffer) == LUMPERROR)
return NULL;
else
return (patch_t *)W_CachePatchName(buffer, PU_HUDGFX);
}
static inline void HU_Stop(void)
{
headsupactive = false;
@ -760,169 +739,30 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
{
const char *prefix = "", *cstart = "", *cend = "", *adminchar = "\x82~\x83", *remotechar = "\x82@\x83", *fmt2, *textcolor = "\x80";
char *tempchar = NULL;
char color_prefix[2];
// player is a spectator?
if (players[playernum].spectator)
if (players[playernum].spectator)
{
cstart = "\x86"; // grey name
textcolor = "\x86";
// grey text
cstart = textcolor = "\x86";
}
else if (target == -1) // say team
{
if (players[playernum].ctfteam == 1) // red
if (players[playernum].ctfteam == 1)
{
cstart = "\x85";
textcolor = "\x85";
// red text
cstart = textcolor = "\x85";
}
else // blue
else
{
cstart = "\x84";
textcolor = "\x84";
// blue text
cstart = textcolor = "\x84";
}
}
else
{
const UINT8 color = players[playernum].skincolor;
cstart = "\x83";
switch (color)
{
case SKINCOLOR_WHITE:
case SKINCOLOR_SILVER:
case SKINCOLOR_SLATE:
cstart = "\x80"; // White
break;
case SKINCOLOR_GREY:
case SKINCOLOR_NICKEL:
case SKINCOLOR_BLACK:
case SKINCOLOR_SKUNK:
case SKINCOLOR_PLATINUM:
case SKINCOLOR_JET:
cstart = "\x86"; // V_GRAYMAP
break;
case SKINCOLOR_SEPIA:
case SKINCOLOR_BEIGE:
case SKINCOLOR_CARAMEL:
case SKINCOLOR_PEACH:
case SKINCOLOR_BROWN:
case SKINCOLOR_LEATHER:
case SKINCOLOR_RUST:
case SKINCOLOR_WRISTWATCH:
cstart = "\x8e"; // V_BROWNMAP
break;
case SKINCOLOR_FAIRY:
case SKINCOLOR_PINK:
case SKINCOLOR_ROSE:
case SKINCOLOR_LEMONADE:
case SKINCOLOR_LILAC:
case SKINCOLOR_BLOSSOM:
case SKINCOLOR_TAFFY:
cstart = "\x8d"; // V_PINKMAP
break;
case SKINCOLOR_CINNAMON:
case SKINCOLOR_RUBY:
case SKINCOLOR_RASPBERRY:
case SKINCOLOR_RED:
case SKINCOLOR_CRIMSON:
case SKINCOLOR_MAROON:
case SKINCOLOR_SCARLET:
case SKINCOLOR_KETCHUP:
cstart = "\x85"; // V_REDMAP
break;
case SKINCOLOR_DAWN:
case SKINCOLOR_SUNSLAM:
case SKINCOLOR_CREAMSICLE:
case SKINCOLOR_ORANGE:
case SKINCOLOR_ROSEWOOD:
case SKINCOLOR_TANGERINE:
cstart = "\x87"; // V_ORANGEMAP
break;
case SKINCOLOR_TAN:
case SKINCOLOR_CREAM:
cstart = "\x8f"; // V_TANMAP
break;
case SKINCOLOR_GOLD:
case SKINCOLOR_ROYAL:
case SKINCOLOR_BRONZE:
case SKINCOLOR_COPPER:
case SKINCOLOR_THUNDER:
cstart = "\x8A"; // V_GOLDMAP
break;
case SKINCOLOR_POPCORN:
case SKINCOLOR_YELLOW:
case SKINCOLOR_MUSTARD:
case SKINCOLOR_BANANA:
case SKINCOLOR_OLIVE:
case SKINCOLOR_CROCODILE:
cstart = "\x82"; // V_YELLOWMAP
break;
case SKINCOLOR_ARTICHOKE:
case SKINCOLOR_PERIDOT:
case SKINCOLOR_VOMIT:
case SKINCOLOR_GARDEN:
case SKINCOLOR_LIME:
case SKINCOLOR_HANDHELD:
case SKINCOLOR_TEA:
case SKINCOLOR_PISTACHIO:
case SKINCOLOR_MOSS:
case SKINCOLOR_CAMOUFLAGE:
case SKINCOLOR_MINT:
case SKINCOLOR_GREEN:
case SKINCOLOR_PINETREE:
case SKINCOLOR_TURTLE:
case SKINCOLOR_SWAMP:
case SKINCOLOR_DREAM:
case SKINCOLOR_PLAGUE:
case SKINCOLOR_EMERALD:
case SKINCOLOR_ALGAE:
cstart = "\x83"; // V_GREENMAP
break;
case SKINCOLOR_AQUAMARINE:
case SKINCOLOR_TURQUOISE:
case SKINCOLOR_TEAL:
cstart = "\x8b"; // V_AQUAMAP
break;
case SKINCOLOR_PIGEON:
case SKINCOLOR_ROBIN:
case SKINCOLOR_CYAN:
case SKINCOLOR_JAWZ:
case SKINCOLOR_CERULEAN:
case SKINCOLOR_NAVY:
case SKINCOLOR_SAPPHIRE:
cstart = "\x88"; // V_SKYMAP
break;
case SKINCOLOR_STEEL:
case SKINCOLOR_ULTRAMARINE:
case SKINCOLOR_PERIWINKLE:
case SKINCOLOR_BLUE:
case SKINCOLOR_MIDNIGHT:
case SKINCOLOR_BLUEBERRY:
case SKINCOLOR_NOVA:
cstart = "\x84"; // V_BLUEMAP
break;
case SKINCOLOR_THISTLE:
case SKINCOLOR_PURPLE:
case SKINCOLOR_PASTEL:
cstart = "\x81"; // V_PURPLEMAP
break;
case SKINCOLOR_MAGENTA:
case SKINCOLOR_FUCHSIA:
case SKINCOLOR_MOONSET:
case SKINCOLOR_VIOLET:
cstart = "\x8c"; // V_MAGENTAMAP
break;
case SKINCOLOR_DUSK:
case SKINCOLOR_TOXIC:
case SKINCOLOR_MAUVE:
case SKINCOLOR_LAVENDER:
case SKINCOLOR_BYZANTIUM:
case SKINCOLOR_POMEGRANATE:
cstart = "\x89"; // V_LAVENDERMAP
break;
default:
break;
}
sprintf(color_prefix, "%c", '\x80' + (K_SkincolorToTextColor(players[playernum].skincolor) >> V_CHARCOLORSHIFT));
cstart = color_prefix;
}
prefix = cstart;
@ -932,6 +772,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
tempchar = (char *)Z_Calloc(strlen(cstart) + strlen(adminchar) + 1, PU_STATIC, NULL);
else if (IsPlayerAdmin(playernum))
tempchar = (char *)Z_Calloc(strlen(cstart) + strlen(remotechar) + 1, PU_STATIC, NULL);
if (tempchar)
{
if (playernum == serverplayer)
@ -999,7 +840,7 @@ static inline boolean HU_keyInChatString(char *s, char ch)
{
size_t l;
if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && hu_font[ch-HU_FONTSTART])
if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && fontv[HU_FONT].font[ch-HU_FONTSTART])
|| ch == ' ') // Allow spaces, of course
{
l = strlen(s);
@ -1433,7 +1274,7 @@ static char *CHAT_WordWrap(INT32 x, INT32 w, INT32 option, const char *string)
c = toupper(c);
c -= HU_FONTSTART;
if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
if (c < 0 || c >= HU_FONTSIZE || !fontv[HU_FONT].font[c])
{
chw = spacewidth;
lastusablespace = i;

View file

@ -17,6 +17,7 @@
#include "d_event.h"
#include "w_wad.h"
#include "r_defs.h"
#include "font.h"
//------------------------------------
// heads up font
@ -42,6 +43,23 @@
#define CRED_FONTEND 'Z' // the last font character
#define CRED_FONTSIZE (CRED_FONTEND - CRED_FONTSTART + 1)
#define X( name ) name ## _FONT
/* fonts */
enum
{
X (HU),
X (TINY),
X (KART),
X (LT),
X (CRED),
X (TALLNUM),
X (NIGHTSNUM),
X (PINGNUM),
};
#undef X
#define HU_CROSSHAIRS 3 // maximum of 9 - see HU_Init();
extern char *shiftxform; // english translation shift table
@ -78,15 +96,9 @@ void HU_AddChatText(const char *text, boolean playsound);
// set true when entering a chat message
extern boolean chat_on;
extern patch_t *hu_font[HU_FONTSIZE], *kart_font[KART_FONTSIZE], *tny_font[HU_FONTSIZE]; // SRB2kart
extern patch_t *tallnum[10];
extern patch_t *pingnum[10];
extern patch_t *pinggfx[5];
extern patch_t *nightsnum[10];
extern patch_t *framecounter;
extern patch_t *frameslash;
extern patch_t *lt_font[LT_FONTSIZE];
extern patch_t *cred_font[CRED_FONTSIZE];
extern patch_t *emeraldpics[7];
extern patch_t *tinyemeraldpics[7];
extern patch_t *rflagico;
@ -104,6 +116,9 @@ void HU_Init(void);
void HU_LoadGraphics(void);
// Load a HUDGFX patch or NULL.
patch_t *HU_CachePatch(const char *format, ...);
// reset heads up when consoleplayer respawns.
void HU_Start(void);

View file

@ -254,7 +254,6 @@ void K_CheckBumpers(void)
for (i = 0; i < MAXPLAYERS; i++)
{
players[i].pflags |= PF_TIMEOVER;
//players[i].lives = 0;
P_DoPlayerExit(&players[i]);
}
}

View file

@ -1,5 +1,6 @@
// SONIC ROBO BLAST 2 KART
//-----------------------------------------------------------------------------
// Copyright (C) 2018-2020 by Sally "TehRealSalt" Cochenour
// Copyright (C) 2018-2020 by Kart Krew
//
// This program is free software distributed under the
@ -295,11 +296,17 @@ boolean K_BotCanTakeCut(player_t *player)
--------------------------------------------------*/
static UINT32 K_BotRubberbandDistance(player_t *player)
{
const UINT32 spacing = 2048;
const UINT32 spacing = FixedDiv(512 * FRACUNIT, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT;
const UINT8 portpriority = player - players;
UINT8 pos = 0;
UINT8 i;
if (player->botvars.rival)
{
// The rival should always try to be the front runner for the race.
return 0;
}
for (i = 0; i < MAXPLAYERS; i++)
{
if (i == portpriority)
@ -312,15 +319,15 @@ static UINT32 K_BotRubberbandDistance(player_t *player)
// First check difficulty levels, then score, then settle it with port priority!
if (player->botvars.difficulty < players[i].botvars.difficulty)
{
pos++;
pos += 3;
}
else if (player->score < players[i].score)
{
pos++;
pos += 2;
}
else if (i < portpriority)
{
pos++;
pos += 1;
}
}
}
@ -336,11 +343,13 @@ static UINT32 K_BotRubberbandDistance(player_t *player)
fixed_t K_BotRubberband(player_t *player)
{
fixed_t rubberband = FRACUNIT;
fixed_t max, min;
player_t *firstplace = NULL;
UINT8 i;
if (player->exiting)
{
// You're done, we don't need to rubberband anymore.
return FRACUNIT;
}
@ -372,8 +381,8 @@ fixed_t K_BotRubberband(player_t *player)
if (wanteddist > player->distancetofinish)
{
// Whoa, you're too far ahead!
rubberband += (MAXBOTDIFFICULTY - player->botvars.difficulty) * distdiff;
// Whoa, you're too far ahead! Slow back down a little.
rubberband += (MAXBOTDIFFICULTY - player->botvars.difficulty) * (distdiff / 3);
}
else
{
@ -382,13 +391,23 @@ fixed_t K_BotRubberband(player_t *player)
}
}
if (rubberband > 2*FRACUNIT)
// Lv. 1: x1.0 max
// Lv. 5: x1.5 max
// Lv. 9: x2.0 max
max = FRACUNIT + ((FRACUNIT * (player->botvars.difficulty - 1)) / (MAXBOTDIFFICULTY - 1));
// Lv. 1: x0.75 min
// Lv. 5: x0.875 min
// Lv. 9: x1.0 min
min = FRACUNIT - (((FRACUNIT/4) * (MAXBOTDIFFICULTY - player->botvars.difficulty)) / (MAXBOTDIFFICULTY - 1));
if (rubberband > max)
{
rubberband = 2*FRACUNIT;
rubberband = max;
}
else if (rubberband < 7*FRACUNIT/8)
else if (rubberband < min)
{
rubberband = 7*FRACUNIT/8;
rubberband = min;
}
return rubberband;
@ -447,6 +466,25 @@ fixed_t K_BotTopSpeedRubberband(player_t *player)
return rubberband;
}
/*--------------------------------------------------
fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict)
See header file for description.
--------------------------------------------------*/
fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict)
{
fixed_t rubberband = K_BotRubberband(player) - FRACUNIT;
if (rubberband <= 0)
{
// Never get stronger than normal friction
return frict;
}
// 128 is a magic number that felt good in-game
return FixedDiv(frict, FRACUNIT + (rubberband / 2));
}
/*--------------------------------------------------
fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy)
@ -637,7 +675,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
// Remove any existing controls
memset(cmd, 0, sizeof(ticcmd_t));
cmd->angleturn = (player->mo->angle >> 16) | TICCMD_RECEIVED;
cmd->angleturn = (player->mo->angle >> 16);
if (gamestate != GS_LEVEL)
{

View file

@ -1,5 +1,6 @@
// SONIC ROBO BLAST 2 KART
//-----------------------------------------------------------------------------
// Copyright (C) 2018-2020 by Sally "TehRealSalt" Cochenour
// Copyright (C) 2018-2020 by Kart Krew
//
// This program is free software distributed under the
@ -95,6 +96,23 @@ fixed_t K_BotRubberband(player_t *player);
fixed_t K_BotTopSpeedRubberband(player_t *player);
/*--------------------------------------------------
fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict);
Gives a multiplier for a bot's rubberbanding.
Adjusted from K_BotRubberband to be used for friction.
Input Arguments:-
player - Player to check.
frict - Friction value to adjust.
Return:-
The new friction value.
--------------------------------------------------*/
fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict);
/*--------------------------------------------------
fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy);
@ -228,5 +246,4 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd);
void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt);
#endif

View file

@ -1,5 +1,6 @@
// SONIC ROBO BLAST 2 KART
//-----------------------------------------------------------------------------
// Copyright (C) 2018-2020 by Sally "TehRealSalt" Cochenour
// Copyright (C) 2018-2020 by Kart Krew
//
// This program is free software distributed under the

View file

@ -1,5 +1,6 @@
// SONIC ROBO BLAST 2 KART
//-----------------------------------------------------------------------------
// Copyright (C) 2018-2020 by Sally "TehRealSalt" Cochenour
// Copyright (C) 2018-2020 by Kart Krew
//
// This program is free software distributed under the

View file

@ -380,12 +380,160 @@ UINT8 colortranslations[MAXTRANSLATIONS][16] = {
{ 0, 0, 96, 96, 97, 98, 98, 99, 81, 81, 69, 71, 73, 75, 77, 79}, // SKINCOLOR_CSUPER5
};
/*--------------------------------------------------
INT32 K_SkincolorToTextColor(UINT8 skincolor)
See header file for description.
--------------------------------------------------*/
INT32 K_SkincolorToTextColor(UINT8 skincolor)
{
INT32 textcolor = 0;
switch (skincolor)
{
default: // default to white?
case SKINCOLOR_WHITE:
case SKINCOLOR_SILVER:
case SKINCOLOR_SLATE:
textcolor = 0; // No color is white
break;
case SKINCOLOR_GREY:
case SKINCOLOR_NICKEL:
case SKINCOLOR_BLACK:
case SKINCOLOR_SKUNK:
case SKINCOLOR_PLATINUM:
case SKINCOLOR_JET:
textcolor = V_GRAYMAP;
break;
case SKINCOLOR_SEPIA:
case SKINCOLOR_BEIGE:
case SKINCOLOR_CARAMEL:
case SKINCOLOR_PEACH:
case SKINCOLOR_BROWN:
case SKINCOLOR_LEATHER:
case SKINCOLOR_RUST:
case SKINCOLOR_WRISTWATCH:
textcolor = V_BROWNMAP;
break;
case SKINCOLOR_FAIRY:
case SKINCOLOR_PINK:
case SKINCOLOR_ROSE:
case SKINCOLOR_LEMONADE:
case SKINCOLOR_LILAC:
case SKINCOLOR_BLOSSOM:
case SKINCOLOR_TAFFY:
textcolor = V_PINKMAP;
break;
case SKINCOLOR_CINNAMON:
case SKINCOLOR_RUBY:
case SKINCOLOR_RASPBERRY:
case SKINCOLOR_RED:
case SKINCOLOR_CRIMSON:
case SKINCOLOR_MAROON:
case SKINCOLOR_SCARLET:
case SKINCOLOR_KETCHUP:
textcolor = V_REDMAP;
break;
case SKINCOLOR_DAWN:
case SKINCOLOR_SUNSLAM:
case SKINCOLOR_CREAMSICLE:
case SKINCOLOR_ORANGE:
case SKINCOLOR_ROSEWOOD:
case SKINCOLOR_TANGERINE:
textcolor = V_ORANGEMAP;
break;
case SKINCOLOR_TAN:
case SKINCOLOR_CREAM:
textcolor = V_TANMAP;
break;
case SKINCOLOR_GOLD:
case SKINCOLOR_ROYAL:
case SKINCOLOR_BRONZE:
case SKINCOLOR_COPPER:
case SKINCOLOR_THUNDER:
textcolor = V_GOLDMAP;
break;
case SKINCOLOR_POPCORN:
case SKINCOLOR_YELLOW:
case SKINCOLOR_MUSTARD:
case SKINCOLOR_BANANA:
case SKINCOLOR_OLIVE:
case SKINCOLOR_CROCODILE:
textcolor = V_YELLOWMAP;
break;
case SKINCOLOR_ARTICHOKE:
case SKINCOLOR_PERIDOT:
case SKINCOLOR_VOMIT:
case SKINCOLOR_GARDEN:
case SKINCOLOR_LIME:
case SKINCOLOR_HANDHELD:
case SKINCOLOR_TEA:
case SKINCOLOR_PISTACHIO:
case SKINCOLOR_MOSS:
case SKINCOLOR_CAMOUFLAGE:
case SKINCOLOR_MINT:
case SKINCOLOR_GREEN:
case SKINCOLOR_PINETREE:
case SKINCOLOR_TURTLE:
case SKINCOLOR_SWAMP:
case SKINCOLOR_DREAM:
case SKINCOLOR_PLAGUE:
case SKINCOLOR_EMERALD:
case SKINCOLOR_ALGAE:
textcolor = V_GREENMAP;
break;
case SKINCOLOR_AQUAMARINE:
case SKINCOLOR_TURQUOISE:
case SKINCOLOR_TEAL:
textcolor = V_AQUAMAP;
break;
case SKINCOLOR_PIGEON:
case SKINCOLOR_ROBIN:
case SKINCOLOR_CYAN:
case SKINCOLOR_JAWZ:
case SKINCOLOR_CERULEAN:
case SKINCOLOR_NAVY:
case SKINCOLOR_SAPPHIRE:
textcolor = V_SKYMAP;
break;
case SKINCOLOR_STEEL:
case SKINCOLOR_ULTRAMARINE:
case SKINCOLOR_PERIWINKLE:
case SKINCOLOR_BLUE:
case SKINCOLOR_MIDNIGHT:
case SKINCOLOR_BLUEBERRY:
case SKINCOLOR_NOVA:
textcolor = V_BLUEMAP;
break;
case SKINCOLOR_THISTLE:
case SKINCOLOR_PURPLE:
case SKINCOLOR_PASTEL:
textcolor = V_PURPLEMAP;
break;
case SKINCOLOR_MAGENTA:
case SKINCOLOR_FUCHSIA:
case SKINCOLOR_MOONSET:
case SKINCOLOR_VIOLET:
textcolor = V_MAGENTAMAP;
break;
case SKINCOLOR_DUSK:
case SKINCOLOR_TOXIC:
case SKINCOLOR_MAUVE:
case SKINCOLOR_LAVENDER:
case SKINCOLOR_BYZANTIUM:
case SKINCOLOR_POMEGRANATE:
textcolor = V_LAVENDERMAP;
break;
}
return textcolor;
}
/*--------------------------------------------------
UINT8 K_ColorRelativeLuminance(UINT8 r, UINT8 g, UINT8 b)
See header file for description.
--------------------------------------------------*/
UINT8 K_ColorRelativeLuminance(UINT8 r, UINT8 g, UINT8 b)
{
UINT32 redweight = 1063 * r;
@ -400,7 +548,6 @@ UINT8 K_ColorRelativeLuminance(UINT8 r, UINT8 g, UINT8 b)
See header file for description.
--------------------------------------------------*/
void K_RainbowColormap(UINT8 *dest_colormap, UINT8 skincolor)
{
INT32 i;
@ -444,14 +591,11 @@ void K_RainbowColormap(UINT8 *dest_colormap, UINT8 skincolor)
}
}
/** \brief Generates a translation colormap for Kart, to replace R_GenerateTranslationColormap in r_draw.c
/*--------------------------------------------------
void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color)
\param dest_colormap colormap to populate
\param skinnum number of skin, TC_DEFAULT or TC_BOSS
\param color translation color
\return void
*/
See header file for description.
--------------------------------------------------*/
void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color)
{
INT32 i;
@ -505,12 +649,11 @@ void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color)
}
}
/** \brief Pulls kart color by name, to replace R_GetColorByName in r_draw.c
/*--------------------------------------------------
UINT8 K_GetKartColorByName(const char *name)
\param name color name
\return 0
*/
See header file for description.
--------------------------------------------------*/
UINT8 K_GetKartColorByName(const char *name)
{
UINT8 color = (UINT8)atoi(name);

View file

@ -23,6 +23,21 @@ extern UINT8 colortranslations[MAXTRANSLATIONS][16];
extern const char *KartColor_Names[MAXSKINCOLORS];
extern const UINT8 KartColor_Opposite[MAXSKINCOLORS*2];
/*--------------------------------------------------
INT32 K_SkincolorToTextColor(UINT8 skincolor);
Gives you the "text" color (V_ constants) from a skincolor (SKINCOLOR_ constants).
Used primarily by the chat system.
Input Arguments:-
skincolor - SKINCOLOR_ constant
Return:-
V_ constant for font coloring
--------------------------------------------------*/
INT32 K_SkincolorToTextColor(UINT8 skincolor);
/*--------------------------------------------------
UINT8 K_ColorRelativeLuminance(UINT8 r, UINT8 g, UINT8 b);

605
src/k_grandprix.c Normal file
View file

@ -0,0 +1,605 @@
// SONIC ROBO BLAST 2 KART
//-----------------------------------------------------------------------------
// Copyright (C) 2018-2020 by Sally "TehRealSalt" Cochenour
// Copyright (C) 2018-2020 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_grandprix.c
/// \brief Grand Prix mode game logic & bot behaviors
#include "k_grandprix.h"
#include "doomdef.h"
#include "d_player.h"
#include "g_game.h"
#include "k_bot.h"
#include "k_kart.h"
#include "m_random.h"
#include "r_things.h"
struct grandprixinfo grandprixinfo;
/*--------------------------------------------------
UINT8 K_BotStartingDifficulty(SINT8 value)
See header file for description.
--------------------------------------------------*/
UINT8 K_BotStartingDifficulty(SINT8 value)
{
// startingdifficulty: Easy = 3, Normal = 6, Hard = 9
SINT8 difficulty = (value + 1) * 3;
if (difficulty > MAXBOTDIFFICULTY)
{
difficulty = MAXBOTDIFFICULTY;
}
else if (difficulty < 1)
{
difficulty = 1;
}
return difficulty;
}
/*--------------------------------------------------
INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers)
See header file for description.
--------------------------------------------------*/
INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers)
{
INT16 points;
if (position >= numplayers || position == 0)
{
// Invalid position, no points
return 0;
}
points = numplayers - position;
// Give bonus to high-ranking players, depending on player count
// This rounds out the point gain when you get 1st every race,
// and gives bots able to catch up in points if a player gets an early lead.
// The maximum points you can get in a cup is: ((number of players - 1) + (max extra points)) * (number of races)
// 8P: (7 + 5) * 5 = 60 maximum points
// 12P: (11 + 5) * 5 = 80 maximum points
// 16P: (15 + 5) * 5 = 100 maximum points
switch (numplayers)
{
case 0: case 1: case 2: // 1v1
break; // No bonus needed.
case 3: case 4: // 3-4P
if (position == 1) { points += 1; } // 1st gets +1 extra point
break;
case 5: case 6: // 5-6P
if (position == 1) { points += 3; } // 1st gets +3 extra points
else if (position == 2) { points += 1; } // 2nd gets +1 extra point
break;
default: // Normal matches
if (position == 1) { points += 5; } // 1st gets +5 extra points
else if (position == 2) { points += 3; } // 2nd gets +3 extra points
else if (position == 3) { points += 1; } // 3rd gets +1 extra point
break;
}
// somehow underflowed?
if (points < 0)
{
points = 0;
}
return points;
}
/*--------------------------------------------------
void K_InitGrandPrixBots(void)
See header file for description.
--------------------------------------------------*/
void K_InitGrandPrixBots(void)
{
const char *defaultbotskinname = "eggrobo";
SINT8 defaultbotskin = R_SkinAvailable(defaultbotskinname);
const UINT8 startingdifficulty = K_BotStartingDifficulty(grandprixinfo.gamespeed);
UINT8 difficultylevels[MAXPLAYERS];
UINT8 playercount = 8;
UINT8 wantedbots = 0;
UINT8 numplayers = 0;
UINT8 competitors[MAXSPLITSCREENPLAYERS];
boolean skinusable[MAXSKINS];
UINT8 botskinlist[MAXPLAYERS];
UINT8 botskinlistpos = 0;
UINT8 newplayernum = 0;
UINT8 i, j;
if (defaultbotskin == -1)
{
// This shouldn't happen, but just in case
defaultbotskin = 0;
}
memset(competitors, MAXPLAYERS, sizeof (competitors));
memset(botskinlist, defaultbotskin, sizeof (botskinlist));
// init usable bot skins list
for (i = 0; i < MAXSKINS; i++)
{
if (i < numskins)
{
skinusable[i] = true;
}
else
{
skinusable[i] = false;
}
}
#if MAXPLAYERS != 16
I_Error("GP bot difficulty levels need rebalacned for the new player count!\n");
#endif
if (grandprixinfo.masterbots)
{
// Everyone is max difficulty!!
memset(difficultylevels, MAXBOTDIFFICULTY, sizeof (difficultylevels));
}
else
{
// init difficulty levels list
difficultylevels[0] = max(1, startingdifficulty);
difficultylevels[1] = max(1, startingdifficulty-1);
difficultylevels[2] = max(1, startingdifficulty-2);
difficultylevels[3] = max(1, startingdifficulty-3);
difficultylevels[4] = max(1, startingdifficulty-3);
difficultylevels[5] = max(1, startingdifficulty-4);
difficultylevels[6] = max(1, startingdifficulty-4);
difficultylevels[7] = max(1, startingdifficulty-4);
difficultylevels[8] = max(1, startingdifficulty-5);
difficultylevels[9] = max(1, startingdifficulty-5);
difficultylevels[10] = max(1, startingdifficulty-5);
difficultylevels[11] = max(1, startingdifficulty-6);
difficultylevels[12] = max(1, startingdifficulty-6);
difficultylevels[13] = max(1, startingdifficulty-6);
difficultylevels[14] = max(1, startingdifficulty-7);
difficultylevels[15] = max(1, startingdifficulty-7);
}
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
{
if (numplayers < MAXSPLITSCREENPLAYERS && !players[i].spectator)
{
competitors[numplayers] = i;
skinusable[players[i].skin] = false;
numplayers++;
}
else
{
players[i].spectator = true; // force spectate for all other players, if they happen to exist?
}
}
}
if (numplayers > 2)
{
// Add 3 bots per player beyond 2P
playercount += (numplayers-2) * 3;
}
wantedbots = playercount - numplayers;
// Create rival list
if (numplayers > 0)
{
for (i = 0; i < SKINRIVALS; i++)
{
for (j = 0; j < numplayers; j++)
{
player_t *p = &players[competitors[j]];
char *rivalname = skins[p->skin].rivals[i];
SINT8 rivalnum = R_SkinAvailable(rivalname);
if (rivalnum != -1 && skinusable[rivalnum])
{
botskinlist[botskinlistpos] = rivalnum;
skinusable[rivalnum] = false;
botskinlistpos++;
}
}
}
}
// Pad the remaining list with random skins if we need to
if (botskinlistpos < wantedbots)
{
for (i = botskinlistpos; i < wantedbots; i++)
{
UINT8 val = M_RandomKey(numskins);
UINT8 loops = 0;
while (!skinusable[val])
{
if (loops >= numskins)
{
// no more skins
break;
}
val++;
if (val >= numskins)
{
val = 0;
}
loops++;
}
if (loops >= numskins)
{
// leave the rest of the table as the default skin
break;
}
botskinlist[i] = val;
skinusable[val] = false;
}
}
for (i = 0; i < wantedbots; i++)
{
if (!K_AddBot(botskinlist[i], difficultylevels[i], &newplayernum))
{
break;
}
}
}
/*--------------------------------------------------
static INT16 K_RivalScore(player_t *bot)
Creates a "rival score" for a bot, used to determine which bot is the
most deserving of the rival status.
Input Arguments:-
bot - Player to check.
Return:-
"Rival score" value.
--------------------------------------------------*/
static INT16 K_RivalScore(player_t *bot)
{
const UINT16 difficulty = bot->botvars.difficulty;
const UINT16 score = bot->score;
SINT8 roundnum = 1, roundsleft = 1;
UINT16 lowestscore = UINT16_MAX;
UINT8 lowestdifficulty = MAXBOTDIFFICULTY;
UINT8 i;
if (grandprixinfo.cup != NULL)
{
roundnum = grandprixinfo.roundnum;
roundsleft = grandprixinfo.cup->numlevels - grandprixinfo.roundnum;
}
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
{
continue;
}
if (players[i].score < lowestscore)
{
lowestscore = players[i].score;
}
if (players[i].bot == true && players[i].botvars.difficulty < lowestdifficulty)
{
lowestdifficulty = players[i].botvars.difficulty;
}
}
// In the early game, difficulty is more important.
// This will try to influence the higher difficulty bots to get rival more often & get even more points.
// However, when we're running low on matches left, we need to focus more on raw score!
return ((difficulty - lowestdifficulty) * roundsleft) + ((score - lowestscore) * roundnum);
}
/*--------------------------------------------------
void K_UpdateGrandPrixBots(void)
See header file for description.
--------------------------------------------------*/
void K_UpdateGrandPrixBots(void)
{
player_t *oldrival = NULL;
player_t *newrival = NULL;
UINT16 newrivalscore = 0;
UINT8 i;
// Find the rival.
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator || !players[i].bot)
{
continue;
}
if (players[i].botvars.diffincrease)
{
players[i].botvars.difficulty += players[i].botvars.diffincrease;
if (players[i].botvars.difficulty > MAXBOTDIFFICULTY)
{
players[i].botvars.difficulty = MAXBOTDIFFICULTY;
}
players[i].botvars.diffincrease = 0;
}
if (players[i].botvars.rival)
{
if (oldrival == NULL)
{
// Record the old rival to compare with our calculated new rival
oldrival = &players[i];
}
else
{
// Somehow 2 rivals were set?!
// Let's quietly fix our mess...
players[i].botvars.rival = false;
}
}
}
// Find the bot with the best average of score & difficulty.
for (i = 0; i < MAXPLAYERS; i++)
{
UINT16 ns = 0;
if (!playeringame[i] || players[i].spectator || !players[i].bot)
{
continue;
}
ns = K_RivalScore(&players[i]);
if (ns > newrivalscore)
{
newrival = &players[i];
newrivalscore = ns;
}
}
// Even if there's a new rival, we want to make sure that they're a better fit than the current one.
if (oldrival != newrival)
{
if (oldrival != NULL)
{
UINT16 os = K_RivalScore(oldrival);
if (newrivalscore < os + 5)
{
// This rival's only *slightly* better, no need to fire the old one.
// Our current rival's just fine, thank you very much.
return;
}
// Hand over your badge.
oldrival->botvars.rival = false;
}
// Set our new rival!
newrival->botvars.rival = true;
}
}
/*--------------------------------------------------
static UINT8 K_BotExpectedStanding(player_t *bot)
Predicts what placement a bot was expected to be in.
Used for determining if a bot's difficulty should raise.
Input Arguments:-
bot - Player to check.
Return:-
Position number the bot was expected to be in.
--------------------------------------------------*/
static UINT8 K_BotExpectedStanding(player_t *bot)
{
UINT8 pos = 1;
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (i == (bot - players))
{
continue;
}
if (playeringame[i] && !players[i].spectator)
{
if (players[i].bot)
{
if (players[i].botvars.difficulty > bot->botvars.difficulty)
{
pos++;
}
else if (players[i].score > bot->score)
{
pos++;
}
}
else
{
// Human player, always increment
pos++;
}
}
}
return pos;
}
/*--------------------------------------------------
void K_IncreaseBotDifficulty(player_t *bot)
See header file for description.
--------------------------------------------------*/
void K_IncreaseBotDifficulty(player_t *bot)
{
UINT8 expectedstanding;
INT16 standingdiff;
if (bot->botvars.difficulty >= MAXBOTDIFFICULTY)
{
// Already at max difficulty, don't need to increase
return;
}
// Increment bot difficulty based on what position you were meant to come in!
expectedstanding = K_BotExpectedStanding(bot);
standingdiff = expectedstanding - bot->kartstuff[k_position];
if (standingdiff >= -2)
{
UINT8 increase;
if (standingdiff > 5)
{
increase = 3;
}
else if (standingdiff > 2)
{
increase = 2;
}
else
{
increase = 1;
}
bot->botvars.diffincrease = increase;
}
else
{
bot->botvars.diffincrease = 0;
}
}
/*--------------------------------------------------
void K_FakeBotResults(player_t *bot)
See header file for description.
--------------------------------------------------*/
void K_FakeBotResults(player_t *bot)
{
const UINT32 distfactor = FixedMul(32 * bot->mo->scale, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT;
UINT32 worstdist = 0;
tic_t besttime = UINT32_MAX;
UINT8 numplayers = 0;
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator)
{
numplayers++;
if (players[i].exiting && players[i].realtime < besttime)
{
besttime = players[i].realtime;
}
if (players[i].distancetofinish > worstdist)
{
worstdist = players[i].distancetofinish;
}
}
}
if (besttime == UINT32_MAX // No one finished, so you don't finish either.
|| bot->distancetofinish >= worstdist) // Last place, you aren't going to finish.
{
bot->pflags |= PF_TIMEOVER;
return;
}
// hey, you "won"
bot->exiting = 2;
bot->realtime += (bot->distancetofinish / distfactor);
bot->distancetofinish = 0;
K_IncreaseBotDifficulty(bot);
}
/*--------------------------------------------------
void K_PlayerLoseLife(player_t *player)
See header file for description.
--------------------------------------------------*/
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
}
/*--------------------------------------------------
boolean K_CanChangeRules(void)
See header file for description.
--------------------------------------------------*/
boolean K_CanChangeRules(void)
{
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!
return false;
}
if (modeattacking == true)
{
// Don't cheat the rules of Time Trials!
return false;
}
return true;
}

144
src/k_grandprix.h Normal file
View file

@ -0,0 +1,144 @@
// SONIC ROBO BLAST 2 KART
//-----------------------------------------------------------------------------
// Copyright (C) 2018-2020 by Sally "TehRealSalt" Cochenour
// Copyright (C) 2018-2020 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_grandprix.h
/// \brief Grand Prix mode game logic & bot behaviors
#ifndef __K_GRANDPRIX__
#define __K_GRANDPRIX__
#include "doomdef.h"
#include "doomstat.h"
extern struct grandprixinfo
{
boolean gp; ///< If true, then we are in a Grand Prix.
UINT8 roundnum; ///< Round number. If 0, this is a single session from the warp command.
cupheader_t *cup; ///< Which cup are we playing?
UINT8 gamespeed; ///< Copy of gamespeed, just to make sure you can't cheat it with cvars
boolean encore; ///< Ditto, but for encore mode
boolean masterbots; ///< If true, all bots should be max difficulty (Master Mode)
boolean initalize; ///< If true, we need to initialize a new session.
boolean wonround; ///< If false, then we retry the map instead of going to the next.
} grandprixinfo;
/*--------------------------------------------------
UINT8 K_BotStartingDifficulty(SINT8 value);
Determines the starting difficulty of the bots
for a specific game speed.
Input Arguments:-
value - Game speed value to use.
Return:-
Bot difficulty level.
--------------------------------------------------*/
UINT8 K_BotStartingDifficulty(SINT8 value);
/*--------------------------------------------------
INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers);
Calculates the number of points that a player would
recieve if they won the round.
Input Arguments:-
position - Finishing position.
numplayers - Number of players in the game.
Return:-
Number of points to give.
--------------------------------------------------*/
INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers);
/*--------------------------------------------------
void K_InitGrandPrixBots(void);
Spawns bots specifically tailored for Grand Prix mode.
--------------------------------------------------*/
void K_InitGrandPrixBots(void);
/*--------------------------------------------------
void K_UpdateGrandPrixBots(void);
Updates bot settings based on the the results of the race.
--------------------------------------------------*/
void K_UpdateGrandPrixBots(void);
/*--------------------------------------------------
void K_IncreaseBotDifficulty(player_t *bot);
Increases the difficulty of this bot when they finish the race.
Input Arguments:-
bot - Player to do this for.
Return:-
None
--------------------------------------------------*/
void K_IncreaseBotDifficulty(player_t *bot);
/*--------------------------------------------------
void K_FakeBotResults(player_t *bot);
Fakes the results of the race, when all human players have
already finished and only bots were remaining.
Input Arguments:-
bot - Player to do this for.
Return:-
None
--------------------------------------------------*/
void K_FakeBotResults(player_t *bot);
/*--------------------------------------------------
void K_PlayerLoseLife(player_t *player);
Removes a life from a human player.
Input Arguments:-
player - Player to do this for.
Return:-
None
--------------------------------------------------*/
void K_PlayerLoseLife(player_t *player);
/*--------------------------------------------------
boolean K_CanChangeRules(void);
Returns whenver or not the server is allowed
to change the game rules.
Input Arguments:-
None
Return:-
None
--------------------------------------------------*/
boolean K_CanChangeRules(void);
#endif

File diff suppressed because it is too large Load diff

View file

@ -13,6 +13,7 @@
#define KART_FULLTURN 800
player_t *K_GetItemBoxPlayer(mobj_t *mobj);
angle_t K_ReflectAngle(angle_t angle, angle_t against, fixed_t maxspeed, fixed_t yourspeed);
void K_RegisterKartStuff(void);

View file

@ -8,6 +8,7 @@
#include "m_random.h"
#include "m_cond.h" // M_UpdateUnlockablesAndExtraEmblems
#include "p_tick.h" // leveltime
#include "k_grandprix.h"
// Online rankings for the main gametypes.
// This array is saved to the gamedata.
@ -25,6 +26,27 @@ INT16 nospectategrief[MAXPLAYERS];
SINT8 speedscramble = -1;
SINT8 encorescramble = -1;
SINT8 K_UsingPowerLevels(void)
{
SINT8 pt = PWRLV_DISABLED;
if (!cv_kartusepwrlv.value || !netgame || grandprixinfo.gp == true)
{
return PWRLV_DISABLED;
}
if (G_RaceGametype())
{
pt = PWRLV_RACE;
}
else if (G_BattleGametype())
{
pt = PWRLV_BATTLE;
}
return pt;
}
void K_ClearClientPowerLevels(void)
{
UINT8 i, j;

View file

@ -21,6 +21,7 @@ extern UINT16 vspowerlevel[PWRLV_NUMTYPES];
extern UINT16 clientpowerlevels[MAXPLAYERS][PWRLV_NUMTYPES];
extern INT16 nospectategrief[MAXPLAYERS];
SINT8 K_UsingPowerLevels(void);
void K_ClearClientPowerLevels(void);
INT16 K_CalculatePowerLevelInc(INT16 diff);
INT16 K_CalculatePowerLevelAvg(void);

View file

@ -19,6 +19,7 @@ enum hud {
hud_minimap,
hud_item,
hud_position,
hud_names, // online nametags
hud_check, // "CHECK" f-zero indicator
hud_minirankings, // Rankings to the left
hud_battlebumpers, // mini rankings battle bumpers.

View file

@ -22,6 +22,7 @@
#include "v_video.h"
#include "w_wad.h"
#include "z_zone.h"
#include "hu_stuff.h"
#include "lua_script.h"
#include "lua_libs.h"

View file

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

View file

@ -60,6 +60,7 @@
#include "k_pwrlv.h"
#include "d_player.h" // KITEM_ constants
#include "k_color.h"
#include "k_grandprix.h"
#include "i_joy.h" // for joystick menu controls
@ -226,7 +227,8 @@ menu_t SP_MainDef, MP_MainDef, OP_MainDef;
menu_t MISC_ScrambleTeamDef, MISC_ChangeTeamDef, MISC_ChangeSpectateDef;
// Single Player
//static void M_LoadGame(INT32 choice);
static void M_GrandPrixTemp(INT32 choice);
static void M_StartGrandPrix(INT32 choice);
static void M_TimeAttack(INT32 choice);
static boolean M_QuitTimeAttackMenu(void);
static void M_BreakTheCapsules(INT32 choice);
@ -239,6 +241,7 @@ static void M_ModeAttackEndGame(INT32 choice);
static void M_SetGuestReplay(INT32 choice);
//static void M_ChoosePlayer(INT32 choice);
menu_t SP_LevelStatsDef;
static menu_t SP_GrandPrixTempDef;
static menu_t SP_TimeAttackDef, SP_ReplayDef, SP_GuestReplayDef, SP_GhostDef;
//static menu_t SP_NightsAttackDef, SP_NightsReplayDef, SP_NightsGuestReplayDef, SP_NightsGhostDef;
@ -462,6 +465,13 @@ static consvar_t cv_dummycontinues = {"dummycontinues", "0", CV_HIDEN, liveslimi
//static consvar_t cv_dummymares = {"dummymares", "Overall", CV_HIDEN|CV_CALL, dummymares_cons_t, Dummymares_OnChange, 0, NULL, NULL, 0, 0, NULL};
static consvar_t cv_dummystaff = {"dummystaff", "0", CV_HIDEN|CV_CALL, dummystaff_cons_t, Dummystaff_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t dummygpdifficulty_cons_t[] = {{0, "Easy"}, {1, "Normal"}, {2, "Hard"}, {3, "Master"}, {0, NULL}};
static CV_PossibleValue_t dummygpcup_cons_t[50] = {{1, "TEMP"}}; // A REALLY BIG NUMBER, SINCE THIS IS TEMP UNTIL NEW MENUS
static consvar_t cv_dummygpdifficulty = {"dummygpdifficulty", "Normal", CV_HIDEN, dummygpdifficulty_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static consvar_t cv_dummygpencore = {"dummygpencore", "Off", CV_HIDEN, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
static consvar_t cv_dummygpcup = {"dummygpcup", "TEMP", CV_HIDEN, dummygpcup_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
// ==========================================================================
// ORGANIZATION START.
// ==========================================================================
@ -801,30 +811,30 @@ static menuitem_t SR_EmblemHintMenu[] =
// Single Player Main
static menuitem_t SP_MainMenu[] =
{
//{IT_CALL | IT_STRING, NULL, "Grand Prix", M_LoadGame, 92},
{IT_SECRET, NULL, "Time Attack", M_TimeAttack, 100},
{IT_SECRET, NULL, "Break the Capsules", M_BreakTheCapsules, 108},
{IT_STRING|IT_CALL, NULL, "Grand Prix", M_GrandPrixTemp, 92},
{IT_SECRET, NULL, "Time Attack", M_TimeAttack, 100},
{IT_SECRET, NULL, "Break the Capsules", M_BreakTheCapsules, 108},
};
enum
{
//spgrandprix,
spgrandprix,
sptimeattack,
spbreakthecapsules
};
// Single Player Load Game
/*static menuitem_t SP_LoadGameMenu[] =
static menuitem_t SP_GrandPrixPlaceholderMenu[] =
{
{IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLoadSave, '\0'}, // dummy menuitem for the control func
};
{IT_STRING|IT_CVAR, NULL, "Character", &cv_chooseskin, 10},
{IT_STRING|IT_CVAR, NULL, "Color", &cv_playercolor, 20},
// Single Player Level Select
static menuitem_t SP_LevelSelectMenu[] =
{
{IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 78},
{IT_WHITESTRING|IT_CALL, NULL, "Start", M_LevelSelectWarp, 130},
};*/
{IT_STRING|IT_CVAR, NULL, "Difficulty", &cv_dummygpdifficulty, 40},
{IT_STRING|IT_CVAR, NULL, "Encore Mode", &cv_dummygpencore, 50},
{IT_STRING|IT_CVAR, NULL, "Cup", &cv_dummygpcup, 70},
{IT_STRING|IT_CALL, NULL, "Start", M_StartGrandPrix, 80},
};
// Single Player Time Attack
static menuitem_t SP_TimeAttackMenu[] =
@ -1805,6 +1815,8 @@ menu_t SP_LevelStatsDef =
NULL
};
static menu_t SP_GrandPrixTempDef = DEFAULTMENUSTYLE(NULL, SP_GrandPrixPlaceholderMenu, &MainDef, 60, 30);
static menu_t SP_TimeAttackDef =
{
"M_ATTACK",
@ -3369,6 +3381,10 @@ void M_Init(void)
//CV_RegisterVar(&cv_dummymares);
CV_RegisterVar(&cv_dummystaff);
CV_RegisterVar(&cv_dummygpdifficulty);
CV_RegisterVar(&cv_dummygpencore);
CV_RegisterVar(&cv_dummygpcup);
quitmsg[QUITMSG] = M_GetText("Eggman's tied explosives\nto your girlfriend, and\nwill activate them if\nyou press the 'Y' key!\nPress 'N' to save her!\n\n(Press 'Y' to quit)");
quitmsg[QUITMSG1] = M_GetText("What would Tails say if\nhe saw you quitting the game?\n\n(Press 'Y' to quit)");
quitmsg[QUITMSG2] = M_GetText("Hey!\nWhere do ya think you're goin'?\n\n(Press 'Y' to quit)");
@ -4214,6 +4230,35 @@ static void M_PatchSkinNameTable(void)
return;
}
//
// M_PrepareCupList
//
static void M_PrepareCupList(void)
{
cupheader_t *cup = kartcupheaders;
INT32 i = 0;
memset(dummygpcup_cons_t, 0, sizeof (dummygpcup_cons_t));
while (cup != NULL)
{
dummygpcup_cons_t[i].strvalue = cup->name;
dummygpcup_cons_t[i].value = i+1;
// this will probably crash or do something stupid at over 50 cups,
// but this is all behavior that gets completely overwritten in new-menus, so I'm not worried
i++;
cup = cup->next;
}
for (; i < 50; i++)
{
dummygpcup_cons_t[i].strvalue = NULL;
dummygpcup_cons_t[i].value = 0;
}
CV_SetValue(&cv_dummygpcup, 1); // This causes crash sometimes?!
}
// Call before showing any level-select menus
static void M_PrepareLevelSelect(void)
{
@ -5442,7 +5487,11 @@ static void DrawReplayHutReplayInfo(void)
}
// Character face!
if (W_CheckNumForName(skins[demolist[dir_on[menudepthleft]].standings[0].skin].facewant) != LUMPERROR)
// Lat: 08/06/2020: For some reason missing skins have their value set to 255 (don't even ask me why I didn't write this)
// and for an even STRANGER reason this passes the first check below, so we're going to make sure that the skin here ISN'T 255 before we do anything stupid.
if (demolist[dir_on[menudepthleft]].standings[0].skin != 0xFF && W_CheckNumForName(skins[demolist[dir_on[menudepthleft]].standings[0].skin].facewant) != LUMPERROR)
{
patch = facewantprefix[demolist[dir_on[menudepthleft]].standings[0].skin];
colormap = R_GetTranslationColormap(
@ -5640,7 +5689,11 @@ static void M_DrawReplayStartMenu(void)
V_DrawString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d", demolist[dir_on[menudepthleft]].standings[i].timeorscore));
// Character face!
if (W_CheckNumForName(skins[demolist[dir_on[menudepthleft]].standings[i].skin].facerank) != LUMPERROR)
// Lat: 08/06/2020: For some reason missing skins have their value set to 255 (don't even ask me why I didn't write this)
// and for an even STRANGER reason this passes the first check below, so we're going to make sure that the skin here ISN'T 255 before we do anything stupid.
if (demolist[dir_on[menudepthleft]].standings[0].skin != 0xFF && W_CheckNumForName(skins[demolist[dir_on[menudepthleft]].standings[i].skin].facerank) != LUMPERROR)
{
patch = facerankprefix[demolist[dir_on[menudepthleft]].standings[i].skin];
colormap = R_GetTranslationColormap(
@ -6659,6 +6712,8 @@ static void M_Credits(INT32 choice)
static void M_SinglePlayerMenu(INT32 choice)
{
(void)choice;
SP_MainMenu[spgrandprix].status = IT_CALL|IT_STRING;
SP_MainMenu[sptimeattack].status =
(M_SecretUnlocked(SECRET_TIMEATTACK)) ? IT_CALL|IT_STRING : IT_SECRET;
SP_MainMenu[spbreakthecapsules].status =
@ -7543,6 +7598,80 @@ static void M_HandleLevelStats(INT32 choice)
}
}
static void M_GrandPrixTemp(INT32 choice)
{
(void)choice;
M_PatchSkinNameTable();
M_PrepareCupList();
M_SetupNextMenu(&SP_GrandPrixTempDef);
}
// Start Grand Prix!
static void M_StartGrandPrix(INT32 choice)
{
cupheader_t *gpcup = kartcupheaders;
(void)choice;
if (gpcup == NULL)
{
// welp
I_Error("No cup definitions for GP\n");
return;
}
M_ClearMenus(true);
memset(&grandprixinfo, 0, sizeof(struct grandprixinfo));
switch (cv_dummygpdifficulty.value)
{
case 0:
grandprixinfo.gamespeed = KARTSPEED_EASY;
break;
case 1:
default:
grandprixinfo.gamespeed = KARTSPEED_NORMAL;
break;
case 2:
grandprixinfo.gamespeed = KARTSPEED_HARD;
break;
case 3:
grandprixinfo.gamespeed = KARTSPEED_HARD;
grandprixinfo.masterbots = true;
break;
}
grandprixinfo.encore = (boolean)(cv_dummygpencore.value);
while (gpcup != NULL && gpcup->id != cv_dummygpcup.value-1)
{
gpcup = gpcup->next;
}
if (gpcup == NULL)
{
gpcup = kartcupheaders;
}
grandprixinfo.cup = gpcup;
grandprixinfo.gp = true;
grandprixinfo.roundnum = 1;
grandprixinfo.wonround = false;
grandprixinfo.initalize = true;
G_DeferedInitNew(
false,
G_BuildMapName(grandprixinfo.cup->levellist[0] + 1),
(UINT8)(cv_chooseskin.value - 1),
(UINT8)(cv_splitplayers.value - 1),
false
);
}
// ===========
// MODE ATTACK
// ===========

View file

@ -29,6 +29,7 @@
#include "k_kart.h" // SRB2kart
#include "k_battle.h"
#include "k_pwrlv.h"
#include "k_grandprix.h"
// CTF player names
#define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : ""
@ -1968,71 +1969,146 @@ void P_CheckPointLimit(void)
// Checks whether or not to end a race netgame.
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 everyonedone = true;
boolean eliminatebots = false;
boolean griefed = false;
// Check if all the players in the race have finished. If so, end the level.
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator || players[i].exiting || players[i].bot || !players[i].lives)
continue;
if (nospectategrief[i] != -1) // prevent spectate griefing
{
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.)
// Set this so that we can check for bots that need to get eliminated, though!
eliminatebots = true;
continue;
}
everyonedone = false;
}
}
if (i == MAXPLAYERS) // finished
// If we returned here with bots left, then the last place bot may have a chance to finish the map and NOT get time over.
// Not that it affects anything, they didn't make the map take longer or even get any points from it. But... it's a bit inconsistent!
// So if there's any bots, we'll let the game skip this, continue onto calculating eliminatelast, THEN we return true anyway.
if (everyonedone && !eliminatebots)
{
// Everyone's finished, we're done here!
racecountdown = exitcountdown = 0;
return true;
}
for (j = 0; j < MAXPLAYERS; j++)
if (numplayersingame <= 1)
{
if (nospectategrief[j] != -1) // prevent spectate griefing
griefed = true;
if (!playeringame[j] || players[j].spectator)
continue;
numplayersingame++;
if (players[j].exiting)
numexiting++;
// Never do this without enough players.
eliminatelast = false;
}
else
{
if (grandprixinfo.gp == true)
{
// 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
for (j = i+1; j < MAXPLAYERS; j++)
// Everyone's done playing but one guy apparently.
// 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;
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
{
P_DoTimeOver(&players[i]);
racecountdown = exitcountdown = 0;
return true;
}
// Everyone should be done playing at this point now.
racecountdown = exitcountdown = 0;
return true;
}
if (!racecountdown) // Check to see if the winners have finished, to set countdown.
if (everyonedone)
{
// See above: there might be bots that are still going, but all players are done, so we can exit now.
racecountdown = exitcountdown = 0;
return true;
}
// 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;
winningpos = max(1, numplayersingame/2);
if (numplayersingame % 2) // any remainder?
{
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++)
nospectategrief[j] = -1;
memset(nospectategrief, -1, sizeof (nospectategrief));
}
// Turns out we're still having a good time & playing the game, we didn't have to do anything :)
return false;
}
@ -2150,11 +2226,28 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source)
{
if (target->flags & MF_MONITOR || target->type == MT_RANDOMITEM)
{
UINT8 i;
P_SetTarget(&target->target, source);
source->player->numboxes++;
if (cv_itemrespawn.value && (netgame || multiplayer))
for (i = 0; i < MAXPLAYERS; i++)
{
target->fuse = cv_itemrespawntime.value*TICRATE + 2; // Random box generation
if (&players[i] == source->player)
{
continue;
}
if (playeringame[i] && !players[i].spectator && players[i].lives != 0)
{
break;
}
}
if (i < MAXPLAYERS)
{
// Respawn items in multiplayer, don't respawn them when alone
target->fuse = 2*TICRATE + 2;
}
}
@ -2246,20 +2339,6 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source)
target->flags |= MF_NOBLOCKMAP|MF_NOCLIPHEIGHT;
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;
if (target->player == &players[consoleplayer])
@ -3153,6 +3232,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|| inflictor->type == MT_SMK_THWOMP || inflictor->player))
{
player->kartstuff[k_sneakertimer] = 0;
player->kartstuff[k_numsneakers] = 0;
K_SpinPlayer(player, source, 1, inflictor, false);
K_KartPainEnergyFling(player);

View file

@ -324,39 +324,10 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
// This makes it a bit more interesting & unique than just being a speed boost in a pre-defined direction
if (savemomx || savemomy)
{
angle_t momang;
INT32 angoffset;
boolean subtract = false;
momang = R_PointToAngle2(0, 0, savemomx, savemomy);
angoffset = momang;
angoffset -= spring->angle; // Subtract
// Flip on wrong side
if ((angle_t)angoffset > ANGLE_180)
{
angoffset = InvAngle((angle_t)angoffset);
subtract = !subtract;
}
// Fix going directly against the spring's angle sending you the wrong way
if ((spring->angle - momang) > ANGLE_90)
angoffset = ANGLE_180 - angoffset;
// Offset is reduced to cap it (90 / 2 = max of 45 degrees)
angoffset /= 2;
// Reduce further based on how slow your speed is compared to the spring's speed
if (finalSpeed > objectSpeed)
angoffset = FixedDiv(angoffset, FixedDiv(finalSpeed, objectSpeed));
if (subtract)
angoffset = (signed)(spring->angle) - angoffset;
else
angoffset = (signed)(spring->angle) + angoffset;
finalAngle = angoffset;
finalAngle = K_ReflectAngle(
R_PointToAngle2(0, 0, savemomx, savemomy), finalAngle,
objectSpeed, finalSpeed
);
}
}

View file

@ -8348,10 +8348,9 @@ void P_MobjThinker(mobj_t *mobj)
if (p)
{
if (p->kartstuff[k_sneakertimer] > mobj->movecount
|| p->kartstuff[k_levelbooster] > mobj->movecount)
if (p->kartstuff[k_sneakertimer] > mobj->movecount)
P_SetMobjState(mobj, S_BOOSTFLAME);
mobj->movecount = max(p->kartstuff[k_sneakertimer], p->kartstuff[k_levelbooster]);
mobj->movecount = p->kartstuff[k_sneakertimer];
}
}
@ -11407,9 +11406,6 @@ void P_RemoveSavegameMobj(mobj_t *mobj)
P_RemoveThinker((thinker_t *)mobj);
}
static CV_PossibleValue_t respawnitemtime_cons_t[] = {{1, "MIN"}, {300, "MAX"}, {0, NULL}};
consvar_t cv_itemrespawntime = {"respawnitemtime", "2", CV_NETVAR|CV_CHEAT, respawnitemtime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_itemrespawn = {"respawnitem", "On", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t flagtime_cons_t[] = {{0, "MIN"}, {300, "MAX"}, {0, NULL}};
consvar_t cv_flagtime = {"flagtime", "30", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, flagtime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_suddendeath = {"suddendeath", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
@ -11680,10 +11676,6 @@ void P_RespawnSpecials(void)
time = (time * 3) / max(1, mapheaderinfo[gamemap-1]->numlaps);
}
// only respawn items when cv_itemrespawn is on
//if (!cv_itemrespawn.value) // TODO: remove this cvar
//return;
// nothing left to respawn?
if (iquehead == iquetail)
return;

View file

@ -138,6 +138,7 @@ static void P_NetArchivePlayers(void)
WRITEFIXED(save_p, players[i].dashspeed);
WRITEINT32(save_p, players[i].dashtime);
WRITESINT8(save_p, players[i].lives);
WRITEUINT8(save_p, players[i].lostlife);
WRITESINT8(save_p, players[i].continues);
WRITESINT8(save_p, players[i].xtralife);
WRITEUINT8(save_p, players[i].gotcontinue);
@ -279,6 +280,8 @@ static void P_NetArchivePlayers(void)
// botvars_t
WRITEUINT8(save_p, players[i].botvars.difficulty);
WRITEUINT8(save_p, players[i].botvars.diffincrease);
WRITEUINT8(save_p, players[i].botvars.rival);
WRITEUINT32(save_p, players[i].botvars.itemdelay);
WRITEUINT32(save_p, players[i].botvars.itemconfirm);
WRITESINT8(save_p, players[i].botvars.turnconfirm);
@ -332,6 +335,7 @@ static void P_NetUnArchivePlayers(void)
players[i].dashspeed = READFIXED(save_p); // dashing speed
players[i].dashtime = READINT32(save_p); // dashing speed
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].xtralife = READSINT8(save_p); // Ring Extra Life counter
players[i].gotcontinue = READUINT8(save_p); // got continue from stage
@ -464,6 +468,8 @@ static void P_NetUnArchivePlayers(void)
// botvars_t
players[i].botvars.difficulty = READUINT8(save_p);
players[i].botvars.diffincrease = READUINT8(save_p);
players[i].botvars.rival = (boolean)READUINT8(save_p);
players[i].botvars.itemdelay = READUINT32(save_p);
players[i].botvars.itemconfirm = READUINT32(save_p);
players[i].botvars.turnconfirm = READSINT8(save_p);

View file

@ -88,6 +88,7 @@
#include "k_pwrlv.h"
#include "k_waypoint.h"
#include "k_bot.h"
#include "k_grandprix.h"
//
// Map MD5, calculated on level load.
@ -2415,22 +2416,20 @@ static void P_LevelInitStuff(void)
for (i = 0; i < MAXPLAYERS; i++)
{
#if 0
if ((netgame || multiplayer) && (gametype == GT_COMPETITION || players[i].lives <= 0))
if (grandprixinfo.gp == false)
{
// In Co-Op, replenish a user's lives if they are depleted.
players[i].lives = cv_startinglives.value;
players[i].lives = 3;
players[i].xtralife = 0;
players[i].totalring = 0;
}
#else
players[i].lives = 1; // SRB2Kart
#endif
players[i].realtime = racecountdown = exitcountdown = 0;
curlap = bestlap = 0; // SRB2Kart
players[i].lostlife = false;
players[i].gotcontinue = false;
players[i].xtralife = players[i].deadtimer = players[i].numboxes = players[i].totalring = players[i].laps = 0;
players[i].deadtimer = players[i].numboxes = players[i].laps = 0;
players[i].health = 1;
players[i].aiming = 0;
players[i].pflags &= ~PF_TIMEOVER;
@ -2464,8 +2463,23 @@ static void P_LevelInitStuff(void)
}
// SRB2Kart: map load variables
if (modeattacking) // Just play it safe and set everything
if (grandprixinfo.gp == true)
{
if (G_BattleGametype())
{
gamespeed = KARTSPEED_EASY;
}
else
{
gamespeed = grandprixinfo.gamespeed;
}
franticitems = false;
comeback = true;
}
else if (modeattacking)
{
// Just play it safe and set everything
gamespeed = KARTSPEED_HARD;
franticitems = false;
comeback = true;
@ -2490,11 +2504,6 @@ static void P_LevelInitStuff(void)
memset(&battleovertime, 0, sizeof(struct battleovertime));
speedscramble = encorescramble = -1;
if (!modeattacking)
{
K_UpdateMatchRaceBots();
}
}
//
@ -2679,12 +2688,6 @@ static void P_ForceCharacter(const char *forcecharskin)
}
SetPlayerSkin(consoleplayer, forcecharskin);
// normal player colors in single player
if ((unsigned)cv_playercolor.value != skins[players[consoleplayer].skin].prefcolor && !modeattacking)
{
CV_StealthSetValue(&cv_playercolor, skins[players[consoleplayer].skin].prefcolor);
players[consoleplayer].skincolor = skins[players[consoleplayer].skin].prefcolor;
}
}
}
@ -3381,6 +3384,28 @@ boolean P_SetupLevel(boolean skipprecip)
}
#endif
// NOW you can try to spawn in the Battle capsules, if there's not enough players for a match
K_SpawnBattleCapsules();
if (grandprixinfo.gp == true)
{
if (grandprixinfo.initalize == true)
{
K_InitGrandPrixBots();
grandprixinfo.initalize = false;
}
else if (grandprixinfo.wonround == true)
{
K_UpdateGrandPrixBots();
grandprixinfo.wonround = false;
}
}
else if (!modeattacking)
{
// We're in a Match Race, use simplistic randomized bots.
K_UpdateMatchRaceBots();
}
P_MapEnd();
// Remove the loading shit from the screen
@ -3432,9 +3457,6 @@ boolean P_SetupLevel(boolean skipprecip)
#endif
}
// NOW you can try to spawn in the Battle capsules, if there's not enough players for a match
K_SpawnBattleCapsules();
return true;
}

View file

@ -4049,52 +4049,38 @@ DoneSection2:
case 5: // Speed pad w/o spin
case 6: // Speed pad w/ spin
if (player->kartstuff[k_dashpadcooldown] != 0)
if (player->kartstuff[k_floorboost] != 0)
{
player->kartstuff[k_floorboost] = 2;
break;
}
i = P_FindSpecialLineFromTag(4, sector->tag, -1);
if (i != -1)
{
angle_t lineangle;
fixed_t linespeed;
lineangle = R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y);
linespeed = P_AproxDistance(lines[i].v2->x-lines[i].v1->x, lines[i].v2->y-lines[i].v1->y);
angle_t lineangle = R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y);
fixed_t linespeed = P_AproxDistance(lines[i].v2->x-lines[i].v1->x, lines[i].v2->y-lines[i].v1->y);
fixed_t playerspeed = P_AproxDistance(player->mo->momx, player->mo->momy);
// SRB2Kart: Scale the speed you get from them!
// This is scaled differently from other horizontal speed boosts from stuff like springs, because of how this is used for some ramp jumps.
if (player->mo->scale > mapobjectscale)
linespeed = FixedMul(linespeed, mapobjectscale + (player->mo->scale - mapobjectscale));
if (!(lines[i].flags & ML_EFFECT4))
{
P_UnsetThingPosition(player->mo);
if (roversector) // make FOF speed pads work
{
player->mo->x = roversector->soundorg.x;
player->mo->y = roversector->soundorg.y;
}
else
{
player->mo->x = sector->soundorg.x;
player->mo->y = sector->soundorg.y;
}
P_SetThingPosition(player->mo);
linespeed = FixedMul(linespeed, mapobjectscale + (player->mo->scale - mapobjectscale));
}
P_InstaThrust(player->mo, lineangle, linespeed);
lineangle = K_ReflectAngle(
R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy), lineangle,
playerspeed, linespeed
);
P_InstaThrust(player->mo, lineangle, max(linespeed, 2*playerspeed));
player->kartstuff[k_dashpadcooldown] = TICRATE/3;
player->trickpanel = 0;
S_StartSound(player->mo, sfx_spdpad);
{
sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons
if (cv_kartvoices.value)
S_StartSound(player->mo, sfx_kbost1+pick);
//K_TauntVoiceTimers(player);
}
player->kartstuff[k_floorboost] = 2;
S_StartSound(player->mo, sfx_cdfm62);
}
break;

View file

@ -40,7 +40,6 @@
#include "st_stuff.h"
#include "lua_script.h"
#include "lua_hook.h"
#include "k_bot.h"
// Objectplace
#include "m_cheat.h"
// SRB2kart
@ -48,6 +47,8 @@
#include "k_kart.h"
#include "console.h" // CON_LogMessage
#include "k_respawn.h"
#include "k_bot.h"
#include "k_grandprix.h"
#ifdef HW3SOUND
#include "hardware/hw3sound.h"
@ -949,7 +950,6 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings)
return;
player->kartstuff[k_rings] += num_rings;
//player->totalring += num_rings; // Used for GP lives later
if (player->kartstuff[k_rings] > 20)
player->kartstuff[k_rings] = 20; // Caps at 20 rings, sorry!
@ -967,8 +967,8 @@ void P_GivePlayerLives(player_t *player, INT32 numlives)
{
player->lives += numlives;
if (player->lives > 99)
player->lives = 99;
if (player->lives > 9)
player->lives = 9;
else if (player->lives < 1)
player->lives = 1;
}
@ -1686,12 +1686,20 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
// Player exits the map via sector trigger
void P_DoPlayerExit(player_t *player)
{
const boolean losing = K_IsPlayerLosing(player);
if (player->exiting || mapreset)
return;
if (P_IsLocalPlayer(player) && (!player->spectator && !demo.playback))
legitimateexit = true;
if (G_GametypeUsesLives() && losing)
{
// Remove a life from the losing player
K_PlayerLoseLife(player);
}
if (G_RaceGametype()) // If in Race Mode, allow
{
player->exiting = raceexittime+2;
@ -1702,7 +1710,7 @@ void P_DoPlayerExit(player_t *player)
if (P_IsDisplayPlayer(player))
{
sfxenum_t sfx_id;
if (K_IsPlayerLosing(player))
if (losing)
sfx_id = ((skin_t *)player->mo->skin)->soundsid[S_sfx[sfx_klose].skinsound];
else
sfx_id = ((skin_t *)player->mo->skin)->soundsid[S_sfx[sfx_kwin].skinsound];
@ -1710,7 +1718,7 @@ void P_DoPlayerExit(player_t *player)
}
else
{
if (K_IsPlayerLosing(player))
if (losing)
S_StartSound(player->mo, sfx_klose);
else
S_StartSound(player->mo, sfx_kwin);
@ -1720,10 +1728,6 @@ void P_DoPlayerExit(player_t *player)
if (cv_inttime.value > 0)
P_EndingMusic(player);
// SRB2kart 120217
//if (!exitcountdown)
//exitcountdown = racecountdown + 8*TICRATE;
if (P_CheckRacers())
player->exiting = raceexittime+1;
}
@ -1735,24 +1739,44 @@ void P_DoPlayerExit(player_t *player)
else
player->exiting = raceexittime+2; // Accidental death safeguard???
//player->pflags &= ~PF_GLIDING;
/* // SRB2kart - don't need
if (player->climbing)
if (grandprixinfo.gp == true)
{
player->climbing = 0;
player->pflags |= PF_JUMPED;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
if (player->bot)
{
// Bots are going to get harder... :)
K_IncreaseBotDifficulty(player);
}
else if (!losing)
{
const UINT8 lifethreshold = 20;
UINT8 extra = 0;
// YOU WIN
grandprixinfo.wonround = true;
// Increase your total rings
if (RINGTOTAL(player) > 0)
{
player->totalring += RINGTOTAL(player);
extra = player->totalring / lifethreshold;
if (extra > player->xtralife)
{
P_GivePlayerLives(player, extra - player->xtralife);
S_StartSound(NULL, sfx_cdfm73);
player->xtralife = extra;
}
}
}
}
*/
player->powers[pw_underwater] = 0;
player->powers[pw_spacetime] = 0;
player->karthud[khud_cardanimation] = 0; // srb2kart: reset battle animation
if (player == &players[consoleplayer])
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
@ -4162,23 +4186,21 @@ static void P_3dMovement(player_t *player)
const fixed_t airspeedcap = (50*mapobjectscale);
const fixed_t speed = R_PointToDist2(0, 0, player->mo->momx, player->mo->momy);
// If you're going too fast in the air, ease back down to a certain speed.
// Helps lots of jumps from breaking when using speed items, since you can't move in the air.
if (speed > airspeedcap)
{
fixed_t div = 32*FRACUNIT;
fixed_t newspeed;
// Make rubberbanding bots slow down faster
if (K_PlayerUsesBotMovement(player))
{
fixed_t baserubberband = K_BotRubberband(player);
fixed_t rubberband = FixedMul(baserubberband,
FixedMul(baserubberband,
FixedMul(baserubberband,
baserubberband
))); // This looks extremely goofy, but we need this really high, but at the same time, proportional.
fixed_t rubberband = K_BotRubberband(player) - FRACUNIT;
if (rubberband > FRACUNIT)
if (rubberband > 0)
{
div = FixedMul(div, rubberband);
div = FixedDiv(div, FRACUNIT + (rubberband * 2));
}
}
@ -6253,7 +6275,7 @@ static void P_MovePlayer(player_t *player)
////////////////////////////
// SRB2kart - Drifting smoke and fire
if ((EITHERSNEAKER(player) || player->kartstuff[k_flamedash])
if ((player->kartstuff[k_sneakertimer] || player->kartstuff[k_flamedash])
&& onground && (leveltime & 1))
K_SpawnBoostTrail(player);
@ -7096,14 +7118,9 @@ static void P_DeathThink(player_t *player)
K_KartPlayerHUDUpdate(player);
// Force respawn if idle for more than 30 seconds in shooter modes.
if (player->lives > 0 /*&& leveltime >= starttime*/) // *could* you respawn?
if (player->lives > 0 && !(player->pflags & PF_TIMEOVER) && player->deadtimer > TICRATE)
{
// SRB2kart - spawn automatically after 1 second
if (player->deadtimer > ((netgame || multiplayer)
? cv_respawntime.value*TICRATE
: TICRATE)) // don't let them change it in record attack
player->playerstate = PST_REBORN;
player->playerstate = PST_REBORN;
}
// Keep time rolling
@ -8219,8 +8236,8 @@ boolean P_SpectatorJoinGame(player_t *player)
player->playerstate = PST_REBORN;
//Reset away view
if (P_IsLocalPlayer(player) && g_localplayers[0] != consoleplayer)
g_localplayers[0] = consoleplayer;
if (P_IsLocalPlayer(player) && displayplayers[0] != consoleplayer)
displayplayers[0] = consoleplayer;
HU_AddChatText(va(M_GetText("\x82*%s entered the game."), player_names[player-players]), false);
return true; // no more player->mo, cannot continue.
@ -8357,13 +8374,28 @@ static void P_CalcPostImg(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]));
}
player->pflags |= PF_TIMEOVER;
if (P_IsLocalPlayer(player) && !demo.playback)
legitimateexit = true; // SRB2kart: losing a race is still seeing it through to the end :p
if (G_GametypeUsesLives())
{
K_PlayerLoseLife(player);
}
if (player->mo)
{
@ -8371,8 +8403,6 @@ void P_DoTimeOver(player_t *player)
P_DamageMobj(player->mo, NULL, NULL, 10000);
}
player->lives = 0;
P_EndingMusic(player);
if (!exitcountdown)
@ -8489,7 +8519,7 @@ void P_PlayerThink(player_t *player)
{
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;
}
}
@ -8497,24 +8527,26 @@ void P_PlayerThink(player_t *player)
if (i == MAXPLAYERS && player->exiting == raceexittime+2) // finished
player->exiting = raceexittime+1;
#if 0
// If 10 seconds are left on the timer,
// begin the drown music for countdown!
// SRB2Kart: despite how perfect this is, it's disabled FOR A REASON
/*if (racecountdown == 11*TICRATE - 1)
if (racecountdown == 11*TICRATE - 1)
{
if (P_IsLocalPlayer(player))
S_ChangeMusicInternal("drown", false);
}*/
}
#endif
// If you've hit the countdown and you haven't made
// 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);
if (player->playerstate == PST_DEAD)
{
return;
}
}
}
@ -8528,33 +8560,9 @@ void P_PlayerThink(player_t *player)
if (player->exiting == 2 || exitcountdown == 2)
{
if (cv_playersforexit.value) // Count to be sure everyone's exited
if (server)
{
INT32 i;
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);
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
}
}
@ -8592,17 +8600,6 @@ void P_PlayerThink(player_t *player)
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
if (leveltime < starttime)
{

View file

@ -2920,6 +2920,11 @@ static void Sk_SetDefaultValue(skin_t *skin)
strncpy(skin->facewant, "PLAYWANT", 9);
strncpy(skin->facemmap, "PLAYMMAP", 9);
for (i = 0; i < SKINRIVALS; i++)
{
strcpy(skin->rivals[i], "");
}
skin->starttranscolor = 96;
skin->prefcolor = SKINCOLOR_GREEN;
@ -3190,6 +3195,45 @@ void R_AddSkins(UINT16 wadnum)
strupr(value);
strncpy(skin->facemmap, value, sizeof skin->facemmap);
}
else if (!stricmp(stoken, "rivals"))
{
size_t len = strlen(value);
size_t i;
char rivalname[SKINNAMESIZE] = "";
UINT8 pos = 0;
UINT8 numrivals = 0;
// Can't use strtok, because this function's already using it.
// Using it causes it to upset the saved pointer,
// corrupting the reading for the rest of the file.
// So instead we get to crawl through the value, character by character,
// and write it down as we go, until we hit a comma or the end of the string.
// Yaaay.
for (i = 0; i <= len; i++)
{
if (numrivals >= SKINRIVALS)
{
break;
}
if (value[i] == ',' || i == len)
{
STRBUFCPY(skin->rivals[numrivals], rivalname);
strlwr(skin->rivals[numrivals]);
numrivals++;
memset(rivalname, 0, sizeof (rivalname));
pos = 0;
continue;
}
rivalname[pos] = value[i];
pos++;
}
}
#define FULLPROCESS(field) else if (!stricmp(stoken, #field)) skin->field = get_number(value);
// character type identification

View file

@ -67,6 +67,7 @@ void R_DrawMasked(void);
// SKINS STUFF
// -----------
#define SKINNAMESIZE 16
#define SKINRIVALS 3
// should be all lowercase!! S_SKIN processing does a strlwr
#define DEFAULTSKIN "sonic"
#define DEFAULTSKIN2 "tails" // secondary player
@ -95,6 +96,8 @@ typedef struct
UINT8 prefcolor;
fixed_t highresscale; // scale of highres, default is 0.5
char rivals[SKINRIVALS][SKINNAMESIZE+1]; // Your top 3 rivals for GP mode. Uses names so that you can reference skins that aren't added
// specific sounds per skin
sfxenum_t soundsid[NUMSKINSOUNDS]; // sound # in S_sfx table
} skin_t;

View file

@ -231,6 +231,7 @@
<ClInclude Include="..\hardware\hw_md3load.h" />
<ClInclude Include="..\hardware\hw_model.h" />
<ClInclude Include="..\hardware\u_list.h" />
<ClInclude Include="..\font.h" />
<ClInclude Include="..\hu_stuff.h" />
<ClInclude Include="..\info.h" />
<ClInclude Include="..\i_addrinfo.h" />
@ -376,6 +377,7 @@
<ClCompile Include="..\hardware\hw_trick.c" />
<ClCompile Include="..\hardware\r_opengl\r_opengl.c" />
<ClCompile Include="..\hardware\u_list.c" />
<ClCompile Include="..\font.c" />
<ClCompile Include="..\hu_stuff.c" />
<ClCompile Include="..\info.c" />
<ClCompile Include="..\i_addrinfo.c">

View file

@ -2090,6 +2090,50 @@
RelativePath="..\console.h"
>
</File>
<File
RelativePath="..\font.c"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
/>
</FileConfiguration>
</File>
<File
RelativePath="..\font.h"
>
</File>
<File
RelativePath="..\hu_stuff.c"
>

View file

@ -655,6 +655,16 @@
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ClCompile Include="..\font.c">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ClCompile Include="..\hu_stuff.c">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -1366,6 +1376,7 @@
<ClInclude Include="..\am_map.h" />
<ClInclude Include="..\command.h" />
<ClInclude Include="..\console.h" />
<ClInclude Include="..\font.h" />
<ClInclude Include="..\hu_stuff.h" />
<ClInclude Include="..\st_stuff.h" />
<ClInclude Include="..\y_inter.h" />

View file

@ -2090,6 +2090,50 @@
RelativePath="..\console.h"
>
</File>
<File
RelativePath="..\font.c"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
/>
</FileConfiguration>
</File>
<File
RelativePath="..\font.h"
>
</File>
<File
RelativePath="..\hu_stuff.c"
>

View file

@ -65,7 +65,6 @@ extern INT32 st_palette; // 0 is default, any others are special palettes.
extern lumpnum_t st_borderpatchnum;
// patches, also used in intermission
extern patch_t *tallnum[10];
extern patch_t *sboscore;
extern patch_t *sbotime;
extern patch_t *sbocolon;

File diff suppressed because it is too large Load diff

View file

@ -173,6 +173,12 @@ void V_DrawCustomFadeScreen(const char *lump, UINT8 strength);
void V_DrawFadeConsBack(INT32 plines);
void V_EncoreInvertScreen(void);
/* Convenience macros for leagacy string function macros. */
#define V__DrawOneScaleString( x,y,scale,option,font,string ) \
V_DrawStringScaled(x,y,scale,FRACUNIT,FRACUNIT,option,font,string)
#define V__DrawDupxString( x,y,scale,option,font,string )\
V__DrawOneScaleString ((x)<<FRACBITS,(y)<<FRACBITS,scale,option,font,string)
// draw a single character
void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed);
// draw a single character, but for the chat
@ -180,27 +186,47 @@ void V_DrawChatCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed, UI
UINT8 *V_GetStringColormap(INT32 colorflags);
void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string);
#define V_DrawLevelTitle( x,y,option,string ) \
V__DrawDupxString (x,y,FRACUNIT,option,LT_FONT,string)
// wordwrap a string using the hu_font
char *V_WordWrap(INT32 x, INT32 w, INT32 option, const char *string);
// draw a string using a font
void V_DrawStringScaled(
fixed_t x,
fixed_t y,
fixed_t scale,
fixed_t space_scale,
fixed_t linefeed_scale,
INT32 flags,
int font,
const char *text);
// draw a string using the hu_font
void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string);
void V_DrawKartString(INT32 x, INT32 y, INT32 option, const char *string); // SRB2kart
#define V_DrawString( x,y,option,string ) \
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)
void V_DrawCenteredString(INT32 x, INT32 y, INT32 option, const char *string);
void V_DrawRightAlignedString(INT32 x, INT32 y, INT32 option, const char *string);
// draw a string using the hu_font, 0.5x scale
void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string);
#define V_DrawSmallString( x,y,option,string ) \
V__DrawDupxString (x,y,FRACUNIT>>1,option,HU_FONT,string)
void V_DrawRightAlignedSmallString(INT32 x, INT32 y, INT32 option, const char *string);
// draw a string using the tny_font
void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string);
#define V_DrawThinString( x,y,option,string ) \
V__DrawDupxString (x,y,FRACUNIT,option,TINY_FONT,string)
void V_DrawCenteredThinString(INT32 x, INT32 y, INT32 option, const char *string);
void V_DrawRightAlignedThinString(INT32 x, INT32 y, INT32 option, const char *string);
void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string);
#define V_DrawStringAtFixed( x,y,option,string ) \
V__DrawOneScaleString (x,y,FRACUNIT,option,HU_FONT,string)
#define V_DrawThinStringAtFixed( x,y,option,string ) \
V__DrawOneScaleString (x,y,FRACUNIT,option,TINY_FONT,string)
// Draw tall nums, used for menu, HUD, intermission
void V_DrawTallNum(INT32 x, INT32 y, INT32 flags, INT32 num);
@ -214,7 +240,8 @@ void V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *colorm
INT32 V_LevelNameWidth(const char *string);
INT32 V_LevelNameHeight(const char *string);
void V_DrawCreditString(fixed_t x, fixed_t y, INT32 option, const char *string);
#define V_DrawCreditString( x,y,option,string ) \
V__DrawOneScaleString (x,y,FRACUNIT,option,CRED_FONT,string)
INT32 V_CreditStringWidth(const char *string);
// Find string width from hu_font chars

View file

@ -223,6 +223,20 @@
<ClCompile Include="..\f_wipe.c" />
<ClCompile Include="..\g_game.c" />
<ClCompile Include="..\g_input.c" />
<ClCompile Include="..\hardware\hw3sound.c" />
<ClCompile Include="..\hardware\hw_bsp.c" />
<ClCompile Include="..\hardware\hw_cache.c" />
<ClCompile Include="..\hardware\hw_clip.c" />
<ClCompile Include="..\hardware\hw_draw.c" />
<ClCompile Include="..\hardware\hw_light.c" />
<ClCompile Include="..\hardware\hw_main.c" />
<ClCompile Include="..\hardware\hw_md2.c" />
<ClCompile Include="..\hardware\hw_md2load.c" />
<ClCompile Include="..\hardware\hw_md3load.c" />
<ClCompile Include="..\hardware\hw_model.c" />
<ClCompile Include="..\hardware\hw_trick.c" />
<ClCompile Include="..\hardware\u_list.c" />
<ClCompile Include="..\font.c" />
<ClCompile Include="..\hu_stuff.c" />
<ClCompile Include="..\info.c" />
<ClCompile Include="..\i_addrinfo.c">
@ -376,6 +390,21 @@
<ClInclude Include="..\hardware\hw3dsdrv.h" />
<ClInclude Include="..\hardware\hw3sound.h" />
<ClInclude Include="..\hardware\hws_data.h" />
<ClInclude Include="..\hardware\hw_clip.h" />
<ClInclude Include="..\hardware\hw_data.h" />
<ClInclude Include="..\hardware\hw_defs.h" />
<ClInclude Include="..\hardware\hw_dll.h" />
<ClInclude Include="..\hardware\hw_drv.h" />
<ClInclude Include="..\hardware\hw_glide.h" />
<ClInclude Include="..\hardware\hw_glob.h" />
<ClInclude Include="..\hardware\hw_light.h" />
<ClInclude Include="..\hardware\hw_main.h" />
<ClInclude Include="..\hardware\hw_md2.h" />
<ClInclude Include="..\hardware\hw_md2load.h" />
<ClInclude Include="..\hardware\hw_md3load.h" />
<ClInclude Include="..\hardware\hw_model.h" />
<ClInclude Include="..\hardware\u_list.h" />
<ClInclude Include="..\font.h" />
<ClInclude Include="..\hu_stuff.h" />
<ClInclude Include="..\info.h" />
<ClInclude Include="..\i_addrinfo.h" />

View file

@ -1831,6 +1831,50 @@
RelativePath="..\console.h"
>
</File>
<File
RelativePath="..\font.c"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
/>
</FileConfiguration>
</File>
<File
RelativePath="..\font.h"
>
</File>
<File
RelativePath="..\hu_stuff.c"
>

View file

@ -43,6 +43,7 @@
#include "k_pwrlv.h"
#include "console.h" // cons_menuhighlight
#include "lua_hook.h" // IntermissionThinker hook
#include "k_grandprix.h"
#ifdef HWRENDER
#include "hardware/hw_main.h"
@ -198,7 +199,12 @@ static void Y_CompareScore(INT32 i)
static void Y_CompareRank(INT32 i)
{
INT16 increase = ((data.match.increase[i] == INT16_MIN) ? 0 : data.match.increase[i]);
UINT32 score = (powertype != -1 ? clientpowerlevels[i][powertype] : players[i].score);
UINT32 score = players[i].score;
if (powertype != PWRLV_DISABLED)
{
score = clientpowerlevels[i][powertype];
}
if (!(data.match.val[data.match.numplayers] == UINT32_MAX || (score - increase) > data.match.val[data.match.numplayers]))
return;
@ -302,18 +308,26 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
data.match.name[data.match.numplayers] = player_names[i];
if (data.match.numplayers && (data.match.val[data.match.numplayers] == data.match.val[data.match.numplayers-1]))
data.match.pos[data.match.numplayers] = data.match.pos[data.match.numplayers-1];
else
data.match.pos[data.match.numplayers] = data.match.numplayers+1;
if ((!rankingsmode && powertype == -1) // Single player rankings (grand prix). Online rank is handled below.
&& !(players[i].pflags & PF_TIMEOVER) && (data.match.pos[data.match.numplayers] < (numplayersingame + numgriefers)))
{
data.match.increase[i] = (numplayersingame + numgriefers) - data.match.pos[data.match.numplayers];
data.match.pos[data.match.numplayers] = data.match.pos[data.match.numplayers-1];
}
else
{
data.match.pos[data.match.numplayers] = data.match.numplayers+1;
}
if ((powertype == PWRLV_DISABLED)
&& (!rankingsmode)
&& !(players[i].pflags & PF_TIMEOVER)
&& (data.match.pos[data.match.numplayers] < (numplayersingame + numgriefers)))
{
// Online rank is handled further below in this file.
data.match.increase[i] = K_CalculateGPRankPoints(data.match.pos[data.match.numplayers], numplayersingame + numgriefers);
players[i].score += data.match.increase[i];
}
if (demo.recording && !rankingsmode)
{
G_WriteStanding(
data.match.pos[data.match.numplayers],
data.match.name[data.match.numplayers],
@ -321,6 +335,7 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
*data.match.color[data.match.numplayers],
data.match.val[data.match.numplayers]
);
}
data.match.numplayers++;
}
@ -440,9 +455,36 @@ void Y_IntermissionDrawer(void)
int y2;
if (data.match.rankingsmode)
timeheader = (powertype != -1 ? "PWR.LV" : "RANK");
{
if (powertype == PWRLV_DISABLED)
{
timeheader = "RANK";
}
else
{
timeheader = "PWR.LV";
}
}
else
timeheader = ((intertype == int_race || (intertype == int_match && battlecapsules)) ? "TIME" : "SCORE");
{
switch (intertype)
{
default:
case int_race:
timeheader = "TIME";
break;
case int_match:
if (battlecapsules)
{
timeheader = "TIME";
}
else
{
timeheader = "SCORE";
}
break;
}
}
// draw the level name
V_DrawCenteredString(-4 + x + BASEVIDWIDTH/2, 12, 0, data.match.levelstring);
@ -533,8 +575,11 @@ void Y_IntermissionDrawer(void)
if (data.match.rankingsmode)
{
if (powertype != -1 && !clientpowerlevels[data.match.num[i]][powertype]) // No power level (splitscreen guests)
if (powertype != PWRLV_DISABLED && !clientpowerlevels[data.match.num[i]][powertype])
{
// No power level (splitscreen guests)
STRBUFCPY(strtime, "----");
}
else
{
if (data.match.increase[data.match.num[i]] != INT16_MIN)
@ -600,7 +645,7 @@ void Y_IntermissionDrawer(void)
}
dotimer:
if (timer)
if (timer && grandprixinfo.gp == false)
{
char *string;
INT32 tickdown = (timer+1)/TICRATE;
@ -705,74 +750,73 @@ void Y_Ticker(void)
if (intertype == int_race || intertype == int_match)
{
if (netgame || multiplayer)
if (!(multiplayer && demo.playback)) // Don't advance to rankings in replays
{
if (sorttic == -1)
sorttic = intertic + max((cv_inttime.value/2)-2, 2)*TICRATE; // 8 second pause after match results
else if (!(multiplayer && demo.playback)) // Don't advance to rankings in replays
if (!data.match.rankingsmode && (intertic >= sorttic + 8))
{
if (!data.match.rankingsmode && (intertic >= sorttic + 8))
Y_CalculateMatchData(1, Y_CompareRank);
Y_CalculateMatchData(1, Y_CompareRank);
}
if (data.match.rankingsmode && intertic > sorttic+16+(2*TICRATE))
if (data.match.rankingsmode && intertic > sorttic+16+(2*TICRATE))
{
INT32 q=0,r=0;
boolean kaching = true;
for (q = 0; q < data.match.numplayers; q++)
{
INT32 q=0,r=0;
boolean kaching = true;
for (q = 0; q < data.match.numplayers; q++)
{
if (data.match.num[q] == MAXPLAYERS
if (data.match.num[q] == MAXPLAYERS
|| !data.match.increase[data.match.num[q]]
|| data.match.increase[data.match.num[q]] == INT16_MIN)
continue;
{
continue;
}
r++;
data.match.jitter[data.match.num[q]] = 1;
r++;
data.match.jitter[data.match.num[q]] = 1;
if (powertype != -1)
if (powertype != PWRLV_DISABLED)
{
// Power Levels
if (abs(data.match.increase[data.match.num[q]]) < 10)
{
// Power Levels
if (abs(data.match.increase[data.match.num[q]]) < 10)
{
// Not a lot of point increase left, just set to 0 instantly
data.match.increase[data.match.num[q]] = 0;
}
else
{
SINT8 remove = 0; // default (should not happen)
if (data.match.increase[data.match.num[q]] < 0)
remove = -10;
else if (data.match.increase[data.match.num[q]] > 0)
remove = 10;
// Remove 10 points at a time
data.match.increase[data.match.num[q]] -= remove;
// Still not zero, no kaching yet
if (data.match.increase[data.match.num[q]] != 0)
kaching = false;
}
// Not a lot of point increase left, just set to 0 instantly
data.match.increase[data.match.num[q]] = 0;
}
else
{
// Basic bitch points
if (data.match.increase[data.match.num[q]])
{
if (--data.match.increase[data.match.num[q]])
kaching = false;
}
SINT8 remove = 0; // default (should not happen)
if (data.match.increase[data.match.num[q]] < 0)
remove = -10;
else if (data.match.increase[data.match.num[q]] > 0)
remove = 10;
// Remove 10 points at a time
data.match.increase[data.match.num[q]] -= remove;
// Still not zero, no kaching yet
if (data.match.increase[data.match.num[q]] != 0)
kaching = false;
}
}
if (r)
{
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
{
// Basic bitch points
if (data.match.increase[data.match.num[q]])
{
if (--data.match.increase[data.match.num[q]])
kaching = false;
}
}
}
if (r)
{
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
@ -1047,19 +1091,11 @@ void Y_StartIntermission(void)
#endif
// set player Power Level type
powertype = PWRLV_DISABLED;
if (netgame && cv_kartusepwrlv.value)
{
if (G_RaceGametype())
powertype = PWRLV_RACE;
else if (G_BattleGametype())
powertype = PWRLV_BATTLE;
}
powertype = K_UsingPowerLevels();
if (!multiplayer)
{
timer = 0;
timer = 20*TICRATE;
if (!majormods && !multiplayer && !demo.playback) // move this once we have a proper time attack screen
{
@ -1078,7 +1114,7 @@ void Y_StartIntermission(void)
}
else
{
if (cv_inttime.value == 0 && gametype == GT_COOP)
if (cv_inttime.value == 0)
timer = 0;
else if (demo.playback) // Override inttime (which is pulled from the replay anyway
timer = 10*TICRATE;
@ -1091,6 +1127,8 @@ void Y_StartIntermission(void)
}
}
sorttic = max((timer/2) - 2*TICRATE, 2*TICRATE); // 8 second pause after match results
if (gametype == GT_MATCH)
intertype = int_match;
else //if (gametype == GT_RACE)
@ -1136,7 +1174,9 @@ void Y_StartIntermission(void)
}
if (powertype != PWRLV_DISABLED)
{
K_UpdatePowerLevels();
}
//if (intertype == int_race || intertype == int_match)
{