Merge branch 'v2-master' into followme-internal

This commit is contained in:
Latapostrophe 2020-06-07 23:56:32 +02:00
commit cab9c2a139
51 changed files with 3470 additions and 1592 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;
@ -657,6 +659,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;
@ -714,6 +718,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;
@ -795,6 +800,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;
@ -5267,20 +5274,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)
@ -5317,11 +5333,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;
@ -5342,6 +5377,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;
@ -295,6 +296,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
@ -717,8 +718,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);
@ -2873,6 +2872,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;
@ -3021,6 +3026,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
@ -3051,6 +3057,7 @@ static void Command_Map_f(void)
if (COM_CheckParm("-force"))
{
G_SetGameModified(false, true);
startgp = true;
}
else
{
@ -3097,7 +3104,6 @@ static void Command_Map_f(void)
// new encoremode value
// use cvar by default
newencoremode = (cv_kartencore.value == 1);
if (COM_CheckParm("-encore"))
@ -3144,6 +3150,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);
}
@ -5167,14 +5236,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);
@ -5259,11 +5330,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:
@ -5274,8 +5340,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:
@ -5286,8 +5350,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
@ -5296,17 +5358,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
@ -5817,14 +5874,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);
@ -6501,24 +6558,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"));
}
}
@ -6531,47 +6599,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

@ -65,10 +65,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

@ -316,8 +316,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
@ -391,9 +391,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])
@ -434,12 +431,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;
// ========================================================================
@ -525,6 +524,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

@ -1521,6 +1521,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);
@ -3669,6 +3776,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)
@ -8691,7 +8834,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
@ -2352,17 +2357,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 +2401,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 +2573,7 @@ void G_PlayerReborn(INT32 player)
player_t *p;
INT32 score, marescore;
INT32 lives;
boolean lostlife;
INT32 continues;
// SRB2kart
UINT8 kartspeed;
@ -2589,7 +2599,10 @@ void G_PlayerReborn(INT32 player)
boolean spectator;
boolean bot;
UINT8 botdifficulty;
UINT8 botdiffincrease;
boolean botrival;
SINT8 pity;
SINT8 xtralife;
// SRB2kart
respawnvars_t respawn;
@ -2607,6 +2620,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;
@ -2640,7 +2654,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)
@ -2691,6 +2708,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;
@ -2717,7 +2735,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;
@ -3218,6 +3239,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;
@ -3293,18 +3355,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
}
//
@ -3640,8 +3699,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
@ -3658,10 +3716,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
}
@ -3681,11 +3750,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))
@ -3695,8 +3786,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);
@ -3740,44 +3830,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.
@ -3787,14 +3851,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:
@ -3854,7 +3910,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++)
@ -3951,7 +4007,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);
@ -4520,7 +4576,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)
@ -4540,23 +4596,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);
@ -4585,63 +4638,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....
@ -5156,7 +5186,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)
@ -8152,7 +8182,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;
@ -761,168 +740,27 @@ 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;
// 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;
}
cstart = "\x80" + (K_SkincolorToTextColor(players[playernum].skincolor) >> V_CHARCOLORSHIFT);
}
prefix = cstart;
@ -932,6 +770,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 +838,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 +1272,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

@ -252,6 +252,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"))
@ -503,6 +505,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
@ -227,7 +228,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);
@ -240,6 +242,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;
@ -463,6 +466,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.
// ==========================================================================
@ -802,30 +812,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[] =
@ -1810,6 +1820,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",
@ -3376,6 +3388,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)");
@ -4221,6 +4237,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)
{
@ -6666,6 +6711,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 =
@ -7550,6 +7597,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

@ -8369,10 +8369,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];
}
}
@ -11428,9 +11427,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};
@ -11701,10 +11697,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

@ -141,6 +141,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);
@ -291,6 +292,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);
@ -346,6 +349,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
@ -483,6 +487,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;
@ -2467,8 +2466,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;
@ -2493,11 +2507,6 @@ static void P_LevelInitStuff(void)
memset(&battleovertime, 0, sizeof(struct battleovertime));
speedscramble = encorescramble = -1;
if (!modeattacking)
{
K_UpdateMatchRaceBots();
}
}
//
@ -2682,12 +2691,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;
}
}
}
@ -3384,6 +3387,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
@ -3435,9 +3460,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

@ -4052,52 +4052,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->kartstuff[k_pogospring] = 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
@ -49,6 +48,8 @@
#include "k_color.h" // KartColor_Opposite
#include "console.h" // CON_LogMessage
#include "k_respawn.h"
#include "k_bot.h"
#include "k_grandprix.h"
#ifdef HW3SOUND
#include "hardware/hw3sound.h"
@ -950,7 +951,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!
@ -968,8 +968,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;
}
@ -1687,12 +1687,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;
@ -1703,7 +1711,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];
@ -1711,7 +1719,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);
@ -1721,10 +1729,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;
}
@ -1736,24 +1740,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
@ -4165,23 +4189,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));
}
}
@ -6252,7 +6274,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);
@ -7095,14 +7117,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
@ -8356,13 +8373,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)
{
@ -8370,8 +8402,6 @@ void P_DoTimeOver(player_t *player)
P_DamageMobj(player->mo, NULL, NULL, 10000);
}
player->lives = 0;
P_EndingMusic(player);
if (!exitcountdown)
@ -8770,7 +8800,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;
}
}
@ -8778,24 +8808,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;
}
}
}
@ -8809,33 +8841,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);
}
}
}
@ -8873,17 +8881,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

@ -2925,6 +2925,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;
@ -3280,6 +3285,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)
{