mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-05-10 19:01:50 +00:00
Merge branch 'grand-pricks' into 'master'
Grand pricks See merge request KartKrew/Kart!258
This commit is contained in:
commit
58dbdc2378
32 changed files with 2288 additions and 540 deletions
|
|
@ -167,6 +167,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 +189,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"))
|
||||
|
|
|
|||
|
|
@ -566,6 +566,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) \
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
65
src/d_main.c
65
src/d_main.c
|
|
@ -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)
|
||||
|
|
|
|||
206
src/d_netcmd.c
206
src/d_netcmd.c
|
|
@ -52,6 +52,7 @@
|
|||
#include "y_inter.h"
|
||||
#include "k_color.h"
|
||||
#include "k_respawn.h"
|
||||
#include "k_grandprix.h"
|
||||
|
||||
#ifdef NETGAME_DEVMODE
|
||||
#define CV_RESTRICT CV_NETVAR
|
||||
|
|
@ -690,8 +691,6 @@ void D_RegisterServerCommands(void)
|
|||
AddMServCommands();
|
||||
|
||||
// p_mobj.c
|
||||
CV_RegisterVar(&cv_itemrespawntime);
|
||||
CV_RegisterVar(&cv_itemrespawn);
|
||||
CV_RegisterVar(&cv_flagtime);
|
||||
CV_RegisterVar(&cv_suddendeath);
|
||||
|
||||
|
|
@ -2770,6 +2769,12 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r
|
|||
if (netgame || multiplayer)
|
||||
FLS = false;
|
||||
|
||||
if (grandprixinfo.gp == true)
|
||||
{
|
||||
// Too lazy to change the input value for every instance of this function.......
|
||||
pencoremode = grandprixinfo.encore;
|
||||
}
|
||||
|
||||
if (delay != 2)
|
||||
{
|
||||
UINT8 flags = 0;
|
||||
|
|
@ -2917,6 +2922,7 @@ static void Command_Map_f(void)
|
|||
INT32 newmapnum;
|
||||
boolean newresetplayers, newencoremode;
|
||||
INT32 newgametype = gametype;
|
||||
boolean startgp = false;
|
||||
|
||||
// max length of command: map map03 -gametype race -noresetplayers -force -encore
|
||||
// 1 2 3 4 5 6 7
|
||||
|
|
@ -2947,6 +2953,7 @@ static void Command_Map_f(void)
|
|||
if (COM_CheckParm("-force"))
|
||||
{
|
||||
G_SetGameModified(false, true);
|
||||
startgp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -2993,7 +3000,6 @@ static void Command_Map_f(void)
|
|||
|
||||
// new encoremode value
|
||||
// use cvar by default
|
||||
|
||||
newencoremode = (cv_kartencore.value == 1);
|
||||
|
||||
if (COM_CheckParm("-encore"))
|
||||
|
|
@ -3040,6 +3046,69 @@ static void Command_Map_f(void)
|
|||
return;
|
||||
}
|
||||
|
||||
if (startgp)
|
||||
{
|
||||
i = COM_CheckParm("-skill");
|
||||
|
||||
grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value);
|
||||
grandprixinfo.masterbots = false;
|
||||
|
||||
if (i)
|
||||
{
|
||||
const UINT8 master = KARTSPEED_HARD+1;
|
||||
const char *masterstr = "Master";
|
||||
const char *skillname = COM_Argv(i+1);
|
||||
INT32 newskill = -1;
|
||||
INT32 j;
|
||||
|
||||
if (!strcasecmp(masterstr, skillname))
|
||||
{
|
||||
newskill = master;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (j = 0; kartspeed_cons_t[j].strvalue; j++)
|
||||
{
|
||||
if (!strcasecmp(kartspeed_cons_t[j].strvalue, skillname))
|
||||
{
|
||||
newskill = (INT16)kartspeed_cons_t[j].value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!kartspeed_cons_t[j].strvalue) // reached end of the list with no match
|
||||
{
|
||||
j = atoi(COM_Argv(i+1)); // assume they gave us a skill number, which is okay too
|
||||
if (j >= KARTSPEED_EASY && j <= master)
|
||||
newskill = (INT16)j;
|
||||
}
|
||||
}
|
||||
|
||||
if (newskill != -1)
|
||||
{
|
||||
if (newskill == master)
|
||||
{
|
||||
grandprixinfo.gamespeed = KARTSPEED_HARD;
|
||||
grandprixinfo.masterbots = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
grandprixinfo.gamespeed = newskill;
|
||||
grandprixinfo.masterbots = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grandprixinfo.encore = newencoremode;
|
||||
|
||||
grandprixinfo.gp = true;
|
||||
grandprixinfo.roundnum = 0;
|
||||
grandprixinfo.cup = NULL;
|
||||
grandprixinfo.wonround = false;
|
||||
|
||||
grandprixinfo.initalize = true;
|
||||
}
|
||||
|
||||
fromlevelselect = false;
|
||||
D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, false);
|
||||
}
|
||||
|
|
@ -5063,14 +5132,16 @@ static void PointLimit_OnChange(void)
|
|||
|
||||
static void NumLaps_OnChange(void)
|
||||
{
|
||||
if (!G_RaceGametype() || (modeattacking || demo.playback))
|
||||
if (K_CanChangeRules() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (server && Playing()
|
||||
&& (netgame || multiplayer)
|
||||
&& (mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE)
|
||||
&& (cv_numlaps.value > mapheaderinfo[gamemap - 1]->numlaps))
|
||||
if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE)
|
||||
&& (cv_numlaps.value > mapheaderinfo[gamemap - 1]->numlaps))
|
||||
{
|
||||
CV_StealthSetValue(&cv_numlaps, mapheaderinfo[gamemap - 1]->numlaps);
|
||||
}
|
||||
|
||||
// Just don't be verbose
|
||||
CONS_Printf(M_GetText("Number of laps set to %d\n"), cv_numlaps.value);
|
||||
|
|
@ -5155,11 +5226,6 @@ void D_GameTypeChanged(INT32 lastgametype)
|
|||
// There will always be a server, and this only needs to be done once.
|
||||
if (server && (multiplayer || netgame))
|
||||
{
|
||||
if (gametype == GT_COMPETITION || gametype == GT_COOP)
|
||||
CV_SetValue(&cv_itemrespawn, 0);
|
||||
else if (!cv_itemrespawn.changed)
|
||||
CV_SetValue(&cv_itemrespawn, 1);
|
||||
|
||||
switch (gametype)
|
||||
{
|
||||
case GT_MATCH:
|
||||
|
|
@ -5170,8 +5236,6 @@ void D_GameTypeChanged(INT32 lastgametype)
|
|||
CV_SetValue(&cv_pointlimit, 0);
|
||||
CV_SetValue(&cv_timelimit, 120);
|
||||
}
|
||||
if (!cv_itemrespawntime.changed)
|
||||
CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally
|
||||
break;
|
||||
case GT_TAG:
|
||||
case GT_HIDEANDSEEK:
|
||||
|
|
@ -5182,8 +5246,6 @@ void D_GameTypeChanged(INT32 lastgametype)
|
|||
CV_SetValue(&cv_timelimit, 5);
|
||||
CV_SetValue(&cv_pointlimit, 0);
|
||||
}
|
||||
if (!cv_itemrespawntime.changed)
|
||||
CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally
|
||||
break;
|
||||
case GT_CTF:
|
||||
if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits
|
||||
|
|
@ -5192,17 +5254,12 @@ void D_GameTypeChanged(INT32 lastgametype)
|
|||
CV_SetValue(&cv_timelimit, 0);
|
||||
CV_SetValue(&cv_pointlimit, 5);
|
||||
}
|
||||
if (!cv_itemrespawntime.changed)
|
||||
CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!multiplayer && !netgame)
|
||||
{
|
||||
gametype = GT_RACE; // SRB2kart
|
||||
// These shouldn't matter anymore
|
||||
//CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue);
|
||||
//CV_SetValue(&cv_itemrespawn, 0);
|
||||
gametype = GT_RACE;
|
||||
}
|
||||
|
||||
// reset timelimit and pointlimit in race/coop, prevent stupid cheats
|
||||
|
|
@ -5713,14 +5770,14 @@ void Command_ExitGame_f(void)
|
|||
|
||||
void Command_Retry_f(void)
|
||||
{
|
||||
if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING))
|
||||
if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION))
|
||||
{
|
||||
CONS_Printf(M_GetText("You must be in a level to use this.\n"));
|
||||
else if (netgame || multiplayer)
|
||||
CONS_Printf(M_GetText("This only works in single player.\n"));
|
||||
/*else if (!&players[consoleplayer] || players[consoleplayer].lives <= 1)
|
||||
CONS_Printf(M_GetText("You can't retry without any lives remaining!\n"));
|
||||
else if (G_IsSpecialStage(gamemap))
|
||||
CONS_Printf(M_GetText("You can't retry special stages!\n"));*/
|
||||
}
|
||||
else if (grandprixinfo.gp == false)
|
||||
{
|
||||
CONS_Printf(M_GetText("This only works in Grand Prix.\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
M_ClearMenus(true);
|
||||
|
|
@ -6211,24 +6268,35 @@ static void Command_ShowTime_f(void)
|
|||
// SRB2Kart: On change messages
|
||||
static void BaseNumLaps_OnChange(void)
|
||||
{
|
||||
if (gamestate == GS_LEVEL)
|
||||
if (K_CanChangeRules() == true)
|
||||
{
|
||||
if (cv_basenumlaps.value)
|
||||
CONS_Printf(M_GetText("Number of laps will be changed to %d next round.\n"), cv_basenumlaps.value);
|
||||
else
|
||||
CONS_Printf(M_GetText("Number of laps will be changed to map defaults next round.\n"));
|
||||
const char *str = va("%d", cv_basenumlaps.value);
|
||||
|
||||
if (cv_basenumlaps.value == 0)
|
||||
{
|
||||
str = "map defaults";
|
||||
}
|
||||
|
||||
CONS_Printf(M_GetText("Number of laps will be changed to %s next round.\n"), str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void KartFrantic_OnChange(void)
|
||||
{
|
||||
if ((boolean)cv_kartfrantic.value != franticitems && gamestate == GS_LEVEL && leveltime > starttime)
|
||||
CONS_Printf(M_GetText("Frantic items will be turned %s next round.\n"), cv_kartfrantic.value ? M_GetText("on") : M_GetText("off"));
|
||||
if (K_CanChangeRules() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (leveltime < starttime)
|
||||
{
|
||||
CONS_Printf(M_GetText("Frantic items has been set to %s.\n"), cv_kartfrantic.value ? M_GetText("on") : M_GetText("off"));
|
||||
franticitems = (boolean)cv_kartfrantic.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONS_Printf(M_GetText("Frantic items has been turned %s.\n"), cv_kartfrantic.value ? M_GetText("on") : M_GetText("off"));
|
||||
franticitems = (boolean)cv_kartfrantic.value;
|
||||
CONS_Printf(M_GetText("Frantic items will be turned %s next round.\n"), cv_kartfrantic.value ? M_GetText("on") : M_GetText("off"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -6241,47 +6309,59 @@ static void KartSpeed_OnChange(void)
|
|||
return;
|
||||
}
|
||||
|
||||
if (G_RaceGametype())
|
||||
if (K_CanChangeRules() == false)
|
||||
{
|
||||
if ((gamestate == GS_LEVEL && leveltime < starttime) && (cv_kartspeed.value != KARTSPEED_AUTO))
|
||||
{
|
||||
CONS_Printf(M_GetText("Game speed has been changed to \"%s\".\n"), cv_kartspeed.string);
|
||||
gamespeed = (UINT8)cv_kartspeed.value;
|
||||
}
|
||||
else if (cv_kartspeed.value != (signed)gamespeed)
|
||||
{
|
||||
CONS_Printf(M_GetText("Game speed will be changed to \"%s\" next round.\n"), cv_kartspeed.string);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (leveltime < starttime && cv_kartspeed.value != KARTSPEED_AUTO)
|
||||
{
|
||||
CONS_Printf(M_GetText("Game speed has been changed to \"%s\".\n"), cv_kartspeed.string);
|
||||
gamespeed = (UINT8)cv_kartspeed.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONS_Printf(M_GetText("Game speed will be changed to \"%s\" next round.\n"), cv_kartspeed.string);
|
||||
}
|
||||
}
|
||||
|
||||
static void KartEncore_OnChange(void)
|
||||
{
|
||||
if (G_RaceGametype())
|
||||
if (K_CanChangeRules() == false)
|
||||
{
|
||||
if ((cv_kartencore.value == 1) != encoremode && gamestate == GS_LEVEL /*&& leveltime > starttime*/)
|
||||
CONS_Printf(M_GetText("Encore Mode will be set to %s next round.\n"), cv_kartencore.string);
|
||||
else
|
||||
CONS_Printf(M_GetText("Encore Mode has been set to %s.\n"), cv_kartencore.string);
|
||||
return;
|
||||
}
|
||||
|
||||
CONS_Printf(M_GetText("Encore Mode will be set to %s next round.\n"), cv_kartencore.string);
|
||||
}
|
||||
|
||||
static void KartComeback_OnChange(void)
|
||||
{
|
||||
if (G_BattleGametype())
|
||||
if (K_CanChangeRules() == false)
|
||||
{
|
||||
if ((boolean)cv_kartcomeback.value != comeback && gamestate == GS_LEVEL && leveltime > starttime)
|
||||
CONS_Printf(M_GetText("Karma Comeback will be turned %s next round.\n"), cv_kartcomeback.value ? M_GetText("on") : M_GetText("off"));
|
||||
else
|
||||
{
|
||||
CONS_Printf(M_GetText("Karma Comeback has been turned %s.\n"), cv_kartcomeback.value ? M_GetText("on") : M_GetText("off"));
|
||||
comeback = (boolean)cv_kartcomeback.value;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (leveltime < starttime)
|
||||
{
|
||||
CONS_Printf(M_GetText("Karma Comeback has been turned %s.\n"), cv_kartcomeback.value ? M_GetText("on") : M_GetText("off"));
|
||||
comeback = (boolean)cv_kartcomeback.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONS_Printf(M_GetText("Karma Comeback will be turned %s next round.\n"), cv_kartcomeback.value ? M_GetText("on") : M_GetText("off"));
|
||||
}
|
||||
}
|
||||
|
||||
static void KartEliminateLast_OnChange(void)
|
||||
{
|
||||
if (G_RaceGametype() && cv_karteliminatelast.value)
|
||||
if (K_CanChangeRules() == false)
|
||||
{
|
||||
CV_StealthSet(&cv_karteliminatelast, cv_karteliminatelast.defaultvalue);
|
||||
}
|
||||
|
||||
if (G_RaceGametype())
|
||||
{
|
||||
P_CheckRacers();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,10 +60,6 @@ extern consvar_t cv_usemouse2;
|
|||
extern consvar_t cv_mouse2opt;
|
||||
#endif
|
||||
|
||||
// normally in p_mobj but the .h is not read
|
||||
extern consvar_t cv_itemrespawntime;
|
||||
extern consvar_t cv_itemrespawn;
|
||||
|
||||
extern consvar_t cv_flagtime;
|
||||
extern consvar_t cv_suddendeath;
|
||||
|
||||
|
|
|
|||
|
|
@ -431,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;
|
||||
|
||||
// ========================================================================
|
||||
|
|
@ -516,6 +518,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
|
||||
|
|
|
|||
143
src/dehacked.c
143
src/dehacked.c
|
|
@ -1255,6 +1255,113 @@ static void readlevelheader(MYFILE *f, INT32 num)
|
|||
Z_Free(s);
|
||||
}
|
||||
|
||||
static void readcupheader(MYFILE *f, cupheader_t *cup)
|
||||
{
|
||||
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
||||
char *word;
|
||||
char *word2;
|
||||
char *tmp;
|
||||
INT32 i;
|
||||
|
||||
do
|
||||
{
|
||||
if (myfgets(s, MAXLINELEN, f))
|
||||
{
|
||||
if (s[0] == '\n')
|
||||
break;
|
||||
|
||||
// First remove trailing newline, if there is one
|
||||
tmp = strchr(s, '\n');
|
||||
if (tmp)
|
||||
*tmp = '\0';
|
||||
|
||||
tmp = strchr(s, '#');
|
||||
if (tmp)
|
||||
*tmp = '\0';
|
||||
if (s == tmp)
|
||||
continue; // Skip comment lines, but don't break.
|
||||
|
||||
// Set / reset word, because some things (Lua.) move it
|
||||
word = s;
|
||||
|
||||
// Get the part before the " = "
|
||||
tmp = strchr(s, '=');
|
||||
if (tmp)
|
||||
*(tmp-1) = '\0';
|
||||
else
|
||||
break;
|
||||
strupr(word);
|
||||
|
||||
// Now get the part after
|
||||
word2 = tmp += 2;
|
||||
i = atoi(word2); // used for numerical settings
|
||||
strupr(word2);
|
||||
|
||||
if (fastcmp(word, "ICON"))
|
||||
{
|
||||
deh_strlcpy(cup->icon, word2,
|
||||
sizeof(cup->icon), va("%s Cup: icon", cup->name));
|
||||
}
|
||||
else if (fastcmp(word, "LEVELLIST"))
|
||||
{
|
||||
cup->numlevels = 0;
|
||||
|
||||
tmp = strtok(word2,",");
|
||||
do {
|
||||
INT32 map = atoi(tmp);
|
||||
|
||||
if (tmp[0] >= 'A' && tmp[0] <= 'Z' && tmp[2] == '\0')
|
||||
map = M_MapNumber(tmp[0], tmp[1]);
|
||||
|
||||
if (!map)
|
||||
break;
|
||||
|
||||
if (cup->numlevels >= MAXLEVELLIST)
|
||||
{
|
||||
deh_warning("%s Cup: reached max levellist (%d)\n", cup->name, MAXLEVELLIST);
|
||||
break;
|
||||
}
|
||||
|
||||
cup->levellist[cup->numlevels] = map - 1;
|
||||
cup->numlevels++;
|
||||
} while((tmp = strtok(NULL,",")) != NULL);
|
||||
}
|
||||
else if (fastcmp(word, "BONUSGAME"))
|
||||
{
|
||||
// Convert to map number
|
||||
if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0')
|
||||
i = M_MapNumber(word2[0], word2[1]);
|
||||
cup->bonusgame = (INT16)i - 1;
|
||||
}
|
||||
else if (fastcmp(word, "SPECIALSTAGE"))
|
||||
{
|
||||
// Convert to map number
|
||||
if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0')
|
||||
i = M_MapNumber(word2[0], word2[1]);
|
||||
cup->specialstage = (INT16)i - 1;
|
||||
}
|
||||
else if (fastcmp(word, "EMERALDNUM"))
|
||||
{
|
||||
if (i >= 0 && i <= 14)
|
||||
cup->emeraldnum = (UINT8)i;
|
||||
else
|
||||
deh_warning("%s Cup: invalid emerald number %d", cup->name, i);
|
||||
}
|
||||
else if (fastcmp(word, "UNLOCKABLE"))
|
||||
{
|
||||
if (i >= 0 && i <= MAXUNLOCKABLES) // 0 for no unlock required, anything else requires something
|
||||
cup->unlockrequired = (SINT8)i - 1;
|
||||
else
|
||||
deh_warning("%s Cup: invalid unlockable number %d", cup->name, i);
|
||||
}
|
||||
else
|
||||
deh_warning("%s Cup: unknown word '%s'", cup->name, word);
|
||||
}
|
||||
} while (!myfeof(f)); // finish when the line is empty
|
||||
|
||||
Z_Free(s);
|
||||
}
|
||||
|
||||
static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum)
|
||||
{
|
||||
char *s = Z_Calloc(MAXLINELEN, PU_STATIC, NULL);
|
||||
|
|
@ -3396,6 +3503,42 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
|
|||
}
|
||||
DEH_WriteUndoline(word, word2, UNDO_HEADER);
|
||||
}
|
||||
else if (fastcmp(word, "CUP"))
|
||||
{
|
||||
cupheader_t *cup = kartcupheaders;
|
||||
cupheader_t *prev = NULL;
|
||||
|
||||
while (cup)
|
||||
{
|
||||
if (fastcmp(cup->name, word2))
|
||||
{
|
||||
// mark as a major mod if it replaces an already-existing cup
|
||||
G_SetGameModified(multiplayer, true);
|
||||
break;
|
||||
}
|
||||
|
||||
prev = cup;
|
||||
cup = cup->next;
|
||||
}
|
||||
|
||||
// Nothing found, add to the end.
|
||||
if (!cup)
|
||||
{
|
||||
cup = Z_Calloc(sizeof (cupheader_t), PU_STATIC, NULL);
|
||||
cup->id = numkartcupheaders;
|
||||
deh_strlcpy(cup->name, word2,
|
||||
sizeof(cup->name), va("Cup header %s: name", word2));
|
||||
if (prev != NULL)
|
||||
prev->next = cup;
|
||||
if (kartcupheaders == NULL)
|
||||
kartcupheaders = cup;
|
||||
numkartcupheaders++;
|
||||
CONS_Printf("Added cup %d ('%s')\n", cup->id, cup->name);
|
||||
}
|
||||
|
||||
readcupheader(f, cup);
|
||||
DEH_WriteUndoline(word, word2, UNDO_HEADER);
|
||||
}
|
||||
else if (fastcmp(word, "CUTSCENE"))
|
||||
{
|
||||
if (i > 0 && i < 129)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
280
src/g_game.c
280
src/g_game.c
|
|
@ -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;
|
||||
|
||||
|
|
@ -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++)
|
||||
|
|
@ -2564,6 +2571,7 @@ void G_PlayerReborn(INT32 player)
|
|||
player_t *p;
|
||||
INT32 score, marescore;
|
||||
INT32 lives;
|
||||
boolean lostlife;
|
||||
INT32 continues;
|
||||
// SRB2kart
|
||||
UINT8 kartspeed;
|
||||
|
|
@ -2585,7 +2593,10 @@ void G_PlayerReborn(INT32 player)
|
|||
boolean spectator;
|
||||
boolean bot;
|
||||
UINT8 botdifficulty;
|
||||
UINT8 botdiffincrease;
|
||||
boolean botrival;
|
||||
SINT8 pity;
|
||||
SINT8 xtralife;
|
||||
|
||||
// SRB2kart
|
||||
respawnvars_t respawn;
|
||||
|
|
@ -2603,6 +2614,7 @@ void G_PlayerReborn(INT32 player)
|
|||
score = players[player].score;
|
||||
marescore = players[player].marescore;
|
||||
lives = players[player].lives;
|
||||
lostlife = players[player].lostlife;
|
||||
continues = players[player].continues;
|
||||
ctfteam = players[player].ctfteam;
|
||||
exiting = players[player].exiting;
|
||||
|
|
@ -2632,7 +2644,10 @@ void G_PlayerReborn(INT32 player)
|
|||
mare = players[player].mare;
|
||||
bot = players[player].bot;
|
||||
botdifficulty = players[player].botvars.difficulty;
|
||||
botdiffincrease = players[player].botvars.diffincrease;
|
||||
botrival = players[player].botvars.rival;
|
||||
pity = players[player].pity;
|
||||
xtralife = players[player].xtralife;
|
||||
|
||||
// SRB2kart
|
||||
if (leveltime <= starttime)
|
||||
|
|
@ -2683,6 +2698,7 @@ void G_PlayerReborn(INT32 player)
|
|||
p->score = score;
|
||||
p->marescore = marescore;
|
||||
p->lives = lives;
|
||||
p->lostlife = lostlife;
|
||||
p->continues = continues;
|
||||
p->pflags = pflags;
|
||||
p->ctfteam = ctfteam;
|
||||
|
|
@ -2709,7 +2725,10 @@ void G_PlayerReborn(INT32 player)
|
|||
p->mare = mare;
|
||||
p->bot = bot;
|
||||
p->botvars.difficulty = botdifficulty;
|
||||
p->botvars.diffincrease = botdiffincrease;
|
||||
p->botvars.rival = botrival;
|
||||
p->pity = pity;
|
||||
p->xtralife = xtralife;
|
||||
|
||||
// SRB2kart
|
||||
p->kartstuff[k_itemroulette] = itemroulette;
|
||||
|
|
@ -3201,6 +3220,47 @@ void G_ExitLevel(void)
|
|||
{
|
||||
if (gamestate == GS_LEVEL)
|
||||
{
|
||||
if (grandprixinfo.gp == true && grandprixinfo.wonround != true)
|
||||
{
|
||||
UINT8 i;
|
||||
|
||||
// You didn't win...
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] && !players[i].spectator && !players[i].bot)
|
||||
{
|
||||
if (players[i].lives > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i == MAXPLAYERS)
|
||||
{
|
||||
// GAME OVER, try again from the start!
|
||||
|
||||
if (netgame)
|
||||
{
|
||||
; // restart cup here if we do online GP
|
||||
}
|
||||
else
|
||||
{
|
||||
D_QuitNetGame();
|
||||
CL_Reset();
|
||||
D_StartTitle();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Go redo this course.
|
||||
G_SetRetryFlag();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
gameaction = ga_completed;
|
||||
lastdraw = true;
|
||||
|
||||
|
|
@ -3276,18 +3336,15 @@ boolean G_IsSpecialStage(INT32 mapnum)
|
|||
//
|
||||
boolean G_GametypeUsesLives(void)
|
||||
{
|
||||
// SRB2kart NEEDS no lives
|
||||
#if 0
|
||||
// Coop, Competitive
|
||||
if ((gametype == GT_COOP || gametype == GT_COMPETITION)
|
||||
&& !modeattacking // No lives in Time Attack
|
||||
//&& !G_IsSpecialStage(gamemap)
|
||||
&& !(maptol & TOL_NIGHTS)) // No lives in NiGHTS
|
||||
if ((grandprixinfo.gp == true) // In Grand Prix
|
||||
&& (gametype == GT_RACE) // NOT in bonus round
|
||||
&& !(modeattacking) // NOT in Record Attack
|
||||
&& !G_IsSpecialStage(gamemap)) // NOT in special stage
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -3623,8 +3680,7 @@ void G_AddMapToBuffer(INT16 map)
|
|||
static void G_DoCompleted(void)
|
||||
{
|
||||
INT32 i, j = 0;
|
||||
boolean gottoken = false;
|
||||
SINT8 powertype = PWRLV_DISABLED;
|
||||
SINT8 powertype = K_UsingPowerLevels();
|
||||
|
||||
tokenlist = 0; // Reset the list
|
||||
|
||||
|
|
@ -3641,10 +3697,21 @@ static void G_DoCompleted(void)
|
|||
// SRB2Kart: exitlevel shouldn't get you the points
|
||||
if (!players[i].exiting && !(players[i].pflags & PF_TIMEOVER))
|
||||
{
|
||||
players[i].pflags |= PF_TIMEOVER;
|
||||
if (P_IsLocalPlayer(&players[i]))
|
||||
j++;
|
||||
if (players[i].bot)
|
||||
{
|
||||
K_FakeBotResults(&players[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
players[i].pflags |= PF_TIMEOVER;
|
||||
|
||||
if (P_IsLocalPlayer(&players[i]))
|
||||
{
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
G_PlayerFinishLevel(i); // take away cards and stuff
|
||||
}
|
||||
|
||||
|
|
@ -3664,11 +3731,33 @@ static void G_DoCompleted(void)
|
|||
// go to next level
|
||||
// nextmap is 0-based, unlike gamemap
|
||||
if (nextmapoverride != 0)
|
||||
{
|
||||
nextmap = (INT16)(nextmapoverride-1);
|
||||
else if (mapheaderinfo[gamemap-1]->nextlevel == 1101) // SRB2Kart: !!! WHENEVER WE GET GRAND PRIX, GO TO AWARDS MAP INSTEAD !!!
|
||||
nextmap = (INT16)(mapheaderinfo[gamemap] ? gamemap : (spstage_start-1)); // (gamemap-1)+1 == gamemap :V
|
||||
}
|
||||
else if (grandprixinfo.gp == true)
|
||||
{
|
||||
if (grandprixinfo.roundnum == 0 || grandprixinfo.cup == NULL) // Single session
|
||||
{
|
||||
nextmap = prevmap; // Same map
|
||||
}
|
||||
else
|
||||
{
|
||||
if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) // On final map
|
||||
{
|
||||
nextmap = 1101; // ceremonymap
|
||||
}
|
||||
else
|
||||
{
|
||||
// Proceed to next map
|
||||
nextmap = grandprixinfo.cup->levellist[grandprixinfo.roundnum];
|
||||
grandprixinfo.roundnum++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nextmap = (INT16)(mapheaderinfo[gamemap-1]->nextlevel-1);
|
||||
}
|
||||
|
||||
// Remember last map for when you come out of the special stage.
|
||||
if (!G_IsSpecialStage(gamemap))
|
||||
|
|
@ -3678,8 +3767,7 @@ static void G_DoCompleted(void)
|
|||
// a map of the proper gametype -- skip levels that don't support
|
||||
// the current gametype. (Helps avoid playing boss levels in Race,
|
||||
// for instance).
|
||||
if (!token && !G_IsSpecialStage(gamemap) && !modeattacking
|
||||
&& (nextmap >= 0 && nextmap < NUMMAPS))
|
||||
if (!modeattacking && grandprixinfo.gp == false && (nextmap >= 0 && nextmap < NUMMAPS))
|
||||
{
|
||||
register INT16 cm = nextmap;
|
||||
INT16 tolflag = G_TOLFlag(gametype);
|
||||
|
|
@ -3723,44 +3811,18 @@ static void G_DoCompleted(void)
|
|||
if (nextmap < 0 || (nextmap >= NUMMAPS && nextmap < 1100-1) || nextmap > 1102-1)
|
||||
I_Error("Followed map %d to invalid map %d\n", prevmap + 1, nextmap + 1);
|
||||
|
||||
// wrap around in race
|
||||
if (nextmap >= 1100-1 && nextmap <= 1102-1 && G_RaceGametype())
|
||||
nextmap = (INT16)(spstage_start-1);
|
||||
|
||||
if (gametype == GT_COOP && token)
|
||||
{
|
||||
token--;
|
||||
gottoken = true;
|
||||
|
||||
if (!(emeralds & EMERALD1))
|
||||
nextmap = (INT16)(sstage_start - 1); // Special Stage 1
|
||||
else if (!(emeralds & EMERALD2))
|
||||
nextmap = (INT16)(sstage_start); // Special Stage 2
|
||||
else if (!(emeralds & EMERALD3))
|
||||
nextmap = (INT16)(sstage_start + 1); // Special Stage 3
|
||||
else if (!(emeralds & EMERALD4))
|
||||
nextmap = (INT16)(sstage_start + 2); // Special Stage 4
|
||||
else if (!(emeralds & EMERALD5))
|
||||
nextmap = (INT16)(sstage_start + 3); // Special Stage 5
|
||||
else if (!(emeralds & EMERALD6))
|
||||
nextmap = (INT16)(sstage_start + 4); // Special Stage 6
|
||||
else if (!(emeralds & EMERALD7))
|
||||
nextmap = (INT16)(sstage_start + 5); // Special Stage 7
|
||||
else
|
||||
gottoken = false;
|
||||
}
|
||||
|
||||
if (G_IsSpecialStage(gamemap) && !gottoken)
|
||||
nextmap = lastmap; // Exiting from a special stage? Go back to the game. Tails 08-11-2001
|
||||
|
||||
automapactive = false;
|
||||
|
||||
if (gametype != GT_COOP)
|
||||
{
|
||||
if (cv_advancemap.value == 0) // Stay on same map.
|
||||
{
|
||||
nextmap = prevmap;
|
||||
}
|
||||
else if (cv_advancemap.value == 2) // Go to random map.
|
||||
{
|
||||
nextmap = G_RandMap(G_TOLFlag(gametype), prevmap, false, 0, false, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// We are committed to this map now.
|
||||
|
|
@ -3770,14 +3832,6 @@ static void G_DoCompleted(void)
|
|||
P_AllocMapHeader(nextmap);
|
||||
|
||||
// Set up power level gametype scrambles
|
||||
if (netgame && cv_kartusepwrlv.value)
|
||||
{
|
||||
if (G_RaceGametype())
|
||||
powertype = PWRLV_RACE;
|
||||
else if (G_BattleGametype())
|
||||
powertype = PWRLV_BATTLE;
|
||||
}
|
||||
|
||||
K_SetPowerLevelScrambles(powertype);
|
||||
|
||||
demointermission:
|
||||
|
|
@ -3837,7 +3891,7 @@ void G_NextLevel(void)
|
|||
{
|
||||
if (gamestate != GS_VOTING)
|
||||
{
|
||||
if ((cv_advancemap.value == 3) && !modeattacking && !skipstats && (multiplayer || netgame))
|
||||
if ((cv_advancemap.value == 3) && grandprixinfo.gp == false && !modeattacking && !skipstats && (multiplayer || netgame))
|
||||
{
|
||||
UINT8 i;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
|
|
@ -3934,7 +3988,7 @@ static void G_DoContinued(void)
|
|||
token = 0;
|
||||
|
||||
// Reset # of lives
|
||||
pl->lives = (ultimatemode) ? 1 : 3;
|
||||
pl->lives = 3;
|
||||
|
||||
D_MapChange(gamemap, gametype, false, false, 0, false, false);
|
||||
|
||||
|
|
@ -4503,7 +4557,7 @@ void G_SaveGame(UINT32 savegameslot)
|
|||
void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar, UINT8 ssplayers, boolean FLS)
|
||||
{
|
||||
INT32 i;
|
||||
UINT8 color = 0;
|
||||
//UINT8 color = 0;
|
||||
paused = false;
|
||||
|
||||
if (demo.playback)
|
||||
|
|
@ -4523,23 +4577,20 @@ void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar
|
|||
// this leave the actual game if needed
|
||||
SV_StartSinglePlayerServer();
|
||||
|
||||
if (savedata.lives > 0)
|
||||
{
|
||||
color = savedata.skincolor;
|
||||
}
|
||||
else if (splitscreen != ssplayers)
|
||||
if (splitscreen != ssplayers)
|
||||
{
|
||||
splitscreen = ssplayers;
|
||||
SplitScreen_OnChange();
|
||||
}
|
||||
|
||||
if (!color && !modeattacking)
|
||||
color = skins[pickedchar].prefcolor;
|
||||
//if (!color)
|
||||
//color = skins[pickedchar].prefcolor;
|
||||
|
||||
SetPlayerSkinByNum(consoleplayer, pickedchar);
|
||||
CV_StealthSet(&cv_skin, skins[pickedchar].name);
|
||||
|
||||
if (color)
|
||||
CV_StealthSetValue(&cv_playercolor, color);
|
||||
//if (color)
|
||||
//CV_StealthSetValue(&cv_playercolor, color);
|
||||
|
||||
if (mapname)
|
||||
D_MapChange(M_MapNumber(mapname[3], mapname[4]), gametype, pencoremode, true, 1, false, FLS);
|
||||
|
|
@ -4568,63 +4619,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....
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
58
src/k_bot.c
58
src/k_bot.c
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
19
src/k_bot.h
19
src/k_bot.h
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
605
src/k_grandprix.c
Normal file
605
src/k_grandprix.c
Normal 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
144
src/k_grandprix.h
Normal 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
|
||||
429
src/k_kart.c
429
src/k_kart.c
|
|
@ -393,7 +393,7 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem)
|
|||
\return void
|
||||
*/
|
||||
|
||||
static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, boolean bot)
|
||||
static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, boolean bot, boolean rival)
|
||||
{
|
||||
INT32 newodds;
|
||||
INT32 i;
|
||||
|
|
@ -471,10 +471,12 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp
|
|||
|
||||
#define POWERITEMODDS(odds) {\
|
||||
if (franticitems) \
|
||||
odds <<= 1; \
|
||||
odds = FixedMul(odds<<FRACBITS, FRACUNIT + ((PLAYERSCALING << FRACBITS) / 25)) >> FRACBITS; \
|
||||
odds *= 2; \
|
||||
if (rival) \
|
||||
odds *= 2; \
|
||||
odds = FixedMul(odds * FRACUNIT, FRACUNIT + ((PLAYERSCALING * FRACUNIT) / 25)) / FRACUNIT; \
|
||||
if (mashed > 0) \
|
||||
odds = FixedDiv(odds<<FRACBITS, FRACUNIT + mashed) >> FRACBITS; \
|
||||
odds = FixedDiv(odds * FRACUNIT, FRACUNIT + mashed) / FRACUNIT; \
|
||||
}
|
||||
|
||||
#define COOLDOWNONSTART (leveltime < (30*TICRATE)+starttime)
|
||||
|
|
@ -610,7 +612,7 @@ static UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8
|
|||
|
||||
for (j = 1; j < NUMKARTRESULTS; j++)
|
||||
{
|
||||
if (K_KartGetItemOdds(i, j, mashed, spbrush, player->bot) > 0)
|
||||
if (K_KartGetItemOdds(i, j, mashed, spbrush, player->bot, (player->bot && player->botvars.rival)) > 0)
|
||||
{
|
||||
available = true;
|
||||
break;
|
||||
|
|
@ -772,7 +774,9 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
|
|||
pdis = FixedDiv(pdis * FRACUNIT, mapobjectscale) / FRACUNIT;
|
||||
|
||||
if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items
|
||||
{
|
||||
pdis = (15 * pdis) / 14;
|
||||
}
|
||||
|
||||
if (spbplace != -1 && player->kartstuff[k_position] == spbplace+1) // SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell
|
||||
{
|
||||
|
|
@ -780,6 +784,12 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
|
|||
spbrush = true;
|
||||
}
|
||||
|
||||
if (player->bot && player->botvars.rival)
|
||||
{
|
||||
// Rival has better odds :)
|
||||
pdis = (15 * pdis) / 14;
|
||||
}
|
||||
|
||||
pdis = ((28 + (8-pingame)) * pdis) / 28; // scale with player count
|
||||
|
||||
// SPECIAL CASE No. 1:
|
||||
|
|
@ -908,7 +918,7 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
|
|||
useodds = K_FindUseodds(player, mashed, pdis, bestbumper, spbrush);
|
||||
|
||||
for (i = 1; i < NUMKARTRESULTS; i++)
|
||||
spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, mashed, spbrush, player->bot));
|
||||
spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, mashed, spbrush, player->bot, (player->bot && player->botvars.rival)));
|
||||
|
||||
// Award the player whatever power is rolled
|
||||
if (totalspawnchance > 0)
|
||||
|
|
@ -2052,6 +2062,12 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower)
|
|||
{
|
||||
// Give top speed a buff for bots, since it's a fairly weak stat without drifting
|
||||
fixed_t speedmul = ((kartspeed-1) * FRACUNIT / 8) / 10; // +10% for speed 9
|
||||
|
||||
if (player->botvars.rival == true)
|
||||
{
|
||||
speedmul += FRACUNIT/10; // +10% for rival
|
||||
}
|
||||
|
||||
finalspeed = FixedMul(finalspeed, FRACUNIT + speedmul);
|
||||
}
|
||||
|
||||
|
|
@ -2082,9 +2098,12 @@ fixed_t K_GetKartAccel(player_t *player)
|
|||
//k_accel += 3 * (9 - kartspeed); // 36 - 60
|
||||
k_accel += 4 * (9 - kartspeed); // 32 - 64
|
||||
|
||||
|
||||
if (K_PlayerUsesBotMovement(player))
|
||||
{
|
||||
k_accel = FixedMul(k_accel, K_BotRubberband(player));
|
||||
// Rubberbanding acceleration is waekened since it makes hits feel more meaningful
|
||||
fixed_t rubberband = K_BotRubberband(player) - FRACUNIT;
|
||||
k_accel = FixedMul(k_accel, FRACUNIT + (rubberband/2));
|
||||
}
|
||||
|
||||
return FixedMul(k_accel, FRACUNIT+player->kartstuff[k_accelboost]);
|
||||
|
|
@ -2107,19 +2126,25 @@ UINT16 K_GetKartFlashing(player_t *player)
|
|||
|
||||
fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove)
|
||||
{
|
||||
fixed_t accelmax = 4000;
|
||||
const fixed_t accelmax = 4000;
|
||||
const fixed_t p_speed = K_GetKartSpeed(player, true);
|
||||
const fixed_t p_accel = K_GetKartAccel(player);
|
||||
fixed_t newspeed, oldspeed, finalspeed;
|
||||
fixed_t p_speed = K_GetKartSpeed(player, true);
|
||||
fixed_t p_accel = K_GetKartAccel(player);
|
||||
fixed_t orig = ORIG_FRICTION;
|
||||
|
||||
if (!onground) return 0; // If the player isn't on the ground, there is no change in speed
|
||||
|
||||
if (K_PlayerUsesBotMovement(player))
|
||||
{
|
||||
orig = K_BotFrictionRubberband(player, ORIG_FRICTION);
|
||||
}
|
||||
|
||||
// ACCELCODE!!!1!11!
|
||||
oldspeed = R_PointToDist2(0, 0, player->rmomx, player->rmomy); // FixedMul(P_AproxDistance(player->rmomx, player->rmomy), player->mo->scale);
|
||||
// Don't calculate the acceleration as ever being above top speed
|
||||
if (oldspeed > p_speed)
|
||||
oldspeed = p_speed;
|
||||
newspeed = FixedDiv(FixedDiv(FixedMul(oldspeed, accelmax - p_accel) + FixedMul(p_speed, p_accel), accelmax), ORIG_FRICTION);
|
||||
newspeed = FixedDiv(FixedDiv(FixedMul(oldspeed, accelmax - p_accel) + FixedMul(p_speed, p_accel), accelmax), orig);
|
||||
|
||||
if (player->kartstuff[k_pogospring]) // Pogo Spring minimum/maximum thrust
|
||||
{
|
||||
|
|
@ -7380,7 +7405,8 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
player->mo->friction += 4608;
|
||||
}
|
||||
|
||||
if (player->speed > 0 && cmd->forwardmove < 0) // change friction while braking no matter what, otherwise it's not any more effective than just letting go off accel
|
||||
// change friction while braking no matter what, otherwise it's not any more effective than just letting go off accel
|
||||
if (player->speed > 0 && cmd->forwardmove < 0)
|
||||
player->mo->friction -= 2048;
|
||||
|
||||
// Karma ice physics
|
||||
|
|
@ -7417,6 +7443,13 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
if (player->mo->movefactor < 32)
|
||||
player->mo->movefactor = 32;
|
||||
}
|
||||
|
||||
// Don't go too far above your top speed when rubberbanding
|
||||
// Down here, because we do NOT want to modify movefactor
|
||||
if (K_PlayerUsesBotMovement(player))
|
||||
{
|
||||
player->mo->friction = K_BotFrictionRubberband(player, player->mo->friction);
|
||||
}
|
||||
}
|
||||
|
||||
K_KartDrift(player, P_IsObjectOnGround(player->mo)); // Not using onground, since we don't want this affected by spring pads
|
||||
|
|
@ -7707,6 +7740,8 @@ static patch_t *kp_sadface[2];
|
|||
|
||||
static patch_t *kp_check[6];
|
||||
|
||||
static patch_t *kp_rival[2];
|
||||
|
||||
static patch_t *kp_eggnum[4];
|
||||
|
||||
static patch_t *kp_flameshieldmeter[104][2];
|
||||
|
|
@ -7729,6 +7764,8 @@ static patch_t *kp_itemminimap;
|
|||
static patch_t *kp_alagles[10];
|
||||
static patch_t *kp_blagles[6];
|
||||
|
||||
static patch_t *kp_cpu;
|
||||
|
||||
void K_LoadKartHUDGraphics(void)
|
||||
{
|
||||
INT32 i, j;
|
||||
|
|
@ -7999,6 +8036,14 @@ void K_LoadKartHUDGraphics(void)
|
|||
kp_check[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
|
||||
}
|
||||
|
||||
// Rival indicators
|
||||
sprintf(buffer, "K_RIVALx");
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
buffer[7] = '1'+i;
|
||||
kp_rival[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
|
||||
}
|
||||
|
||||
// Eggman warning numbers
|
||||
sprintf(buffer, "K_EGGNx");
|
||||
for (i = 0; i < 4; i++)
|
||||
|
|
@ -8086,6 +8131,8 @@ void K_LoadKartHUDGraphics(void)
|
|||
buffer[7] = '0'+i;
|
||||
kp_blagles[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
|
||||
}
|
||||
|
||||
kp_cpu = (patch_t *) W_CachePatchName("K_CPU", PU_HUDGFX);
|
||||
}
|
||||
|
||||
// For the item toggle menu
|
||||
|
|
@ -9130,7 +9177,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
|
|||
{
|
||||
if (players[tab[i].num].bot)
|
||||
{
|
||||
; // TODO: Put a graphic here to indicate this player is a bot!
|
||||
V_DrawScaledPatch(x + ((i < 8) ? -25 : rightoffset + 3), y-2, 0, kp_cpu);
|
||||
}
|
||||
else if (tab[i].num != serverplayer || !server_lagless)
|
||||
{
|
||||
|
|
@ -9243,6 +9290,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
|
|||
|
||||
static void K_drawKartLapsAndRings(void)
|
||||
{
|
||||
const boolean uselives = G_GametypeUsesLives();
|
||||
SINT8 ringanim_realframe = stplyr->karthud[khud_ringframe];
|
||||
INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT);
|
||||
UINT8 rn[2];
|
||||
|
|
@ -9327,7 +9375,7 @@ static void K_drawKartLapsAndRings(void)
|
|||
}
|
||||
|
||||
// Rings
|
||||
if (netgame)
|
||||
if (!uselives)
|
||||
{
|
||||
V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy-10, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[1]);
|
||||
if (flipflag)
|
||||
|
|
@ -9349,7 +9397,7 @@ static void K_drawKartLapsAndRings(void)
|
|||
V_DrawScaledPatch(fr-12, fy-23, V_HUDTRANS|splitflags, kp_ringspblocksmall[stplyr->karthud[khud_ringspblock]]);
|
||||
|
||||
// Lives
|
||||
if (!netgame)
|
||||
if (uselives)
|
||||
{
|
||||
UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE);
|
||||
V_DrawMappedPatch(fr+21, fy-13, V_HUDTRANS|splitflags, facemmapprefix[stplyr->skin], colormap);
|
||||
|
|
@ -9367,7 +9415,7 @@ static void K_drawKartLapsAndRings(void)
|
|||
V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->laps, cv_numlaps.value));
|
||||
|
||||
// Rings
|
||||
if (netgame)
|
||||
if (!uselives)
|
||||
V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringsticker[1]);
|
||||
else
|
||||
V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringsticker[0]);
|
||||
|
|
@ -9391,7 +9439,7 @@ static void K_drawKartLapsAndRings(void)
|
|||
V_DrawScaledPatch(LAPS_X-5, LAPS_Y-28, V_HUDTRANS|splitflags, kp_ringspblock[stplyr->karthud[khud_ringspblock]]);
|
||||
|
||||
// Lives
|
||||
if (!netgame)
|
||||
if (uselives)
|
||||
{
|
||||
UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE);
|
||||
V_DrawMappedPatch(LAPS_X+46, LAPS_Y-16, V_HUDTRANS|splitflags, facerankprefix[stplyr->skin], colormap);
|
||||
|
|
@ -9582,34 +9630,6 @@ static void K_drawKartBumpersOrKarma(void)
|
|||
}
|
||||
}
|
||||
|
||||
static fixed_t K_FindCheckX(fixed_t px, fixed_t py, angle_t ang, fixed_t mx, fixed_t my)
|
||||
{
|
||||
fixed_t dist, x;
|
||||
fixed_t range = RING_DIST/3;
|
||||
angle_t diff;
|
||||
|
||||
range *= gamespeed+1;
|
||||
|
||||
dist = abs(R_PointToDist2(px, py, mx, my));
|
||||
if (dist > range)
|
||||
return -320;
|
||||
|
||||
diff = R_PointToAngle2(px, py, mx, my) - ang;
|
||||
|
||||
if (diff < ANGLE_90 || diff > ANGLE_270)
|
||||
return -320;
|
||||
else
|
||||
x = (FixedMul(FINETANGENT(((diff+ANGLE_90)>>ANGLETOFINESHIFT) & 4095), 160<<FRACBITS) + (160<<FRACBITS))>>FRACBITS;
|
||||
|
||||
if (encoremode)
|
||||
x = 320-x;
|
||||
|
||||
if (r_splitscreen > 1)
|
||||
x /= 2;
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
static void K_drawKartWanted(void)
|
||||
{
|
||||
UINT8 i, numwanted = 0;
|
||||
|
|
@ -9682,52 +9702,297 @@ static void K_drawKartWanted(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, angle_t camang, angle_t camaim, UINT8 camnum, vertex_t *point)
|
||||
{
|
||||
const INT32 swhalf = (BASEVIDWIDTH / 2);
|
||||
const fixed_t swhalffixed = swhalf * FRACUNIT;
|
||||
|
||||
const INT32 shhalf = (BASEVIDHEIGHT / 2);
|
||||
const fixed_t shhalffixed = shhalf * FRACUNIT;
|
||||
|
||||
INT32 anglediff = (signed)(camang - R_PointToAngle2(campos->x, campos->y, point->x, point->y));
|
||||
fixed_t distance = R_PointToDist2(campos->x, campos->y, point->x, point->y);
|
||||
fixed_t factor = INT32_MAX;
|
||||
|
||||
if (abs(anglediff) > ANGLE_90)
|
||||
{
|
||||
if (hud_x != NULL)
|
||||
{
|
||||
*hud_x = -BASEVIDWIDTH * FRACUNIT;
|
||||
}
|
||||
|
||||
if (hud_y != NULL)
|
||||
{
|
||||
*hud_y = -BASEVIDWIDTH * FRACUNIT;
|
||||
}
|
||||
|
||||
//*hud_scale = FRACUNIT;
|
||||
return;
|
||||
}
|
||||
|
||||
factor = max(1, FINECOSINE(anglediff >> ANGLETOFINESHIFT));
|
||||
|
||||
#define NEWTAN(n) FINETANGENT(((n + ANGLE_90) >> ANGLETOFINESHIFT) & 4095)
|
||||
|
||||
if (hud_x != NULL)
|
||||
{
|
||||
*hud_x = FixedMul(NEWTAN(anglediff), swhalffixed) + swhalffixed;
|
||||
|
||||
if (r_splitscreen >= 2)
|
||||
{
|
||||
*hud_x /= 2;
|
||||
|
||||
if (camnum & 1)
|
||||
{
|
||||
*hud_x += swhalffixed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hud_y != NULL)
|
||||
{
|
||||
*hud_y = campos->z - point->z;
|
||||
*hud_y = FixedDiv(*hud_y, FixedMul(factor, distance));
|
||||
*hud_y = (*hud_y * swhalf) + shhalffixed;
|
||||
*hud_y = *hud_y + NEWTAN(camaim) * swhalf;
|
||||
|
||||
if (r_splitscreen >= 1)
|
||||
{
|
||||
*hud_y /= 2;
|
||||
|
||||
if (camnum > 1)
|
||||
{
|
||||
*hud_y += shhalffixed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//*hud_scale = FixedDiv(swhalffixed, FixedMul(factor, distance));
|
||||
|
||||
#undef NEWTAN
|
||||
}
|
||||
|
||||
static void K_drawKartPlayerCheck(void)
|
||||
{
|
||||
INT32 i;
|
||||
UINT8 *colormap;
|
||||
INT32 x;
|
||||
|
||||
const fixed_t maxdistance = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed));
|
||||
camera_t *thiscam;
|
||||
vertex_t c;
|
||||
UINT8 cnum = 0;
|
||||
UINT8 i;
|
||||
INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM);
|
||||
|
||||
if (!stplyr->mo || stplyr->spectator)
|
||||
if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (stplyr->awayviewtics)
|
||||
if (stplyr->spectator || stplyr->awayviewtics)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (( stplyr->cmd.buttons & BT_LOOKBACK ))
|
||||
if (stplyr->cmd.buttons & BT_LOOKBACK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (r_splitscreen)
|
||||
{
|
||||
for (i = 1; i <= r_splitscreen; i++)
|
||||
{
|
||||
if (stplyr == &players[displayplayers[i]])
|
||||
{
|
||||
cnum = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
thiscam = &camera[cnum];
|
||||
|
||||
c.x = thiscam->x;
|
||||
c.y = thiscam->y;
|
||||
c.z = thiscam->z;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
player_t *checkplayer = &players[i];
|
||||
fixed_t distance = maxdistance+1;
|
||||
UINT8 *colormap = NULL;
|
||||
UINT8 pnum = 0;
|
||||
fixed_t x = 0;
|
||||
vertex_t v;
|
||||
|
||||
if (&players[i] == stplyr)
|
||||
continue;
|
||||
if (!playeringame[i] || players[i].spectator)
|
||||
continue;
|
||||
if (!players[i].mo)
|
||||
continue;
|
||||
|
||||
if ((players[i].kartstuff[k_invincibilitytimer] <= 0) && (leveltime & 2))
|
||||
pnum++; // white frames
|
||||
|
||||
if (players[i].kartstuff[k_itemtype] == KITEM_GROW || players[i].kartstuff[k_growshrinktimer] > 0)
|
||||
pnum += 4;
|
||||
else if (players[i].kartstuff[k_itemtype] == KITEM_INVINCIBILITY || players[i].kartstuff[k_invincibilitytimer])
|
||||
pnum += 2;
|
||||
|
||||
x = K_FindCheckX(stplyr->mo->x, stplyr->mo->y, stplyr->mo->angle, players[i].mo->x, players[i].mo->y);
|
||||
if (x <= 320 && x >= 0)
|
||||
if (!playeringame[i] || checkplayer->spectator)
|
||||
{
|
||||
if (x < 14)
|
||||
x = 14;
|
||||
else if (x > 306)
|
||||
x = 306;
|
||||
// Not in-game
|
||||
continue;
|
||||
}
|
||||
|
||||
colormap = R_GetTranslationColormap(TC_DEFAULT, players[i].mo->color, GTC_CACHE);
|
||||
V_DrawMappedPatch(x, CHEK_Y, V_HUDTRANS|splitflags, kp_check[pnum], colormap);
|
||||
if (checkplayer->mo == NULL || P_MobjWasRemoved(checkplayer->mo))
|
||||
{
|
||||
// No object
|
||||
continue;
|
||||
}
|
||||
|
||||
if (checkplayer == stplyr)
|
||||
{
|
||||
// This is you!
|
||||
continue;
|
||||
}
|
||||
|
||||
v.x = checkplayer->mo->x;
|
||||
v.y = checkplayer->mo->y;
|
||||
v.z = checkplayer->mo->z;
|
||||
|
||||
distance = R_PointToDist2(c.x, c.y, v.x, v.y);
|
||||
|
||||
if (distance > maxdistance)
|
||||
{
|
||||
// Too far away
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((checkplayer->kartstuff[k_invincibilitytimer] <= 0) && (leveltime & 2))
|
||||
{
|
||||
pnum++; // white frames
|
||||
}
|
||||
|
||||
if (checkplayer->kartstuff[k_itemtype] == KITEM_GROW || checkplayer->kartstuff[k_growshrinktimer] > 0)
|
||||
{
|
||||
pnum += 4;
|
||||
}
|
||||
else if (checkplayer->kartstuff[k_itemtype] == KITEM_INVINCIBILITY || checkplayer->kartstuff[k_invincibilitytimer])
|
||||
{
|
||||
pnum += 2;
|
||||
}
|
||||
|
||||
K_ObjectTracking(&x, NULL, &c, thiscam->angle + ANGLE_180, 0, cnum, &v);
|
||||
|
||||
colormap = R_GetTranslationColormap(TC_DEFAULT, checkplayer->mo->color, GTC_CACHE);
|
||||
V_DrawFixedPatch(x, CHEK_Y * FRACUNIT, FRACUNIT, V_HUDTRANS|splitflags, kp_check[pnum], colormap);
|
||||
}
|
||||
}
|
||||
|
||||
static void K_drawKartNameTags(void)
|
||||
{
|
||||
const fixed_t maxdistance = 4096*mapobjectscale;
|
||||
camera_t *thiscam;
|
||||
vertex_t c;
|
||||
UINT8 cnum = 0;
|
||||
UINT8 i;
|
||||
|
||||
if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (stplyr->awayviewtics)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (r_splitscreen)
|
||||
{
|
||||
for (i = 1; i <= r_splitscreen; i++)
|
||||
{
|
||||
if (stplyr == &players[displayplayers[i]])
|
||||
{
|
||||
cnum = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
thiscam = &camera[cnum];
|
||||
|
||||
c.x = thiscam->x;
|
||||
c.y = thiscam->y;
|
||||
c.z = thiscam->z;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
player_t *ntplayer = &players[i];
|
||||
fixed_t distance = maxdistance+1;
|
||||
|
||||
fixed_t x = -BASEVIDWIDTH * FRACUNIT;
|
||||
fixed_t y = -BASEVIDWIDTH * FRACUNIT;
|
||||
|
||||
vertex_t v;
|
||||
UINT8 j;
|
||||
|
||||
if (!playeringame[i] || ntplayer->spectator)
|
||||
{
|
||||
// Not in-game
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ntplayer->mo == NULL || P_MobjWasRemoved(ntplayer->mo))
|
||||
{
|
||||
// No object
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!P_CheckSight(stplyr->mo, ntplayer->mo))
|
||||
{
|
||||
// Can't see
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0; j <= r_splitscreen; j++)
|
||||
{
|
||||
if (ntplayer == &players[displayplayers[j]])
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j < r_splitscreen)
|
||||
{
|
||||
// Is a player that's being shown on this computer
|
||||
continue;
|
||||
}
|
||||
|
||||
v.x = ntplayer->mo->x;
|
||||
v.y = ntplayer->mo->y;
|
||||
v.z = ntplayer->mo->z;
|
||||
|
||||
if (!(ntplayer->mo->eflags & MFE_VERTICALFLIP))
|
||||
{
|
||||
v.z += ntplayer->mo->height;
|
||||
}
|
||||
|
||||
distance = R_PointToDist2(c.x, c.y, v.x, v.y);
|
||||
|
||||
if (distance > maxdistance)
|
||||
{
|
||||
// Too far away
|
||||
continue;
|
||||
}
|
||||
|
||||
K_ObjectTracking(&x, &y, &c, thiscam->angle, thiscam->aiming, cnum, &v);
|
||||
|
||||
if (x == -BASEVIDWIDTH * FRACUNIT)
|
||||
{
|
||||
// Off-screen
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ntplayer->bot)
|
||||
{
|
||||
if (ntplayer->botvars.rival == true)
|
||||
{
|
||||
UINT8 blink = ((leveltime / 7) & 1);
|
||||
V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS, kp_rival[blink], NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((ntplayer->kartstuff[k_position] >= stplyr->kartstuff[k_position]-1)
|
||||
&& (ntplayer->kartstuff[k_position] <= stplyr->kartstuff[k_position]+1))
|
||||
{
|
||||
; // TODO: Draw a cool name tag for online
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10628,13 +10893,19 @@ static void K_drawDistributionDebugger(void)
|
|||
spbrush = true;
|
||||
}
|
||||
|
||||
if (stplyr->bot && stplyr->botvars.rival)
|
||||
{
|
||||
// Rival has better odds :)
|
||||
pdis = (15 * pdis) / 14;
|
||||
}
|
||||
|
||||
pdis = ((28 + (8-pingame)) * pdis) / 28; // scale with player count
|
||||
|
||||
useodds = K_FindUseodds(stplyr, 0, pdis, bestbumper, spbrush);
|
||||
|
||||
for (i = 1; i < NUMKARTRESULTS; i++)
|
||||
{
|
||||
const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0, spbrush, stplyr->bot);
|
||||
const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0, spbrush, stplyr->bot, (stplyr->bot && stplyr->botvars.rival));
|
||||
if (itemodds <= 0)
|
||||
continue;
|
||||
|
||||
|
|
@ -10735,6 +11006,12 @@ void K_drawKartHUD(void)
|
|||
if (cv_kartcheck.value && !splitscreen && !players[displayplayers[0]].exiting && !freecam)
|
||||
K_drawKartPlayerCheck();
|
||||
|
||||
// nametags
|
||||
#ifdef HAVE_BLUA
|
||||
if (LUA_HudEnabled(hud_names))
|
||||
#endif
|
||||
K_drawKartNameTags();
|
||||
|
||||
// Draw WANTED status
|
||||
if (G_BattleGametype())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -244,6 +244,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"))
|
||||
|
|
@ -486,6 +488,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"))
|
||||
|
|
|
|||
149
src/m_menu.c
149
src/m_menu.c
|
|
@ -60,6 +60,7 @@
|
|||
#include "k_pwrlv.h"
|
||||
#include "d_player.h" // KITEM_ constants
|
||||
#include "k_color.h"
|
||||
#include "k_grandprix.h"
|
||||
|
||||
#include "i_joy.h" // for joystick menu controls
|
||||
|
||||
|
|
@ -226,7 +227,8 @@ menu_t SP_MainDef, MP_MainDef, OP_MainDef;
|
|||
menu_t MISC_ScrambleTeamDef, MISC_ChangeTeamDef, MISC_ChangeSpectateDef;
|
||||
|
||||
// Single Player
|
||||
//static void M_LoadGame(INT32 choice);
|
||||
static void M_GrandPrixTemp(INT32 choice);
|
||||
static void M_StartGrandPrix(INT32 choice);
|
||||
static void M_TimeAttack(INT32 choice);
|
||||
static boolean M_QuitTimeAttackMenu(void);
|
||||
static void M_BreakTheCapsules(INT32 choice);
|
||||
|
|
@ -239,6 +241,7 @@ static void M_ModeAttackEndGame(INT32 choice);
|
|||
static void M_SetGuestReplay(INT32 choice);
|
||||
//static void M_ChoosePlayer(INT32 choice);
|
||||
menu_t SP_LevelStatsDef;
|
||||
static menu_t SP_GrandPrixTempDef;
|
||||
static menu_t SP_TimeAttackDef, SP_ReplayDef, SP_GuestReplayDef, SP_GhostDef;
|
||||
//static menu_t SP_NightsAttackDef, SP_NightsReplayDef, SP_NightsGuestReplayDef, SP_NightsGhostDef;
|
||||
|
||||
|
|
@ -462,6 +465,13 @@ static consvar_t cv_dummycontinues = {"dummycontinues", "0", CV_HIDEN, liveslimi
|
|||
//static consvar_t cv_dummymares = {"dummymares", "Overall", CV_HIDEN|CV_CALL, dummymares_cons_t, Dummymares_OnChange, 0, NULL, NULL, 0, 0, NULL};
|
||||
static consvar_t cv_dummystaff = {"dummystaff", "0", CV_HIDEN|CV_CALL, dummystaff_cons_t, Dummystaff_OnChange, 0, NULL, NULL, 0, 0, NULL};
|
||||
|
||||
static CV_PossibleValue_t dummygpdifficulty_cons_t[] = {{0, "Easy"}, {1, "Normal"}, {2, "Hard"}, {3, "Master"}, {0, NULL}};
|
||||
static CV_PossibleValue_t dummygpcup_cons_t[50] = {{1, "TEMP"}}; // A REALLY BIG NUMBER, SINCE THIS IS TEMP UNTIL NEW MENUS
|
||||
|
||||
static consvar_t cv_dummygpdifficulty = {"dummygpdifficulty", "Normal", CV_HIDEN, dummygpdifficulty_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
|
||||
static consvar_t cv_dummygpencore = {"dummygpencore", "Off", CV_HIDEN, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
|
||||
static consvar_t cv_dummygpcup = {"dummygpcup", "TEMP", CV_HIDEN, dummygpcup_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
|
||||
|
||||
// ==========================================================================
|
||||
// ORGANIZATION START.
|
||||
// ==========================================================================
|
||||
|
|
@ -801,30 +811,30 @@ static menuitem_t SR_EmblemHintMenu[] =
|
|||
// Single Player Main
|
||||
static menuitem_t SP_MainMenu[] =
|
||||
{
|
||||
//{IT_CALL | IT_STRING, NULL, "Grand Prix", M_LoadGame, 92},
|
||||
{IT_SECRET, NULL, "Time Attack", M_TimeAttack, 100},
|
||||
{IT_SECRET, NULL, "Break the Capsules", M_BreakTheCapsules, 108},
|
||||
{IT_STRING|IT_CALL, NULL, "Grand Prix", M_GrandPrixTemp, 92},
|
||||
{IT_SECRET, NULL, "Time Attack", M_TimeAttack, 100},
|
||||
{IT_SECRET, NULL, "Break the Capsules", M_BreakTheCapsules, 108},
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
//spgrandprix,
|
||||
spgrandprix,
|
||||
sptimeattack,
|
||||
spbreakthecapsules
|
||||
};
|
||||
|
||||
// Single Player Load Game
|
||||
/*static menuitem_t SP_LoadGameMenu[] =
|
||||
static menuitem_t SP_GrandPrixPlaceholderMenu[] =
|
||||
{
|
||||
{IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLoadSave, '\0'}, // dummy menuitem for the control func
|
||||
};
|
||||
{IT_STRING|IT_CVAR, NULL, "Character", &cv_chooseskin, 10},
|
||||
{IT_STRING|IT_CVAR, NULL, "Color", &cv_playercolor, 20},
|
||||
|
||||
// Single Player Level Select
|
||||
static menuitem_t SP_LevelSelectMenu[] =
|
||||
{
|
||||
{IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 78},
|
||||
{IT_WHITESTRING|IT_CALL, NULL, "Start", M_LevelSelectWarp, 130},
|
||||
};*/
|
||||
{IT_STRING|IT_CVAR, NULL, "Difficulty", &cv_dummygpdifficulty, 40},
|
||||
{IT_STRING|IT_CVAR, NULL, "Encore Mode", &cv_dummygpencore, 50},
|
||||
|
||||
{IT_STRING|IT_CVAR, NULL, "Cup", &cv_dummygpcup, 70},
|
||||
{IT_STRING|IT_CALL, NULL, "Start", M_StartGrandPrix, 80},
|
||||
};
|
||||
|
||||
// Single Player Time Attack
|
||||
static menuitem_t SP_TimeAttackMenu[] =
|
||||
|
|
@ -1805,6 +1815,8 @@ menu_t SP_LevelStatsDef =
|
|||
NULL
|
||||
};
|
||||
|
||||
static menu_t SP_GrandPrixTempDef = DEFAULTMENUSTYLE(NULL, SP_GrandPrixPlaceholderMenu, &MainDef, 60, 30);
|
||||
|
||||
static menu_t SP_TimeAttackDef =
|
||||
{
|
||||
"M_ATTACK",
|
||||
|
|
@ -3369,6 +3381,10 @@ void M_Init(void)
|
|||
//CV_RegisterVar(&cv_dummymares);
|
||||
CV_RegisterVar(&cv_dummystaff);
|
||||
|
||||
CV_RegisterVar(&cv_dummygpdifficulty);
|
||||
CV_RegisterVar(&cv_dummygpencore);
|
||||
CV_RegisterVar(&cv_dummygpcup);
|
||||
|
||||
quitmsg[QUITMSG] = M_GetText("Eggman's tied explosives\nto your girlfriend, and\nwill activate them if\nyou press the 'Y' key!\nPress 'N' to save her!\n\n(Press 'Y' to quit)");
|
||||
quitmsg[QUITMSG1] = M_GetText("What would Tails say if\nhe saw you quitting the game?\n\n(Press 'Y' to quit)");
|
||||
quitmsg[QUITMSG2] = M_GetText("Hey!\nWhere do ya think you're goin'?\n\n(Press 'Y' to quit)");
|
||||
|
|
@ -4214,6 +4230,35 @@ static void M_PatchSkinNameTable(void)
|
|||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// M_PrepareCupList
|
||||
//
|
||||
static void M_PrepareCupList(void)
|
||||
{
|
||||
cupheader_t *cup = kartcupheaders;
|
||||
INT32 i = 0;
|
||||
|
||||
memset(dummygpcup_cons_t, 0, sizeof (dummygpcup_cons_t));
|
||||
|
||||
while (cup != NULL)
|
||||
{
|
||||
dummygpcup_cons_t[i].strvalue = cup->name;
|
||||
dummygpcup_cons_t[i].value = i+1;
|
||||
// this will probably crash or do something stupid at over 50 cups,
|
||||
// but this is all behavior that gets completely overwritten in new-menus, so I'm not worried
|
||||
i++;
|
||||
cup = cup->next;
|
||||
}
|
||||
|
||||
for (; i < 50; i++)
|
||||
{
|
||||
dummygpcup_cons_t[i].strvalue = NULL;
|
||||
dummygpcup_cons_t[i].value = 0;
|
||||
}
|
||||
|
||||
CV_SetValue(&cv_dummygpcup, 1); // This causes crash sometimes?!
|
||||
}
|
||||
|
||||
// Call before showing any level-select menus
|
||||
static void M_PrepareLevelSelect(void)
|
||||
{
|
||||
|
|
@ -6659,6 +6704,8 @@ static void M_Credits(INT32 choice)
|
|||
static void M_SinglePlayerMenu(INT32 choice)
|
||||
{
|
||||
(void)choice;
|
||||
|
||||
SP_MainMenu[spgrandprix].status = IT_CALL|IT_STRING;
|
||||
SP_MainMenu[sptimeattack].status =
|
||||
(M_SecretUnlocked(SECRET_TIMEATTACK)) ? IT_CALL|IT_STRING : IT_SECRET;
|
||||
SP_MainMenu[spbreakthecapsules].status =
|
||||
|
|
@ -7543,6 +7590,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
|
||||
// ===========
|
||||
|
|
|
|||
169
src/p_inter.c
169
src/p_inter.c
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -11406,9 +11406,6 @@ void P_RemoveSavegameMobj(mobj_t *mobj)
|
|||
P_RemoveThinker((thinker_t *)mobj);
|
||||
}
|
||||
|
||||
static CV_PossibleValue_t respawnitemtime_cons_t[] = {{1, "MIN"}, {300, "MAX"}, {0, NULL}};
|
||||
consvar_t cv_itemrespawntime = {"respawnitemtime", "2", CV_NETVAR|CV_CHEAT, respawnitemtime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
|
||||
consvar_t cv_itemrespawn = {"respawnitem", "On", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
|
||||
static CV_PossibleValue_t flagtime_cons_t[] = {{0, "MIN"}, {300, "MAX"}, {0, NULL}};
|
||||
consvar_t cv_flagtime = {"flagtime", "30", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, flagtime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
|
||||
consvar_t cv_suddendeath = {"suddendeath", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
|
||||
|
|
@ -11679,10 +11676,6 @@ void P_RespawnSpecials(void)
|
|||
time = (time * 3) / max(1, mapheaderinfo[gamemap-1]->numlaps);
|
||||
}
|
||||
|
||||
// only respawn items when cv_itemrespawn is on
|
||||
//if (!cv_itemrespawn.value) // TODO: remove this cvar
|
||||
//return;
|
||||
|
||||
// nothing left to respawn?
|
||||
if (iquehead == iquetail)
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ static void P_NetArchivePlayers(void)
|
|||
WRITEFIXED(save_p, players[i].dashspeed);
|
||||
WRITEINT32(save_p, players[i].dashtime);
|
||||
WRITESINT8(save_p, players[i].lives);
|
||||
WRITEUINT8(save_p, players[i].lostlife);
|
||||
WRITESINT8(save_p, players[i].continues);
|
||||
WRITESINT8(save_p, players[i].xtralife);
|
||||
WRITEUINT8(save_p, players[i].gotcontinue);
|
||||
|
|
@ -277,6 +278,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);
|
||||
|
|
@ -330,6 +333,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
|
||||
|
|
@ -460,6 +464,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);
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@
|
|||
#include "k_pwrlv.h"
|
||||
#include "k_waypoint.h"
|
||||
#include "k_bot.h"
|
||||
#include "k_grandprix.h"
|
||||
|
||||
//
|
||||
// Map MD5, calculated on level load.
|
||||
|
|
@ -2415,22 +2416,20 @@ static void P_LevelInitStuff(void)
|
|||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
#if 0
|
||||
if ((netgame || multiplayer) && (gametype == GT_COMPETITION || players[i].lives <= 0))
|
||||
if (grandprixinfo.gp == false)
|
||||
{
|
||||
// In Co-Op, replenish a user's lives if they are depleted.
|
||||
players[i].lives = cv_startinglives.value;
|
||||
players[i].lives = 3;
|
||||
players[i].xtralife = 0;
|
||||
players[i].totalring = 0;
|
||||
}
|
||||
#else
|
||||
players[i].lives = 1; // SRB2Kart
|
||||
#endif
|
||||
|
||||
players[i].realtime = racecountdown = exitcountdown = 0;
|
||||
curlap = bestlap = 0; // SRB2Kart
|
||||
|
||||
players[i].lostlife = false;
|
||||
players[i].gotcontinue = false;
|
||||
|
||||
players[i].xtralife = players[i].deadtimer = players[i].numboxes = players[i].totalring = players[i].laps = 0;
|
||||
players[i].deadtimer = players[i].numboxes = players[i].laps = 0;
|
||||
players[i].health = 1;
|
||||
players[i].aiming = 0;
|
||||
players[i].pflags &= ~PF_TIMEOVER;
|
||||
|
|
@ -2464,8 +2463,23 @@ static void P_LevelInitStuff(void)
|
|||
}
|
||||
|
||||
// SRB2Kart: map load variables
|
||||
if (modeattacking) // Just play it safe and set everything
|
||||
if (grandprixinfo.gp == true)
|
||||
{
|
||||
if (G_BattleGametype())
|
||||
{
|
||||
gamespeed = KARTSPEED_EASY;
|
||||
}
|
||||
else
|
||||
{
|
||||
gamespeed = grandprixinfo.gamespeed;
|
||||
}
|
||||
|
||||
franticitems = false;
|
||||
comeback = true;
|
||||
}
|
||||
else if (modeattacking)
|
||||
{
|
||||
// Just play it safe and set everything
|
||||
gamespeed = KARTSPEED_HARD;
|
||||
franticitems = false;
|
||||
comeback = true;
|
||||
|
|
@ -2490,11 +2504,6 @@ static void P_LevelInitStuff(void)
|
|||
|
||||
memset(&battleovertime, 0, sizeof(struct battleovertime));
|
||||
speedscramble = encorescramble = -1;
|
||||
|
||||
if (!modeattacking)
|
||||
{
|
||||
K_UpdateMatchRaceBots();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -2679,12 +2688,6 @@ static void P_ForceCharacter(const char *forcecharskin)
|
|||
}
|
||||
|
||||
SetPlayerSkin(consoleplayer, forcecharskin);
|
||||
// normal player colors in single player
|
||||
if ((unsigned)cv_playercolor.value != skins[players[consoleplayer].skin].prefcolor && !modeattacking)
|
||||
{
|
||||
CV_StealthSetValue(&cv_playercolor, skins[players[consoleplayer].skin].prefcolor);
|
||||
players[consoleplayer].skincolor = skins[players[consoleplayer].skin].prefcolor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3381,6 +3384,28 @@ boolean P_SetupLevel(boolean skipprecip)
|
|||
}
|
||||
#endif
|
||||
|
||||
// NOW you can try to spawn in the Battle capsules, if there's not enough players for a match
|
||||
K_SpawnBattleCapsules();
|
||||
|
||||
if (grandprixinfo.gp == true)
|
||||
{
|
||||
if (grandprixinfo.initalize == true)
|
||||
{
|
||||
K_InitGrandPrixBots();
|
||||
grandprixinfo.initalize = false;
|
||||
}
|
||||
else if (grandprixinfo.wonround == true)
|
||||
{
|
||||
K_UpdateGrandPrixBots();
|
||||
grandprixinfo.wonround = false;
|
||||
}
|
||||
}
|
||||
else if (!modeattacking)
|
||||
{
|
||||
// We're in a Match Race, use simplistic randomized bots.
|
||||
K_UpdateMatchRaceBots();
|
||||
}
|
||||
|
||||
P_MapEnd();
|
||||
|
||||
// Remove the loading shit from the screen
|
||||
|
|
@ -3432,9 +3457,6 @@ boolean P_SetupLevel(boolean skipprecip)
|
|||
#endif
|
||||
}
|
||||
|
||||
// NOW you can try to spawn in the Battle capsules, if there's not enough players for a match
|
||||
K_SpawnBattleCapsules();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
163
src/p_user.c
163
src/p_user.c
|
|
@ -40,7 +40,6 @@
|
|||
#include "st_stuff.h"
|
||||
#include "lua_script.h"
|
||||
#include "lua_hook.h"
|
||||
#include "k_bot.h"
|
||||
// Objectplace
|
||||
#include "m_cheat.h"
|
||||
// SRB2kart
|
||||
|
|
@ -48,6 +47,8 @@
|
|||
#include "k_kart.h"
|
||||
#include "console.h" // CON_LogMessage
|
||||
#include "k_respawn.h"
|
||||
#include "k_bot.h"
|
||||
#include "k_grandprix.h"
|
||||
|
||||
#ifdef HW3SOUND
|
||||
#include "hardware/hw3sound.h"
|
||||
|
|
@ -949,7 +950,6 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings)
|
|||
return;
|
||||
|
||||
player->kartstuff[k_rings] += num_rings;
|
||||
//player->totalring += num_rings; // Used for GP lives later
|
||||
|
||||
if (player->kartstuff[k_rings] > 20)
|
||||
player->kartstuff[k_rings] = 20; // Caps at 20 rings, sorry!
|
||||
|
|
@ -967,8 +967,8 @@ void P_GivePlayerLives(player_t *player, INT32 numlives)
|
|||
{
|
||||
player->lives += numlives;
|
||||
|
||||
if (player->lives > 99)
|
||||
player->lives = 99;
|
||||
if (player->lives > 9)
|
||||
player->lives = 9;
|
||||
else if (player->lives < 1)
|
||||
player->lives = 1;
|
||||
}
|
||||
|
|
@ -1686,12 +1686,20 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
|
|||
// Player exits the map via sector trigger
|
||||
void P_DoPlayerExit(player_t *player)
|
||||
{
|
||||
const boolean losing = K_IsPlayerLosing(player);
|
||||
|
||||
if (player->exiting || mapreset)
|
||||
return;
|
||||
|
||||
if (P_IsLocalPlayer(player) && (!player->spectator && !demo.playback))
|
||||
legitimateexit = true;
|
||||
|
||||
if (G_GametypeUsesLives() && losing)
|
||||
{
|
||||
// Remove a life from the losing player
|
||||
K_PlayerLoseLife(player);
|
||||
}
|
||||
|
||||
if (G_RaceGametype()) // If in Race Mode, allow
|
||||
{
|
||||
player->exiting = raceexittime+2;
|
||||
|
|
@ -1702,7 +1710,7 @@ void P_DoPlayerExit(player_t *player)
|
|||
if (P_IsDisplayPlayer(player))
|
||||
{
|
||||
sfxenum_t sfx_id;
|
||||
if (K_IsPlayerLosing(player))
|
||||
if (losing)
|
||||
sfx_id = ((skin_t *)player->mo->skin)->soundsid[S_sfx[sfx_klose].skinsound];
|
||||
else
|
||||
sfx_id = ((skin_t *)player->mo->skin)->soundsid[S_sfx[sfx_kwin].skinsound];
|
||||
|
|
@ -1710,7 +1718,7 @@ void P_DoPlayerExit(player_t *player)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (K_IsPlayerLosing(player))
|
||||
if (losing)
|
||||
S_StartSound(player->mo, sfx_klose);
|
||||
else
|
||||
S_StartSound(player->mo, sfx_kwin);
|
||||
|
|
@ -1720,10 +1728,6 @@ void P_DoPlayerExit(player_t *player)
|
|||
if (cv_inttime.value > 0)
|
||||
P_EndingMusic(player);
|
||||
|
||||
// SRB2kart 120217
|
||||
//if (!exitcountdown)
|
||||
//exitcountdown = racecountdown + 8*TICRATE;
|
||||
|
||||
if (P_CheckRacers())
|
||||
player->exiting = raceexittime+1;
|
||||
}
|
||||
|
|
@ -1735,24 +1739,44 @@ void P_DoPlayerExit(player_t *player)
|
|||
else
|
||||
player->exiting = raceexittime+2; // Accidental death safeguard???
|
||||
|
||||
//player->pflags &= ~PF_GLIDING;
|
||||
/* // SRB2kart - don't need
|
||||
if (player->climbing)
|
||||
if (grandprixinfo.gp == true)
|
||||
{
|
||||
player->climbing = 0;
|
||||
player->pflags |= PF_JUMPED;
|
||||
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
|
||||
if (player->bot)
|
||||
{
|
||||
// Bots are going to get harder... :)
|
||||
K_IncreaseBotDifficulty(player);
|
||||
}
|
||||
else if (!losing)
|
||||
{
|
||||
const UINT8 lifethreshold = 20;
|
||||
UINT8 extra = 0;
|
||||
|
||||
// YOU WIN
|
||||
grandprixinfo.wonround = true;
|
||||
|
||||
// Increase your total rings
|
||||
if (RINGTOTAL(player) > 0)
|
||||
{
|
||||
player->totalring += RINGTOTAL(player);
|
||||
|
||||
extra = player->totalring / lifethreshold;
|
||||
|
||||
if (extra > player->xtralife)
|
||||
{
|
||||
P_GivePlayerLives(player, extra - player->xtralife);
|
||||
S_StartSound(NULL, sfx_cdfm73);
|
||||
player->xtralife = extra;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
player->powers[pw_underwater] = 0;
|
||||
player->powers[pw_spacetime] = 0;
|
||||
player->karthud[khud_cardanimation] = 0; // srb2kart: reset battle animation
|
||||
|
||||
if (player == &players[consoleplayer])
|
||||
demo.savebutton = leveltime;
|
||||
|
||||
/*if (playeringame[player-players] && netgame && !circuitmap)
|
||||
CONS_Printf(M_GetText("%s has completed the level.\n"), player_names[player-players]);*/
|
||||
}
|
||||
|
||||
#define SPACESPECIAL 12
|
||||
|
|
@ -4164,23 +4188,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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -7094,14 +7116,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
|
||||
|
|
@ -8355,13 +8372,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)
|
||||
{
|
||||
|
|
@ -8369,8 +8401,6 @@ void P_DoTimeOver(player_t *player)
|
|||
P_DamageMobj(player->mo, NULL, NULL, 10000);
|
||||
}
|
||||
|
||||
player->lives = 0;
|
||||
|
||||
P_EndingMusic(player);
|
||||
|
||||
if (!exitcountdown)
|
||||
|
|
@ -8487,7 +8517,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;
|
||||
}
|
||||
}
|
||||
|
|
@ -8495,24 +8525,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -8526,33 +8558,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8590,17 +8598,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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2920,6 +2920,11 @@ static void Sk_SetDefaultValue(skin_t *skin)
|
|||
strncpy(skin->facewant, "PLAYWANT", 9);
|
||||
strncpy(skin->facemmap, "PLAYMMAP", 9);
|
||||
|
||||
for (i = 0; i < SKINRIVALS; i++)
|
||||
{
|
||||
strcpy(skin->rivals[i], "");
|
||||
}
|
||||
|
||||
skin->starttranscolor = 96;
|
||||
skin->prefcolor = SKINCOLOR_GREEN;
|
||||
|
||||
|
|
@ -3190,6 +3195,45 @@ void R_AddSkins(UINT16 wadnum)
|
|||
strupr(value);
|
||||
strncpy(skin->facemmap, value, sizeof skin->facemmap);
|
||||
}
|
||||
else if (!stricmp(stoken, "rivals"))
|
||||
{
|
||||
size_t len = strlen(value);
|
||||
size_t i;
|
||||
char rivalname[SKINNAMESIZE] = "";
|
||||
UINT8 pos = 0;
|
||||
UINT8 numrivals = 0;
|
||||
|
||||
// Can't use strtok, because this function's already using it.
|
||||
// Using it causes it to upset the saved pointer,
|
||||
// corrupting the reading for the rest of the file.
|
||||
|
||||
// So instead we get to crawl through the value, character by character,
|
||||
// and write it down as we go, until we hit a comma or the end of the string.
|
||||
// Yaaay.
|
||||
|
||||
for (i = 0; i <= len; i++)
|
||||
{
|
||||
if (numrivals >= SKINRIVALS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (value[i] == ',' || i == len)
|
||||
{
|
||||
STRBUFCPY(skin->rivals[numrivals], rivalname);
|
||||
strlwr(skin->rivals[numrivals]);
|
||||
numrivals++;
|
||||
|
||||
memset(rivalname, 0, sizeof (rivalname));
|
||||
pos = 0;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
rivalname[pos] = value[i];
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
#define FULLPROCESS(field) else if (!stricmp(stoken, #field)) skin->field = get_number(value);
|
||||
// character type identification
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
190
src/y_inter.c
190
src/y_inter.c
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue