Merge branch 'master' of https://git.do.srb2.org/KartKrew/Kart into conditions-cascading

# Conflicts:
#	src/k_grandprix.c
#	src/k_grandprix.h
This commit is contained in:
toaster 2023-03-07 19:37:46 +00:00
commit d2c36c952a
71 changed files with 3514 additions and 571 deletions

View file

@ -64,6 +64,8 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
tables.c
r_bsp.c
r_data.c
r_debug.cpp
r_debug_parser.cpp
r_draw.c
r_fps.c
r_main.c
@ -134,6 +136,8 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
k_profiles.c
k_specialstage.c
k_roulette.c
k_podium.c
k_rank.c
)
if(SRB2_CONFIG_ENABLE_WEBM_MOVIES)

View file

@ -50,6 +50,7 @@ extern "C" {
#include "../m_cond.h"
#include "../r_skins.h"
#include "../k_battle.h"
#include "../k_podium.h"
}
#include "call-funcs.hpp"
@ -445,6 +446,25 @@ bool CallFunc_PolyWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Wo
return true; // Execution interrupted
}
/*--------------------------------------------------
bool CallFunc_CameraWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Pauses the thread until the tagged
camera is done moving.
--------------------------------------------------*/
bool CallFunc_CameraWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{
(void)argC;
thread->state = {
ACSVM::ThreadState::WaitTag,
argV[0],
ACS_TAGTYPE_CAMERA
};
return true; // Execution interrupted
}
/*--------------------------------------------------
bool CallFunc_ChangeFloor(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
@ -1335,3 +1355,59 @@ bool CallFunc_EncoreMode(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::
thread->dataStk.push(encoremode);
return false;
}
/*--------------------------------------------------
bool CallFunc_PodiumPosition(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Returns the best position of all non-CPU players.
--------------------------------------------------*/
bool CallFunc_PodiumPosition(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{
UINT8 ret = MAXPLAYERS;
INT32 i;
(void)argV;
(void)argC;
for (i = 0; i < MAXPLAYERS; i++)
{
player_t *player = NULL;
if (playeringame[i] == false)
{
continue;
}
player = &players[i];
if (player->spectator == true)
{
continue;
}
if (player->bot == true)
{
continue;
}
ret = std::min(ret, player->position);
}
thread->dataStk.push(ret);
return false;
}
/*--------------------------------------------------
bool CallFunc_PodiumFinish(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Ends the podium sequence. Doesn't do anything
outside of podium maps.
--------------------------------------------------*/
bool CallFunc_PodiumFinish(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{
(void)argV;
(void)argC;
K_FinishCeremony();
return false;
}

View file

@ -50,6 +50,7 @@ bool CallFunc_Random(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word
bool CallFunc_ThingCount(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_TagWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_PolyWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_CameraWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_ChangeFloor(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_ChangeCeiling(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_LineSide(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
@ -84,4 +85,7 @@ bool CallFunc_PlayerLap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::W
bool CallFunc_LowestLap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_EncoreMode(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_PodiumPosition(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_PodiumFinish(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
#endif // __SRB2_ACS_CALL_FUNCS_HPP__

View file

@ -36,6 +36,7 @@ extern "C" {
#include "../p_spec.h"
#include "../w_wad.h"
#include "../z_zone.h"
#include "../p_local.h"
}
#include "environment.hpp"
@ -160,6 +161,10 @@ Environment::Environment()
addFuncDataACS0( 307, addCallFunc(CallFunc_PlayerLap));
addFuncDataACS0( 308, addCallFunc(CallFunc_LowestLap));
addFuncDataACS0( 309, addCallFunc(CallFunc_EncoreMode));
addFuncDataACS0( 500, addCallFunc(CallFunc_CameraWait));
addFuncDataACS0( 501, addCallFunc(CallFunc_PodiumPosition));
addFuncDataACS0( 502, addCallFunc(CallFunc_PodiumFinish));
}
ACSVM::Thread *Environment::allocThread()
@ -262,6 +267,17 @@ bool Environment::checkTag(ACSVM::Word type, ACSVM::Word tag)
const polyobj_t *po = Polyobj_GetForNum(tag);
return (po == nullptr || po->thinker == nullptr);
}
case ACS_TAGTYPE_CAMERA:
{
const mobj_t *camera = P_FindObjectTypeFromTag(MT_ALTVIEWMAN, tag);
if (camera == nullptr || camera->spawnpoint == nullptr)
{
return true;
}
return (camera->tracer == nullptr || P_MobjWasRemoved(camera->tracer) == true);
}
}
return true;

View file

@ -57,6 +57,7 @@ enum acs_tagType_e
{
ACS_TAGTYPE_POLYOBJ,
ACS_TAGTYPE_SECTOR,
ACS_TAGTYPE_CAMERA,
};
class ThreadInfo : public ACSVM::ThreadInfo

View file

@ -1839,6 +1839,25 @@ static void CON_DrawConsole(void)
// Console refresh drawer, call each frame
//
static boolean CON_GamestateDrawHudLines(void)
{
switch (gamestate)
{
case GS_LEVEL:
case GS_INTERMISSION:
case GS_VOTING:
case GS_CUTSCENE:
case GS_CREDITS:
case GS_EVALUATION:
case GS_WAITINGPLAYERS:
case GS_CEREMONY:
return true;
default:
return false;
}
}
void CON_Drawer(void)
{
Lock_state();
@ -1858,8 +1877,7 @@ void CON_Drawer(void)
if (con_curlines > 0)
CON_DrawConsole();
else if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_CUTSCENE || gamestate == GS_CREDITS
|| gamestate == GS_VOTING || gamestate == GS_EVALUATION || gamestate == GS_WAITINGPLAYERS)
else if (CON_GamestateDrawHudLines() == true)
CON_DrawHudlines();
Unlock_state();

View file

@ -2,6 +2,7 @@
#define __SRB2_CXXUTIL_HPP__
#include <cstdlib>
#include <functional>
#include <type_traits>
#include <utility>

View file

@ -1272,7 +1272,7 @@ static void CL_LoadReceivedSavegame(boolean reloading)
paused = false;
demo.playback = false;
demo.title = false;
titlemapinaction = TITLEMAP_OFF;
titlemapinaction = false;
automapactive = false;
// load a base level
@ -2542,7 +2542,7 @@ void CL_ClearPlayer(INT32 playernum)
int i;
// Handle mobj_t pointers.
if (gamestate == GS_LEVEL)
if (G_GamestateUsesLevel() == true)
{
if (players[playernum].follower)
{
@ -2557,10 +2557,11 @@ void CL_ClearPlayer(INT32 playernum)
P_SetTarget(&players[playernum].skybox.viewpoint, NULL);
P_SetTarget(&players[playernum].skybox.centerpoint, NULL);
P_SetTarget(&players[playernum].awayviewmobj, NULL);
P_SetTarget(&players[playernum].awayview.mobj, NULL);
P_SetTarget(&players[playernum].followmobj, NULL);
P_SetTarget(&players[playernum].hoverhyudoro, NULL);
P_SetTarget(&players[playernum].stumbleIndicator, NULL);
P_SetTarget(&players[playernum].sliptideZipIndicator, NULL);
}
// Handle parties.
@ -3765,9 +3766,6 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
sprintf(player_names[newplayernum], "%s", skins[skinnum].realname);
SetPlayerSkinByNum(newplayernum, skinnum);
players[newplayernum].spectator = !(gametyperules & GTR_BOTS)
|| (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE);
if (netgame)
{
HU_AddChatText(va("\x82*Bot %d has been added to the game", newplayernum+1), false);

View file

@ -76,6 +76,7 @@
#include "m_random.h" // P_ClearRandom
#include "k_specialstage.h"
#include "acs/interface.h"
#include "k_podium.h"
#ifdef HWRENDER
#include "hardware/hw_main.h" // 3D View Rendering
@ -178,6 +179,7 @@ boolean capslock = 0; // gee i wonder what this does.
void D_ProcessEvents(void)
{
event_t *ev;
int i;
boolean eaten;
boolean menuresponse = false;
@ -251,6 +253,12 @@ void D_ProcessEvents(void)
{
M_MapMenuControls(NULL);
}
// Update menu CMD
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
M_UpdateMenuCMD(i);
}
}
//
@ -337,8 +345,8 @@ static void D_Display(void)
if (rendermode != render_none)
{
// Fade to black first
if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction)) // fades to black on its own timing, always
&& wipetypepre != UINT8_MAX)
if (G_GamestateUsesLevel() == false // fades to black on its own timing, always
&& wipetypepre != UINT8_MAX)
{
F_WipeStartScreen();
F_WipeColorFill(31);
@ -346,7 +354,7 @@ static void D_Display(void)
F_RunWipe(wipedefindex, wipetypepre, gamestate != GS_MENU, "FADEMAP0", false, false);
}
if (gamestate != GS_LEVEL && rendermode != render_none)
if (G_GamestateUsesLevel() == false && rendermode != render_none)
{
V_SetPaletteLump("PLAYPAL"); // Reset the palette
R_ReInitColormaps(0, NULL, 0);
@ -399,6 +407,13 @@ static void D_Display(void)
HU_Drawer();
break;
case GS_CEREMONY:
if (!gametic)
break;
HU_Erase();
HU_Drawer();
break;
case GS_MENU:
break;
@ -463,7 +478,7 @@ static void D_Display(void)
// clean up border stuff
// see if the border needs to be initially drawn
if (gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction && curbghide && (!hidetitlemap)))
if (G_GamestateUsesLevel() == true)
{
if (!automapactive && !dedicated && cv_renderview.value)
{
@ -559,14 +574,30 @@ static void D_Display(void)
ps_uitime = I_GetPreciseTime();
if (gamestate == GS_LEVEL)
switch (gamestate)
{
ST_Drawer();
F_TextPromptDrawer();
HU_Drawer();
case GS_LEVEL:
{
ST_Drawer();
F_TextPromptDrawer();
HU_Drawer();
break;
}
case GS_TITLESCREEN:
{
F_TitleScreenDrawer();
break;
}
case GS_CEREMONY:
{
K_CeremonyDrawer();
break;
}
default:
{
break;
}
}
else
F_TitleScreenDrawer();
}
else
{
@ -576,7 +607,7 @@ static void D_Display(void)
// change gamma if needed
// (GS_LEVEL handles this already due to level-specific palettes)
if (forcerefresh && !(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction)))
if (forcerefresh && G_GamestateUsesLevel() == false)
V_SetPalette(0);
// draw pause pic
@ -745,17 +776,6 @@ void D_SRB2Loop(void)
because I_FinishUpdate was called afterward
*/
#if 0
/* Smells like a hack... Don't fade Sonic's ass into the title screen. */
if (gamestate != GS_TITLESCREEN)
{
static lumpnum_t gstartuplumpnum = W_CheckNumForName("STARTUP");
if (gstartuplumpnum == LUMPERROR)
gstartuplumpnum = W_GetNumForName("MISSING");
V_DrawScaledPatch(0, 0, 0, W_CachePatchNum(gstartuplumpnum, PU_PATCH));
}
#endif
for (;;)
{
// capbudget is the minimum precise_t duration of a single loop iteration

View file

@ -453,6 +453,7 @@ consvar_t cv_kartdebugcolorize = CVAR_INIT ("debugcolorize", "Off", CV_CHEAT, CV
consvar_t cv_kartdebugdirector = CVAR_INIT ("debugdirector", "Off", CV_CHEAT, CV_OnOff, NULL);
consvar_t cv_spbtest = CVAR_INIT ("spbtest", "Off", CV_CHEAT|CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_gptest = CVAR_INIT ("gptest", "Off", CV_CHEAT|CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_debugrank = CVAR_INIT ("debugrank", "Off", CV_CHEAT, CV_OnOff, NULL);
static CV_PossibleValue_t capsuletest_cons_t[] = {
{CV_CAPSULETEST_OFF, "Off"},
@ -5330,14 +5331,22 @@ static void Command_Mapmd5_f(void)
static void Command_ExitLevel_f(void)
{
if (!(netgame || multiplayer) && !CV_CheatsEnabled())
CONS_Printf(M_GetText("This only works in a netgame.\n"));
else if (!(server || (IsPlayerAdmin(consoleplayer))))
if (!(server || (IsPlayerAdmin(consoleplayer))))
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
}
else if (K_CanChangeRules(false) == false && CV_CheatsEnabled() == false)
{
CONS_Printf(M_GetText("This cannot be used without cheats enabled.\n"));
}
else if (( gamestate != GS_LEVEL && gamestate != GS_CREDITS ) || demo.playback)
{
CONS_Printf(M_GetText("You must be in a level to use this.\n"));
}
else
{
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
}
static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum)

View file

@ -96,6 +96,7 @@ extern consvar_t cv_kartdebugitem, cv_kartdebugamount, cv_kartdebugdistribution,
extern consvar_t cv_kartdebugnodes, cv_kartdebugcolorize, cv_kartdebugdirector;
extern consvar_t cv_spbtest, cv_gptest, cv_reducevfx;
extern consvar_t cv_kartdebugwaypoints, cv_kartdebugbotpredict;
extern consvar_t cv_debugrank;
typedef enum {
CV_CAPSULETEST_OFF,

View file

@ -404,6 +404,13 @@ typedef struct {
boolean flip;
} sonicloopvars_t;
// player_t struct for all alternative viewpoint variables
struct altview_t
{
mobj_t *mobj;
INT32 tics;
};
// ========================================================================
// PLAYER STRUCTURE
// ========================================================================
@ -647,6 +654,7 @@ struct player_t
tic_t realtime; // integer replacement for leveltime
UINT8 laps; // Number of laps (optional)
UINT8 latestlap;
UINT32 lapPoints; // Points given from laps
INT32 starpostnum; // The number of the last starpost you hit
UINT8 ctfteam; // 0 == Spectator, 1 == Red, 2 == Blue
@ -664,9 +672,7 @@ struct player_t
INT32 onconveyor; // You are on a conveyor belt if nonzero
mobj_t *awayviewmobj;
INT32 awayviewtics;
angle_t awayviewaiming; // Used for cut-away view
altview_t awayview;
boolean spectator;
tic_t spectatewait; // reimplementable as UINT8 queue - How long have you been waiting as a spectator
@ -695,7 +701,12 @@ struct player_t
UINT8 tripwireReboundDelay; // When failing Tripwire, brieftly lock out speed-based tripwire pass (anti-cheese)
UINT16 sliptideZip; // How long is our chained sliptide? Grant a proportional boost when it's over.
UINT8 sliptideZipDelay; // How long since the last sliptide? Only boost once you've been straightened out for a bit.
UINT16 sliptideZipBoost; // The actual boost granted from sliptideZip.
mobj_t *stumbleIndicator;
mobj_t *sliptideZipIndicator;
#ifdef HWRENDER
fixed_t fovadd; // adjust FOV for hw rendering

View file

@ -2767,6 +2767,10 @@ void readmaincfg(MYFILE *f, boolean mainfile)
INT32 value;
boolean doClearLevels = false;
#ifdef DEVELOP
(void)mainfile;
#endif
do
{
if (myfgets(s, MAXLINELEN, f))
@ -2842,10 +2846,12 @@ void readmaincfg(MYFILE *f, boolean mainfile)
M_ClearStats();
M_ClearSecrets();
}
#ifndef DEVELOP
else if (!mainfile && !gamedataadded)
{
deh_warning("You must define a custom gamedata to use \"%s\"", word);
}
#endif
else if (fastcmp(word, "CLEARLEVELS"))
{
doClearLevels = (UINT8)(value == 0 || word2[0] == 'F' || word2[0] == 'N');
@ -2953,6 +2959,7 @@ void readmaincfg(MYFILE *f, boolean mainfile)
}
else if (fastcmp(word, "TITLEMAP"))
{
Z_Free(titlemap);
titlemap = Z_StrDup(word2);
titlechanged = true;
}
@ -3047,13 +3054,20 @@ void readmaincfg(MYFILE *f, boolean mainfile)
}
else if (fastcmp(word, "BOOTMAP"))
{
Z_Free(bootmap);
bootmap = Z_StrDup(word2);
//titlechanged = true;
}
else if (fastcmp(word, "TUTORIALMAP"))
{
Z_Free(tutorialmap);
tutorialmap = Z_StrDup(word2);
}
else if (fastcmp(word, "PODIUMMAP"))
{
Z_Free(podiummap);
podiummap = Z_StrDup(word2);
}
else
deh_warning("Maincfg: unknown word '%s'", word);
}
@ -3192,6 +3206,14 @@ void readwipes(MYFILE *f)
else if (fastcmp(pword, "FINAL"))
wipeoffset = wipe_gameend_final;
}
else if (fastncmp(word, "CEREMONY_", 9))
{
pword = word + 9;
if (fastcmp(pword, "TOBLACK"))
wipeoffset = wipe_ceremony_toblack;
else if (fastcmp(pword, "FINAL"))
wipeoffset = wipe_ceremony_final;
}
else if (fastncmp(word, "ENCORE_", 7))
{
pword = word + 7;

View file

@ -3304,6 +3304,8 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_MAGICIANBOXTOP",
"S_MAGICIANBOXBOTTOM",
"S_SLIPTIDEZIP",
// Signpost sparkles
"S_SIGNSPARK1",
"S_SIGNSPARK2",
@ -5323,6 +5325,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_MONITOR_PART",
"MT_MONITOR_SHARD",
"MT_MAGICIANBOX",
"MT_SLIPTIDEZIP",
"MT_SIGNSPARKLE",
@ -6303,6 +6307,7 @@ struct int_const_s const INT_CONST[] = {
{"RF_FULLDARK",RF_FULLDARK},
{"RF_SEMIBRIGHT",RF_SEMIBRIGHT},
{"RF_NOCOLORMAPS",RF_NOCOLORMAPS},
{"RF_ALWAYSONTOP",RF_ALWAYSONTOP},
{"RF_SPRITETYPEMASK",RF_SPRITETYPEMASK},
{"RF_PAPERSPRITE",RF_PAPERSPRITE},
{"RF_FLOORSPRITE",RF_FLOORSPRITE},

View file

@ -212,6 +212,8 @@ extern char * bootmap; //bootmap for loading a map on startup
extern char * tutorialmap; // map to load for tutorial
extern boolean tutorialmode; // are we in a tutorial right now?
extern char * podiummap; // map to load for podium
extern boolean looptitle;
// CTF colors.
@ -697,7 +699,6 @@ extern UINT8 gamespeed;
extern boolean franticitems;
extern boolean encoremode, prevencoremode;
extern UINT32 g_hiscore;
extern tic_t wantedcalcdelay;
extern tic_t itemCooldowns[NUMKARTITEMS - 1];
extern tic_t mapreset;

View file

@ -44,13 +44,14 @@
// SRB2Kart
#include "k_menu.h"
#include "k_grandprix.h"
// Stage of animation:
// 0 = text, 1 = art screen
INT32 finalecount;
INT32 titlescrollxspeed = 16;
INT32 titlescrollyspeed = 0;
UINT8 titlemapinaction = TITLEMAP_OFF;
boolean titlemapinaction = false;
static INT32 timetonext; // Delay between screen changes
@ -62,7 +63,7 @@ static tic_t stoptimer;
static boolean keypressed = false;
static INT32 menuanimtimer; // Title screen: background animation timing
mobj_t *titlemapcameraref = NULL;
altview_t titlemapcam = {0};
// menu presentation state
char curbgname[9];
@ -1835,14 +1836,14 @@ void F_StartTitleScreen(void)
mapthing_t *startpos;
gamestate_t prevwipegamestate = wipegamestate;
titlemapinaction = TITLEMAP_LOADING;
titlemapcameraref = NULL;
titlemapinaction = true;
P_SetTarget(&titlemapcam.mobj, NULL);
gamemap = titleMapNum+1;
maptol = mapheaderinfo[titleMapNum]->typeoflevel;
globalweather = mapheaderinfo[titleMapNum]->weather;
G_DoLoadLevel(true);
G_DoLoadLevelEx(true, GS_TITLESCREEN);
if (!titlemap)
return;
@ -1878,13 +1879,12 @@ void F_StartTitleScreen(void)
}
else
{
titlemapinaction = TITLEMAP_OFF;
G_SetGamestate(GS_TITLESCREEN);
titlemapinaction = false;
gamemap = 1; // g_game.c
CON_ClearHUD();
}
G_SetGamestate(GS_TITLESCREEN);
// IWAD dependent stuff.
animtimer = skullAnimCounter = 0;
@ -2139,7 +2139,7 @@ void F_TitleScreenTicker(boolean run)
mobj_t *cameraref = NULL;
// If there's a Line 422 Switch Cut-Away view, don't force us.
if (!titlemapcameraref || titlemapcameraref->type != MT_ALTVIEWMAN)
if (titlemapcam.mobj == NULL || titlemapcam.mobj->type != MT_ALTVIEWMAN)
{
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
@ -2154,20 +2154,27 @@ void F_TitleScreenTicker(boolean run)
if (mo2->type != MT_ALTVIEWMAN)
continue;
cameraref = titlemapcameraref = mo2;
cameraref = mo2;
break;
}
if (cameraref != NULL)
{
P_SetTarget(&titlemapcam.mobj, cameraref);
}
}
else
cameraref = titlemapcameraref;
{
cameraref = titlemapcam.mobj;
}
if (cameraref)
if (cameraref != NULL)
{
camera[0].x = cameraref->x;
camera[0].y = cameraref->y;
camera[0].z = cameraref->z;
camera[0].angle = cameraref->angle;
camera[0].aiming = cameraref->cusval;
camera[0].aiming = cameraref->pitch;
camera[0].subsector = cameraref->subsector;
}
else

View file

@ -104,17 +104,10 @@ extern INT16 ttloop;
extern UINT16 tttics;
extern boolean ttavailable[6];
typedef enum
{
TITLEMAP_OFF = 0,
TITLEMAP_LOADING,
TITLEMAP_RUNNING
} titlemap_enum;
// Current menu parameters
extern mobj_t *titlemapcameraref;
extern altview_t titlemapcam;
extern char curbgname[9];
extern SINT8 curfadevalue;
extern INT32 curbgcolor;
@ -184,6 +177,7 @@ enum
wipe_credits_toblack,
wipe_evaluation_toblack,
wipe_gameend_toblack,
wipe_ceremony_toblack,
wipe_intro_toblack,
wipe_ending_toblack,
wipe_cutscene_toblack,
@ -202,6 +196,7 @@ enum
wipe_credits_final,
wipe_evaluation_final,
wipe_gameend_final,
wipe_ceremony_final,
wipe_intro_final,
wipe_ending_final,
wipe_cutscene_final,

View file

@ -64,6 +64,7 @@ UINT8 wipedefs[NUMWIPEDEFS] = {
99, // wipe_credits_toblack
0, // wipe_evaluation_toblack
0, // wipe_gameend_toblack
0, // wipe_ceremony_toblack
UINT8_MAX, // wipe_intro_toblack (hardcoded)
99, // wipe_ending_toblack (hardcoded)
99, // wipe_cutscene_toblack (hardcoded)
@ -80,6 +81,7 @@ UINT8 wipedefs[NUMWIPEDEFS] = {
99, // wipe_credits_final
0, // wipe_evaluation_final
0, // wipe_gameend_final
0, // wipe_ceremony_final
99, // wipe_intro_final (hardcoded)
99, // wipe_ending_final (hardcoded)
99 // wipe_cutscene_final (hardcoded)
@ -88,15 +90,16 @@ UINT8 wipedefs[NUMWIPEDEFS] = {
static boolean g_wipedef_toblack[NUMWIPEDEFS] = {
true, // wipe_credits_intermediate (0)
true, // wipe_level_toblack
true, // wipe_intermission_toblack
true, // wipe_voting_toblack,
true, // wipe_continuing_toblack
true, // wipe_titlescreen_toblack
true, // wipe_menu_toblack
true, // wipe_level_toblack
true, // wipe_intermission_toblack
true, // wipe_voting_toblack,
true, // wipe_continuing_toblack
true, // wipe_titlescreen_toblack
true, // wipe_menu_toblack
true, // wipe_credits_toblack
true, // wipe_evaluation_toblack
true, // wipe_gameend_toblack
true, // wipe_evaluation_toblack
true, // wipe_gameend_toblack
true, // wipe_ceremony_toblack
true, // wipe_intro_toblack (hardcoded)
true, // wipe_ending_toblack (hardcoded)
true, // wipe_cutscene_toblack (hardcoded)
@ -105,14 +108,15 @@ static boolean g_wipedef_toblack[NUMWIPEDEFS] = {
false, // wipe_encore_towhite
true, // wipe_level_final
true, // wipe_intermission_final
true, // wipe_voting_final
true, // wipe_continuing_final
true, // wipe_titlescreen_final
true, // wipe_menu_final
true, // wipe_intermission_final
true, // wipe_voting_final
true, // wipe_continuing_final
true, // wipe_titlescreen_final
true, // wipe_menu_final
true, // wipe_credits_final
true, // wipe_evaluation_final
true, // wipe_gameend_final
true, // wipe_evaluation_final
true, // wipe_gameend_final
true, // wipe_ceremony_final
true, // wipe_intro_final (hardcoded)
true, // wipe_ending_final (hardcoded)
true // wipe_cutscene_final (hardcoded)
@ -121,15 +125,16 @@ static boolean g_wipedef_toblack[NUMWIPEDEFS] = {
static boolean g_wipedef_toinvert[NUMWIPEDEFS] = {
false, // wipe_credits_intermediate (0)
false, // wipe_level_toblack
false, // wipe_intermission_toblack
false, // wipe_voting_toblack,
false, // wipe_continuing_toblack
false, // wipe_titlescreen_toblack
false, // wipe_menu_toblack
false, // wipe_level_toblack
false, // wipe_intermission_toblack
false, // wipe_voting_toblack,
false, // wipe_continuing_toblack
false, // wipe_titlescreen_toblack
false, // wipe_menu_toblack
false, // wipe_credits_toblack
false, // wipe_evaluation_toblack
false, // wipe_gameend_toblack
false, // wipe_evaluation_toblack
false, // wipe_gameend_toblack
false, // wipe_ceremony_toblack
false, // wipe_intro_toblack (hardcoded)
false, // wipe_ending_toblack (hardcoded)
false, // wipe_cutscene_toblack (hardcoded)
@ -138,14 +143,15 @@ static boolean g_wipedef_toinvert[NUMWIPEDEFS] = {
false, // wipe_encore_towhite
false, // wipe_level_final
false, // wipe_intermission_final
false, // wipe_voting_final
false, // wipe_continuing_final
false, // wipe_titlescreen_final
false, // wipe_menu_final
false, // wipe_intermission_final
false, // wipe_voting_final
false, // wipe_continuing_final
false, // wipe_titlescreen_final
false, // wipe_menu_final
false, // wipe_credits_final
false, // wipe_evaluation_final
false, // wipe_gameend_final
false, // wipe_evaluation_final
false, // wipe_gameend_final
false, // wipe_ceremony_final
false, // wipe_intro_final (hardcoded)
false, // wipe_ending_final (hardcoded)
false // wipe_cutscene_final (hardcoded)
@ -154,15 +160,16 @@ static boolean g_wipedef_toinvert[NUMWIPEDEFS] = {
static boolean g_wipedef_towhite[NUMWIPEDEFS] = {
false, // wipe_credits_intermediate (0)
false, // wipe_level_toblack
false, // wipe_intermission_toblack
false, // wipe_voting_toblack,
false, // wipe_continuing_toblack
false, // wipe_titlescreen_toblack
false, // wipe_menu_toblack
false, // wipe_level_toblack
false, // wipe_intermission_toblack
false, // wipe_voting_toblack,
false, // wipe_continuing_toblack
false, // wipe_titlescreen_toblack
false, // wipe_menu_toblack
false, // wipe_credits_toblack
false, // wipe_evaluation_toblack
false, // wipe_gameend_toblack
false, // wipe_evaluation_toblack
false, // wipe_gameend_toblack
false, // wipe_ceremony_toblack
false, // wipe_intro_toblack (hardcoded)
false, // wipe_ending_toblack (hardcoded)
false, // wipe_cutscene_toblack (hardcoded)
@ -171,14 +178,15 @@ static boolean g_wipedef_towhite[NUMWIPEDEFS] = {
true, // wipe_encore_towhite
false, // wipe_level_final
false, // wipe_intermission_final
false, // wipe_voting_final
false, // wipe_continuing_final
false, // wipe_titlescreen_final
false, // wipe_menu_final
false, // wipe_intermission_final
false, // wipe_voting_final
false, // wipe_continuing_final
false, // wipe_titlescreen_final
false, // wipe_menu_final
false, // wipe_credits_final
false, // wipe_evaluation_final
false, // wipe_gameend_final
false, // wipe_evaluation_final
false, // wipe_gameend_final
false, // wipe_ceremony_final
false, // wipe_intro_final (hardcoded)
false, // wipe_ending_final (hardcoded)
false // wipe_cutscene_final (hardcoded)
@ -187,15 +195,16 @@ static boolean g_wipedef_towhite[NUMWIPEDEFS] = {
static boolean g_wipedef_crossfade[NUMWIPEDEFS] = {
false, // wipe_credits_intermediate (0)
false, // wipe_level_toblack
false, // wipe_intermission_toblack
false, // wipe_voting_toblack,
false, // wipe_continuing_toblack
false, // wipe_titlescreen_toblack
false, // wipe_menu_toblack
false, // wipe_level_toblack
false, // wipe_intermission_toblack
false, // wipe_voting_toblack,
false, // wipe_continuing_toblack
false, // wipe_titlescreen_toblack
false, // wipe_menu_toblack
false, // wipe_credits_toblack
false, // wipe_evaluation_toblack
false, // wipe_gameend_toblack
false, // wipe_evaluation_toblack
false, // wipe_gameend_toblack
false, // wipe_ceremony_toblack
false, // wipe_intro_toblack (hardcoded)
false, // wipe_ending_toblack (hardcoded)
false, // wipe_cutscene_toblack (hardcoded)
@ -204,14 +213,15 @@ static boolean g_wipedef_crossfade[NUMWIPEDEFS] = {
false, // wipe_encore_towhite
true, // wipe_level_final
true, // wipe_intermission_final
true, // wipe_voting_final
true, // wipe_continuing_final
true, // wipe_titlescreen_final
true, // wipe_menu_final
true, // wipe_intermission_final
true, // wipe_voting_final
true, // wipe_continuing_final
true, // wipe_titlescreen_final
true, // wipe_menu_final
true, // wipe_credits_final
true, // wipe_evaluation_final
true, // wipe_gameend_final
true, // wipe_evaluation_final
true, // wipe_gameend_final
true, // wipe_ceremony_final
true, // wipe_intro_final (hardcoded)
true, // wipe_ending_final (hardcoded)
true // wipe_cutscene_final (hardcoded)

View file

@ -62,6 +62,8 @@
#include "k_bot.h"
#include "doomstat.h"
#include "k_director.h"
#include "k_podium.h"
#include "k_rank.h"
#ifdef HAVE_DISCORDRPC
#include "discord.h"
@ -164,6 +166,8 @@ char * bootmap = NULL; //bootmap for loading a map on startup
char * tutorialmap = NULL; // map to load for tutorial
boolean tutorialmode = false; // are we in a tutorial right now?
char * podiummap = NULL; // map to load for podium
boolean looptitle = true;
UINT16 skincolor_redteam = SKINCOLOR_RED;
@ -298,7 +302,6 @@ SINT8 votes[MAXPLAYERS]; // Each player's vote
SINT8 pickedvote; // What vote the host rolls
// Server-sided, synched variables
UINT32 g_hiscore; // Highest score (points) achieved by anyone in game
tic_t wantedcalcdelay; // Time before it recalculates WANTED
tic_t itemCooldowns[NUMKARTITEMS - 1]; // Cooldowns to prevent item spawning
tic_t mapreset; // Map reset delay when enough players have joined an empty game
@ -1449,9 +1452,9 @@ static void weaponPrefChange4(void)
}
//
// G_DoLoadLevel
// G_DoLoadLevelEx
//
void G_DoLoadLevel(boolean resetplayer)
void G_DoLoadLevelEx(boolean resetplayer, gamestate_t newstate)
{
boolean doAutomate = false;
INT32 i;
@ -1461,7 +1464,7 @@ void G_DoLoadLevel(boolean resetplayer)
levelstarttic = gametic; // for time calculation
if (wipegamestate == GS_LEVEL)
if (wipegamestate == newstate)
wipegamestate = -1; // force a wipe
if (cv_currprofile.value == -1 && !demo.playback)
@ -1478,31 +1481,33 @@ void G_DoLoadLevel(boolean resetplayer)
Y_EndVote();
// cleanup
if (titlemapinaction == TITLEMAP_LOADING)
// Is this actually necessary? Doesn't F_StartTitleScreen already do a significantly more comprehensive check?
if (newstate == GS_TITLESCREEN)
{
//if (W_CheckNumForName(G_BuildMapName(gamemap)) == LUMPERROR)
if (gamemap < 1 || gamemap > nummapheaders)
{
G_SetGamestate(GS_TITLESCREEN);
titlemapinaction = false;
Z_Free(titlemap);
titlemap = NULL; // let's not infinite recursion ok
Command_ExitGame_f();
return;
}
titlemapinaction = TITLEMAP_RUNNING;
}
else
titlemapinaction = TITLEMAP_OFF;
// Doing this matches HOSTMOD behavior.
// Is that desired? IDK
doAutomate = (gamestate != GS_LEVEL);
doAutomate = (gamestate != GS_LEVEL && newstate == GS_LEVEL);
G_SetGamestate(GS_LEVEL);
G_SetGamestate(newstate);
if (wipegamestate == GS_MENU)
M_ClearMenus(true);
I_UpdateMouseGrab();
K_ResetCeremony();
for (i = 0; i < MAXPLAYERS; i++)
{
if (resetplayer || (playeringame[i] && players[i].playerstate == PST_DEAD))
@ -1543,6 +1548,11 @@ void G_DoLoadLevel(boolean resetplayer)
}
}
void G_DoLoadLevel(boolean resetplayer)
{
G_DoLoadLevelEx(resetplayer, GS_LEVEL);
}
//
// Start the title card.
//
@ -1573,7 +1583,7 @@ void G_StartTitleCard(void)
}
// start the title card
WipeStageTitle = (!titlemapinaction);
WipeStageTitle = (gamestate == GS_LEVEL);
}
//
@ -1717,6 +1727,21 @@ boolean G_Responder(event_t *ev)
return true;
}
}
else if (gamestate == GS_CEREMONY)
{
if (HU_Responder(ev))
{
hu_keystrokes = true;
return true; // chat ate the event
}
if (K_CeremonyResponder(ev))
{
nextmap = NEXTMAP_TITLE;
G_EndGame();
return true;
}
}
else if (gamestate == GS_CONTINUING)
{
return true;
@ -1727,11 +1752,13 @@ boolean G_Responder(event_t *ev)
return true;
}
else if (gamestate == GS_INTERMISSION || gamestate == GS_VOTING || gamestate == GS_EVALUATION)
{
if (HU_Responder(ev))
{
hu_keystrokes = true;
return true; // chat ate the event
}
}
if (gamestate == GS_LEVEL && ev->type == ev_keydown && multiplayer && demo.playback && !demo.freecam)
{
@ -2099,7 +2126,7 @@ void G_Ticker(boolean run)
marathontime++;
P_MapStart();
// do player reborns if needed
if (gamestate == GS_LEVEL)
{
// Or, alternatively, retry.
@ -2117,11 +2144,28 @@ void G_Ticker(boolean run)
D_MapChange(gamemap, gametype, (cv_kartencore.value == 1), false, 1, false, false);
}
}
// do player reborns if needed
if (G_GamestateUsesLevel() == true)
{
boolean changed = false;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && players[i].playerstate == PST_REBORN)
{
G_DoReborn(i);
changed = true;
}
}
if (changed == true)
{
K_UpdateAllPlayerPositions();
}
}
P_MapEnd();
// do things to change the game state
@ -2175,7 +2219,6 @@ void G_Ticker(boolean run)
F_TextPromptTicker();
AM_Ticker();
HU_Ticker();
break;
case GS_INTERMISSION:
@ -2237,6 +2280,11 @@ void G_Ticker(boolean run)
F_TitleScreenTicker(run);
break;
case GS_CEREMONY:
P_Ticker(run);
K_CeremonyTicker(run);
break;
case GS_WAITINGPLAYERS:
if (netgame)
F_WaitingPlayersTicker();
@ -2336,6 +2384,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
INT16 totalring;
UINT8 laps;
UINT8 latestlap;
UINT32 lapPoints;
UINT16 skincolor;
INT32 skin;
UINT8 availabilities[MAXAVAILABILITY];
@ -2459,6 +2508,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
nocontrol = 0;
laps = 0;
latestlap = 0;
lapPoints = 0;
roundscore = 0;
exiting = 0;
khudfinish = 0;
@ -2496,6 +2546,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
laps = players[player].laps;
latestlap = players[player].latestlap;
lapPoints = players[player].lapPoints;
roundscore = players[player].roundscore;
@ -2523,7 +2574,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
{
follower = players[player].follower;
P_SetTarget(&players[player].follower, NULL);
P_SetTarget(&players[player].awayviewmobj, NULL);
P_SetTarget(&players[player].awayview.mobj, NULL);
P_SetTarget(&players[player].stumbleIndicator, NULL);
P_SetTarget(&players[player].followmobj, NULL);
@ -2571,6 +2622,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->laps = laps;
p->latestlap = latestlap;
p->lapPoints = lapPoints;
p->totalring = totalring;
p->bot = bot;
@ -2730,7 +2782,7 @@ void G_MovePlayerToSpawnOrStarpost(INT32 playernum)
#else
// Player's first spawn should be at the "map start".
// I.e. level load or join mid game.
if (leveltime > starttime && players[playernum].jointime > 0)
if (leveltime > starttime && players[playernum].jointime > 1 && K_PodiumSequence() == false)
P_MovePlayerToStarpost(playernum);
else
P_MovePlayerToSpawn(playernum, G_FindMapStart(playernum));
@ -2826,7 +2878,9 @@ mapthing_t *G_FindRaceStart(INT32 playernum)
// SRB2Kart: figure out player spawn pos from points
if (!playeringame[playernum] || players[playernum].spectator)
{
return playerstarts[0]; // go to first spot if you're a spectator
}
for (i = 0; i < MAXPLAYERS; i++)
{
@ -2909,6 +2963,42 @@ mapthing_t *G_FindRaceStart(INT32 playernum)
return NULL;
}
mapthing_t *G_FindPodiumStart(INT32 playernum)
{
const boolean doprints = P_IsLocalPlayer(&players[playernum]);
if (numcoopstarts)
{
UINT8 pos = K_GetPodiumPosition(&players[playernum]) - 1;
UINT8 i;
if (G_CheckSpot(playernum, playerstarts[pos % numcoopstarts]))
{
return playerstarts[pos % numcoopstarts];
}
// Your spot isn't available? Find whatever you can get first.
for (i = 0; i < numcoopstarts; i++)
{
if (G_CheckSpot(playernum, playerstarts[(pos + i) % numcoopstarts]))
{
return playerstarts[(pos + i) % numcoopstarts];
}
}
if (doprints)
{
CONS_Alert(CONS_WARNING, M_GetText("Could not spawn at any Podium starts!\n"));
}
return NULL;
}
if (doprints)
CONS_Alert(CONS_WARNING, M_GetText("No Podium starts in this map!\n"));
return NULL;
}
// Find a Co-op start, or fallback into other types of starts.
static inline mapthing_t *G_FindRaceStartOrFallback(INT32 playernum)
{
@ -2945,9 +3035,14 @@ mapthing_t *G_FindMapStart(INT32 playernum)
if (!playeringame[playernum])
return NULL;
// -- Podium --
// Single special behavior
if (K_PodiumSequence() == true)
spawnpoint = G_FindPodiumStart(playernum);
// -- Time Attack --
// Order: Race->DM->CTF
if (K_TimeAttackRules() == true)
else if (K_TimeAttackRules() == true)
spawnpoint = G_FindRaceStartOrFallback(playernum);
// -- CTF --
@ -3094,13 +3189,20 @@ void G_ExitLevel(void)
if (i == MAXPLAYERS)
{
// GAME OVER, try again from the start!
if (netgame)
if (grandprixinfo.gp == true
&& grandprixinfo.eventmode == GPEVENT_SPECIAL)
{
; // restart cup here if we do online GP
// We were in a Special Stage.
// We can still progress to the podium when we game over here.
doretry = false;
}
else if (netgame)
{
; // Restart cup here whenever we do Online GP
}
else
{
// Back to the menu with you.
D_QuitNetGame();
CL_Reset();
D_ClearState();
@ -3109,11 +3211,14 @@ void G_ExitLevel(void)
}
else
{
// Go redo this course.
// We have lives, just redo this one course.
G_SetRetryFlag();
}
return;
if (doretry == true)
{
return;
}
}
gameaction = ga_completed;
@ -3804,20 +3909,9 @@ static void G_GetNextMap(void)
// Special stage
else if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels)
{
INT16 totaltotalring = 0;
gp_rank_e grade = K_CalculateGPGrade(&grandprixinfo.rank);
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].spectator)
continue;
if (players[i].bot)
continue;
totaltotalring += players[i].totalring;
}
if (totaltotalring >= 50)
if (grade >= GRADE_A) // On A rank pace? Then you get a chance for S rank!
{
const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_SPECIAL];
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum])
@ -4068,6 +4162,9 @@ static void G_DoCompleted(void)
G_SetGamestate(GS_NULL);
wipegamestate = GS_NULL;
grandprixinfo.rank.capsules += numtargets;
grandprixinfo.rank.position = MAXPLAYERS;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
@ -4093,6 +4190,11 @@ static void G_DoCompleted(void)
}
G_PlayerFinishLevel(i); // take away cards and stuff
if (players[i].bot == false)
{
grandprixinfo.rank.position = min(grandprixinfo.rank.position, K_GetPodiumPosition(&players[i]));
}
}
}
@ -4302,8 +4404,10 @@ void G_EndGame(void)
{
if (nextmap == NEXTMAP_CEREMONY) // end game with ceremony
{
/*F_StartEnding(); -- temporary
return;*/
if (K_StartCeremony() == true)
{
return;
}
}
if (nextmap == NEXTMAP_CREDITS) // end game with credits
{
@ -5116,6 +5220,16 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr
return;
}
if (map == G_MapNumber(podiummap)+1)
{
// Didn't want to do this, but it needs to be here
// for it to work on startup.
if (K_StartCeremony() == true)
{
return;
}
}
gamemap = map;
maptol = mapheaderinfo[gamemap-1]->typeoflevel;
@ -5410,6 +5524,22 @@ void G_SetGamestate(gamestate_t newstate)
#endif
}
boolean G_GamestateUsesLevel(void)
{
switch (gamestate)
{
case GS_TITLESCREEN:
return titlemapinaction;
case GS_LEVEL:
case GS_CEREMONY:
return true;
default:
return false;
}
}
/* These functions handle the exitgame flag. Before, when the user
chose to end a game, it happened immediately, which could cause
crashes if the game was in the middle of something. Now, a flag
@ -5436,6 +5566,11 @@ boolean G_GetExitGameFlag(void)
// Same deal with retrying.
void G_SetRetryFlag(void)
{
if (retrying == false)
{
grandprixinfo.rank.continuesUsed++;
}
retrying = true;
}
@ -5493,4 +5628,3 @@ INT32 G_TicsToMilliseconds(tic_t tics)
{
return (INT32)((tics%TICRATE) * (1000.00f/TICRATE));
}

View file

@ -156,6 +156,7 @@ INT32 G_FindMapByNameOrCode(const char *query, char **foundmapnamep);
mapthing_t *G_FindTeamStart(INT32 playernum);
mapthing_t *G_FindBattleStart(INT32 playernum);
mapthing_t *G_FindRaceStart(INT32 playernum);
mapthing_t *G_FindPodiumStart(INT32 playernum);
mapthing_t *G_FindMapStart(INT32 playernum);
void G_MovePlayerToSpawnOrStarpost(INT32 playernum);
void G_SpawnPlayer(INT32 playernum);
@ -164,6 +165,7 @@ void G_SpawnPlayer(INT32 playernum);
// A normal game starts at map 1, but a warp test can start elsewhere
void G_DeferedInitNew(boolean pencoremode, INT32 map, INT32 pickedchar,
UINT8 ssplayers, boolean FLS);
void G_DoLoadLevelEx(boolean resetplayer, gamestate_t newstate);
void G_DoLoadLevel(boolean resetplayer);
void G_StartTitleCard(void);
@ -250,8 +252,6 @@ void G_LoadGameSettings(void);
void G_SetGameModified(boolean silent, boolean major);
void G_SetUsedCheats(void);
void G_SetGamestate(gamestate_t newstate);
// Gamedata record shit
void G_AllocMainRecordData(INT16 i);
void G_ClearRecords(void);

View file

@ -36,6 +36,7 @@ typedef enum
GS_CREDITS, // credit sequence
GS_EVALUATION, // Evaluation at the end of a game.
GS_GAMEEND, // game end sequence - "did you get all those chaos emeralds?"
GS_CEREMONY, // RR: Podium sequence
// Hardcoded fades or other fading methods
GS_INTRO, // introduction
@ -58,10 +59,13 @@ typedef enum
} gameaction_t;
extern gamestate_t gamestate;
extern UINT8 titlemapinaction;
extern boolean titlemapinaction;
extern UINT8 ultimatemode; // was sk_insane
extern gameaction_t gameaction;
void G_SetGamestate(gamestate_t newstate);
boolean G_GamestateUsesLevel(void);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -552,6 +552,8 @@ char sprnames[NUMSPRITES + 1][5] =
"IMDB", // Item Monitor Small Shard (Debris)
"MTWK", // Item Monitor Glass Twinkle
"SLPT", // Sliptide zip indicator
"WIPD", // Wipeout dust trail
"DRIF", // Drift Sparks
"BDRF", // Brake drift sparks
@ -3930,6 +3932,8 @@ state_t states[NUMSTATES] =
{SPR_MGBT, FF_FLOORSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_MAGICIANBOX_TOP
{SPR_MGBB, FF_FLOORSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_MAGICIANBOX_BOTTOM
{SPR_SLPT, FF_PAPERSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_SLIPTIDEZIP
{SPR_SGNS, FF_ADD|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_SIGNSPARK2}, // S_SIGNSPARK1
{SPR_SGNS, FF_ADD|FF_FULLBRIGHT|1, 1, {NULL}, 0, 0, S_SIGNSPARK3}, // S_SIGNSPARK2
{SPR_SGNS, FF_ADD|FF_FULLBRIGHT|2, 1, {NULL}, 0, 0, S_SIGNSPARK4}, // S_SIGNSPARK3
@ -21281,7 +21285,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
10, // mass
0, // damage
sfx_None, // activesound
MF_NOTHINK|MF_NOBLOCKMAP|MF_NOGRAVITY, // flags
MF_NOBLOCKMAP|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
@ -22583,6 +22587,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
{ // MT_SLIPTIDEZIP
-1, // doomednum
S_SLIPTIDEZIP, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
20*FRACUNIT, // radius
20*FRACUNIT, // height
0, // display offset
100, // mass
0, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_SIGNSPARKLE
-1, // doomednum

View file

@ -1103,6 +1103,8 @@ typedef enum sprite
SPR_IMDB, // Item Monitor Small Shard (Debris)
SPR_MTWK, // Item Monitor Glass Twinkle
SPR_SLPT, // Sliptide zip indicator
SPR_WIPD, // Wipeout dust trail
SPR_DRIF, // Drift Sparks
SPR_BDRF, // Brake drift sparks
@ -4339,6 +4341,8 @@ typedef enum state
S_MAGICIANBOX_TOP,
S_MAGICIANBOX_BOTTOM,
S_SLIPTIDEZIP,
// Signpost sparkles
S_SIGNSPARK1,
S_SIGNSPARK2,
@ -6394,6 +6398,7 @@ typedef enum mobj_type
MT_MONITOR_PART,
MT_MONITOR_SHARD,
MT_MAGICIANBOX,
MT_SLIPTIDEZIP,
MT_SIGNSPARKLE,

View file

@ -20,6 +20,7 @@
#include "k_boss.h" // bossinfo.valid
#include "p_spec.h"
#include "k_objects.h"
#include "k_rank.h"
// Battle overtime info
struct battleovertime battleovertime;
@ -91,8 +92,10 @@ void K_CheckBumpers(void)
{
UINT8 i;
UINT8 numingame = 0;
UINT32 toproundscore = 0;
UINT8 nobumpers = 0;
UINT8 eliminated = 0;
const boolean singleplayer = (battlecapsules || bossinfo.valid);
if (!(gametyperules & GTR_BUMPERS))
return;
@ -110,37 +113,35 @@ void K_CheckBumpers(void)
numingame++;
if (players[i].roundscore > toproundscore)
{
toproundscore = players[i].roundscore;
}
if (players[i].bumpers <= 0) // if you don't have any bumpers, you're probably not a winner
{
nobumpers++;
}
if (players[i].pflags & PF_ELIMINATED)
{
eliminated++;
}
}
if (battlecapsules || bossinfo.valid)
if (singleplayer
? nobumpers > 0 && nobumpers >= numingame
: eliminated >= numingame - 1)
{
if (nobumpers > 0 && nobumpers >= numingame)
for (i = 0; i < MAXPLAYERS; i++)
{
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].spectator)
continue;
if (!playeringame[i])
continue;
if (players[i].spectator)
continue;
if (singleplayer)
players[i].pflags |= PF_NOCONTEST;
P_DoPlayerExit(&players[i]);
}
P_DoPlayerExit(&players[i]);
}
return;
}
else
{
g_hiscore = toproundscore;
}
if (numingame <= 1)
{
@ -183,10 +184,8 @@ void K_CheckEmeralds(player_t *player)
continue;
}
players[i].bumpers = 0;
P_DoPlayerExit(&players[i]);
}
K_CheckBumpers();
}
UINT16 K_GetChaosEmeraldColor(UINT32 emeraldType)
@ -791,7 +790,14 @@ void K_BattleInit(boolean singleplayercontext)
{
if (!playeringame[i] || players[i].spectator)
continue;
players[i].bumpers = maxbumpers;
if (players[i].mo)
{
players[i].mo->health = maxbumpers;
}
K_SpawnPlayerBattleBumpers(players+i);
}
}

View file

@ -28,7 +28,7 @@
#include "r_things.h" // numskins
#include "k_race.h" // finishBeamLine
#include "m_perfstats.h"
#include "k_podium.h"
/*--------------------------------------------------
boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *p)
@ -251,6 +251,9 @@ void K_UpdateMatchRaceBots(void)
--------------------------------------------------*/
boolean K_PlayerUsesBotMovement(player_t *player)
{
if (K_PodiumSequence() == true)
return true;
if (player->exiting)
return true;
@ -1254,6 +1257,63 @@ static INT32 K_HandleBotReverse(player_t *player, ticcmd_t *cmd, botprediction_t
return turnamt;
}
/*--------------------------------------------------
static void K_BotPodiumTurning(player_t *player, ticcmd_t *cmd)
Calculates bot turning for the podium cutscene.
--------------------------------------------------*/
static void K_BotPodiumTurning(player_t *player, ticcmd_t *cmd)
{
const angle_t destAngle = R_PointToAngle2(
player->mo->x, player->mo->y,
player->currentwaypoint->mobj->x, player->currentwaypoint->mobj->y
);
const INT32 delta = AngleDeltaSigned(destAngle, player->mo->angle);
const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN);
fixed_t mul = FixedDiv(delta, (angle_t)(handling << TICCMD_REDUCE));
if (mul > FRACUNIT)
{
mul = FRACUNIT;
}
if (mul < -FRACUNIT)
{
mul = -FRACUNIT;
}
cmd->turning = FixedMul(mul, KART_FULLTURN);
}
/*--------------------------------------------------
static void K_BuildBotPodiumTiccmd(player_t *player, ticcmd_t *cmd)
Calculates all bot movement for the podium cutscene.
--------------------------------------------------*/
static void K_BuildBotPodiumTiccmd(player_t *player, ticcmd_t *cmd)
{
if (player->currentwaypoint == NULL)
{
// We've reached the end of our path.
// Simply stop moving.
return;
}
if (K_GetWaypointIsSpawnpoint(player->currentwaypoint) == false)
{
// Hacky flag reuse: slow down before reaching your podium stand.
cmd->forwardmove = MAXPLMOVE * 3 / 4;
}
else
{
cmd->forwardmove = MAXPLMOVE;
}
cmd->buttons |= BT_ACCELERATE;
K_BotPodiumTurning(player, cmd);
}
/*--------------------------------------------------
void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
@ -1272,7 +1332,9 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
// Remove any existing controls
memset(cmd, 0, sizeof(ticcmd_t));
if (gamestate != GS_LEVEL || !player->mo || player->spectator)
if (player->mo == NULL
|| player->spectator == true
|| G_GamestateUsesLevel() == false)
{
// Not in the level.
return;
@ -1284,6 +1346,12 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
return;
}
if (K_PodiumSequence() == true)
{
K_BuildBotPodiumTiccmd(player, cmd);
return;
}
if (!(gametyperules & GTR_BOTS) // No bot behaviors
|| K_GetNumWaypoints() == 0 // No waypoints
|| leveltime <= introtime // During intro camera
@ -1546,6 +1614,9 @@ void K_UpdateBotGameplayVars(player_t *player)
{
const line_t *botController;
player->botvars.controller = UINT16_MAX;
player->botvars.rubberband = FRACUNIT;
if (gamestate != GS_LEVEL || !player->mo)
{
// Not in the level.

View file

@ -13,6 +13,7 @@
#include "g_game.h" // Sink snipe print
#include "k_objects.h"
#include "k_roulette.h"
#include "k_podium.h"
angle_t K_GetCollideAngle(mobj_t *t1, mobj_t *t2)
{
@ -816,6 +817,12 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
boolean stungT1 = false;
boolean stungT2 = false;
if (K_PodiumSequence() == true)
{
// Always regular bumps, no ring toss.
return false;
}
// Clash instead of damage if both parties have any of these conditions
t1Condition = (K_IsBigger(t1, t2) == true)
|| (t1->player->invincibilitytimer > 0)

View file

@ -95,6 +95,24 @@ INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers)
return points;
}
/*--------------------------------------------------
UINT8 K_GetGPPlayerCount(UINT8 humans)
See header file for description.
--------------------------------------------------*/
UINT8 K_GetGPPlayerCount(UINT8 humans)
{
UINT8 playerCount = 8;
if (humans > 2)
{
// Add 3 bots per player beyond 2P
playerCount += (humans - 2) * 3;
}
return playerCount;
}
/*--------------------------------------------------
void K_InitGrandPrixBots(void)
@ -179,12 +197,7 @@ void K_InitGrandPrixBots(void)
}
}
if (numplayers > 2)
{
// Add 3 bots per player beyond 2P
playercount += (numplayers-2) * 3;
}
playercount = K_GetGPPlayerCount(numplayers);
wantedbots = playercount - numplayers;
// Create rival list

View file

@ -15,14 +15,18 @@
#include "doomdef.h"
#include "doomstat.h"
#include "k_rank.h" // gpRank_t
#ifdef __cplusplus
extern "C" {
#endif
#define GPEVENT_NONE 0
#define GPEVENT_BONUS 1
#define GPEVENT_SPECIAL 2
typedef enum
{
GPEVENT_NONE = 0,
GPEVENT_BONUS,
GPEVENT_SPECIAL,
} gpEvent_e;
extern struct grandprixinfo
{
@ -34,10 +38,10 @@ extern struct grandprixinfo
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.
UINT8 eventmode; ///< See GPEVENT_ constants
gpEvent_e eventmode; ///< Special event mode, bots are set to spectate and a special gametype is played
gpRank_t rank; ///< Struct containing grading information. (See also: k_rank.h)
} grandprixinfo;
/*--------------------------------------------------
UINT8 K_BotStartingDifficulty(SINT8 value);
@ -71,6 +75,23 @@ UINT8 K_BotStartingDifficulty(SINT8 value);
INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers);
/*--------------------------------------------------
UINT8 K_GetGPPlayerCount(UINT8 humans)
Counts the number of total players,
including humans and bots, to put into
a GP session.
Input Arguments:-
humans - Number of human players.
Return:-
Number of both human players and CPU.
--------------------------------------------------*/
UINT8 K_GetGPPlayerCount(UINT8 humans);
/*--------------------------------------------------
void K_InitGrandPrixBots(void);
@ -160,6 +181,7 @@ void K_PlayerLoseLife(player_t *player);
boolean K_CanChangeRules(boolean allowdemos);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -37,6 +37,8 @@
#include "r_fps.h"
#include "m_random.h"
#include "k_roulette.h"
#include "k_bot.h"
#include "k_rank.h"
//{ Patch Definitions
static patch_t *kp_nodraw;
@ -3005,7 +3007,7 @@ static void K_drawKartPlayerCheck(void)
return;
}
if (stplyr->spectator || stplyr->awayviewtics)
if (stplyr->spectator || stplyr->awayview.tics)
{
return;
}
@ -3254,7 +3256,7 @@ static void K_drawKartNameTags(void)
return;
}
if (stplyr->awayviewtics)
if (stplyr->awayview.tics)
{
return;
}
@ -4815,6 +4817,58 @@ static void K_DrawWaypointDebugger(void)
}
}
static void K_DrawGPRankDebugger(void)
{
gp_rank_e grade = GRADE_E;
char gradeChar = '?';
if (cv_debugrank.value == 0)
{
return;
}
if (stplyr != &players[displayplayers[0]]) // only for p1
{
return;
}
if (grandprixinfo.gp == false)
{
return;
}
grade = K_CalculateGPGrade(&grandprixinfo.rank);
V_DrawThinString(0, 0, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
va("POS: %d / %d", grandprixinfo.rank.position, RANK_NEUTRAL_POSITION));
V_DrawThinString(0, 10, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
va("PTS: %d / %d", grandprixinfo.rank.winPoints, grandprixinfo.rank.totalPoints));
V_DrawThinString(0, 20, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
va("LAPS: %d / %d", grandprixinfo.rank.laps, grandprixinfo.rank.totalLaps));
V_DrawThinString(0, 30, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
va("CONTINUES: %d", grandprixinfo.rank.continuesUsed));
V_DrawThinString(0, 40, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
va("CAPSULES: %d / %d", grandprixinfo.rank.capsules, grandprixinfo.rank.totalCapsules));
V_DrawThinString(0, 50, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
va("RINGS: %d / %d", grandprixinfo.rank.rings, grandprixinfo.rank.totalRings));
V_DrawThinString(0, 60, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
va("EMERALD: %s", (grandprixinfo.rank.specialWon == true) ? "YES" : "NO"));
switch (grade)
{
case GRADE_E: { gradeChar = 'E'; break; }
case GRADE_D: { gradeChar = 'D'; break; }
case GRADE_C: { gradeChar = 'C'; break; }
case GRADE_B: { gradeChar = 'B'; break; }
case GRADE_A: { gradeChar = 'A'; break; }
case GRADE_S: { gradeChar = 'S'; break; }
default: { break; }
}
V_DrawThinString(0, 80, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE|V_YELLOWMAP,
va(" ** FINAL GRADE: %c", gradeChar));
}
void K_drawKartHUD(void)
{
boolean islonesome = false;
@ -5086,4 +5140,5 @@ void K_drawKartHUD(void)
K_DrawWaypointDebugger();
K_DrawDirectorDebugger();
K_DrawGPRankDebugger();
}

View file

@ -2,17 +2,23 @@
#include <cstddef>
#include <vector>
#include "core/static_vec.hpp"
#include "k_battle.h"
#include "k_boss.h"
#include "k_hud.h"
#include "k_objects.h"
#include "m_fixed.h"
#include "p_local.h"
#include "p_mobj.h"
#include "r_draw.h"
#include "r_fps.h"
#include "r_main.h"
#include "st_stuff.h"
#include "v_video.h"
using namespace srb2;
namespace
{
@ -21,10 +27,82 @@ struct TargetTracking
mobj_t* mobj;
vector3_t point;
fixed_t camDist;
skincolornum_t color() const
{
switch (mobj->type)
{
case MT_OVERTIME_CENTER:
return SKINCOLOR_BLUE;
case MT_MONITOR:
case MT_EMERALD:
return static_cast<skincolornum_t>(mobj->color);
case MT_PLAYER:
return player_emeralds_color();
default:
return SKINCOLOR_NONE;
}
}
StaticVec<uint32_t, 7> player_emeralds_vec() const
{
StaticVec<uint32_t, 7> emeralds;
const player_t* player = mobj->player;
if (player == nullptr)
{
return emeralds;
}
for (int i = 0; i < 7; ++i)
{
const uint32_t emeraldFlag = (1U << i);
if (player->emeralds & emeraldFlag)
{
emeralds.push_back(emeraldFlag);
}
}
return emeralds;
}
skincolornum_t player_emeralds_color() const
{
const StaticVec emeralds = player_emeralds_vec();
if (emeralds.empty())
{
return SKINCOLOR_NONE;
}
constexpr tic_t kPeriod = TICRATE / 2;
const int idx = (leveltime / kPeriod) % emeralds.size();
return static_cast<skincolornum_t>(K_GetChaosEmeraldColor(emeralds[idx]));
}
const uint8_t* colormap() const
{
const skincolornum_t clr = color();
if (clr != SKINCOLOR_NONE)
{
return R_GetTranslationColormap(TC_RAINBOW, clr, GTC_CACHE);
}
return nullptr;
}
};
void K_DrawTargetTracking(const TargetTracking& target)
{
const uint8_t* colormap = target.colormap();
trackingResult_t result = {};
int32_t timer = 0;
@ -175,10 +253,10 @@ void K_DrawTargetTracking(const TargetTracking& target)
if (targetPatch)
{
V_DrawFixedPatch(targetPos.x, targetPos.y, FRACUNIT, V_SPLITSCREEN, targetPatch, nullptr);
V_DrawFixedPatch(targetPos.x, targetPos.y, FRACUNIT, V_SPLITSCREEN, targetPatch, colormap);
}
V_DrawFixedPatch(arrowPos.x, arrowPos.y, FRACUNIT, V_SPLITSCREEN | arrowFlags, arrowPatch, nullptr);
V_DrawFixedPatch(arrowPos.x, arrowPos.y, FRACUNIT, V_SPLITSCREEN | arrowFlags, arrowPatch, colormap);
}
else
{
@ -207,7 +285,7 @@ void K_DrawTargetTracking(const TargetTracking& target)
FRACUNIT,
V_SPLITSCREEN,
patch,
nullptr
colormap
);
};
@ -229,7 +307,7 @@ void K_DrawTargetTracking(const TargetTracking& target)
}
}
bool is_player_tracking_target(const player_t *player)
bool is_player_tracking_target(player_t *player = stplyr)
{
if ((gametyperules & (GTR_BUMPERS|GTR_CLOSERPLAYERS)) != (GTR_BUMPERS|GTR_CLOSERPLAYERS))
{
@ -253,17 +331,14 @@ bool is_player_tracking_target(const player_t *player)
return player != stplyr;
}
if (g_hiscore < 1) // SOMEONE should be scoring
// Except for DUEL mode, Overtime hides all TARGETs except
// the kiosk.
if (battleovertime.enabled)
{
return false;
}
if (player->roundscore < g_hiscore)
{
return false;
}
return true;
return K_IsPlayerWanted(player);
}
bool is_object_tracking_target(const mobj_t* mobj)
@ -277,6 +352,15 @@ bool is_object_tracking_target(const mobj_t* mobj)
case MT_PLAYER:
return is_player_tracking_target(mobj->player);
case MT_OVERTIME_CENTER:
return inDuel == false && battleovertime.enabled;
case MT_EMERALD:
return is_player_tracking_target();
case MT_MONITOR:
return is_player_tracking_target() && Obj_MonitorGetEmerald(mobj) != 0;
default:
return false;
}

View file

@ -43,6 +43,7 @@
#include "k_boss.h"
#include "k_specialstage.h"
#include "k_roulette.h"
#include "k_podium.h"
// SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H:
// gamespeed is cc (0 for easy, 1 for normal, 2 for hard)
@ -109,6 +110,12 @@ void K_TimerInit(void)
boolean domodeattack = ((modeattacking != ATTACKING_NONE)
|| (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE));
if (K_PodiumSequence() == true)
{
// Leave it alone for podium
return;
}
// Rooooooolllling staaaaaaart
if ((gametyperules & (GTR_ROLLINGSTART|GTR_CIRCUIT)) == (GTR_ROLLINGSTART|GTR_CIRCUIT))
{
@ -340,6 +347,7 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_kartdebugnodes);
CV_RegisterVar(&cv_kartdebugcolorize);
CV_RegisterVar(&cv_kartdebugdirector);
CV_RegisterVar(&cv_debugrank);
CV_RegisterVar(&cv_spbtest);
CV_RegisterVar(&cv_gptest);
CV_RegisterVar(&cv_capsuletest);
@ -354,6 +362,12 @@ boolean K_IsPlayerLosing(player_t *player)
INT32 winningpos = 1;
UINT8 i, pcount = 0;
if (K_PodiumSequence() == true)
{
// Need to be in top 3 to win.
return (player->position > 3);
}
if (player->pflags & PF_NOCONTEST)
return true;
@ -1229,6 +1243,11 @@ static boolean K_TryDraft(player_t *player, mobj_t *dest, fixed_t minDist, fixed
angle_t yourangle, theirangle, diff;
#endif
if (K_PodiumSequence() == true)
{
return false;
}
#ifndef EASYDRAFTTEST
// Don't draft on yourself :V
if (dest->player && dest->player == player)
@ -1812,6 +1831,11 @@ static void K_SpawnGenericSpeedLines(player_t *player, boolean top)
fast->colorized = true;
fast->renderflags |= RF_ADD;
}
else if (player->sliptideZipBoost)
{
fast->color = SKINCOLOR_WHITE;
fast->colorized = true;
}
}
void K_SpawnNormalSpeedLines(player_t *player)
@ -3014,6 +3038,10 @@ fixed_t K_GetSpindashChargeSpeed(player_t *player)
return val;
}
// v2 almost broke sliptiding when it fixed turning bugs!
// This value is fine-tuned to feel like v1 again without reverting any of those changes.
#define SLIPTIDEHANDLING 7*FRACUNIT/8
// sets boostpower, speedboost, accelboost, and handleboost to whatever we need it to be
static void K_GetKartBoostPower(player_t *player)
{
@ -3021,10 +3049,6 @@ static void K_GetKartBoostPower(player_t *player)
const fixed_t maxmetabolismincrease = FRACUNIT/2;
const fixed_t metabolism = FRACUNIT - ((9-player->kartweight) * maxmetabolismincrease / 8);
// v2 almost broke sliptiding when it fixed turning bugs!
// This value is fine-tuned to feel like v1 again without reverting any of those changes.
const fixed_t sliptidehandling = FRACUNIT/2;
fixed_t boostpower = FRACUNIT;
fixed_t speedboost = 0, accelboost = 0, handleboost = 0;
UINT8 numboosts = 0;
@ -3043,13 +3067,16 @@ static void K_GetKartBoostPower(player_t *player)
boostpower = (4*boostpower)/5;
// Note: Handling will ONLY stack when sliptiding!
// > (NB 2023-03-06: This was previously unintentionally applied while drifting as well.)
// > (This only affected drifts where you were under the effect of multiple handling boosts.)
// > (Revisit if Growvinciblity or sneaker-panels + power items feel too heavy while drifting!)
// When you're not, it just uses the best instead of adding together, like the old behavior.
#define ADDBOOST(s,a,h) { \
numboosts++; \
speedboost += FixedDiv(s, FRACUNIT + (metabolism * (numboosts-1))); \
accelboost += FixedDiv(a, FRACUNIT + (metabolism * (numboosts-1))); \
if (player->aizdriftstrat) \
handleboost += FixedDiv(h, FRACUNIT + (metabolism * (numboosts-1))); \
if (K_Sliptiding(player)) \
handleboost += FixedDiv(h, FRACUNIT + (metabolism * (numboosts-1))/4); \
else \
handleboost = max(h, handleboost); \
}
@ -3059,18 +3086,18 @@ static void K_GetKartBoostPower(player_t *player)
UINT8 i;
for (i = 0; i < player->numsneakers; i++)
{
ADDBOOST(FRACUNIT/2, 8*FRACUNIT, sliptidehandling); // + 50% top speed, + 800% acceleration, +50% handling
ADDBOOST(FRACUNIT/2, 8*FRACUNIT, SLIPTIDEHANDLING); // + 50% top speed, + 800% acceleration, +50% handling
}
}
if (player->invincibilitytimer) // Invincibility
{
ADDBOOST(3*FRACUNIT/8, 3*FRACUNIT, sliptidehandling/2); // + 37.5% top speed, + 300% acceleration, +25% handling
ADDBOOST(3*FRACUNIT/8, 3*FRACUNIT, SLIPTIDEHANDLING/2); // + 37.5% top speed, + 300% acceleration, +25% handling
}
if (player->growshrinktimer > 0) // Grow
{
ADDBOOST(0, 0, sliptidehandling/2); // + 0% top speed, + 0% acceleration, +25% handling
ADDBOOST(0, 0, SLIPTIDEHANDLING/2); // + 0% top speed, + 0% acceleration, +25% handling
}
if (player->flamedash) // Flame Shield dash
@ -3079,10 +3106,16 @@ static void K_GetKartBoostPower(player_t *player)
ADDBOOST(
dash, // + infinite top speed
3*FRACUNIT, // + 300% acceleration
FixedMul(FixedDiv(dash, FRACUNIT/2), sliptidehandling/2) // + infinite handling
FixedMul(FixedDiv(dash, FRACUNIT/2), SLIPTIDEHANDLING/2) // + infinite handling
);
}
if (player->sliptideZipBoost)
{
// NB: This is intentionally under the 25% threshold required to initiate a sliptide
ADDBOOST(13*FRACUNIT/20, 4*FRACUNIT, 2*SLIPTIDEHANDLING/5); // + 65% top speed, + 400% acceleration, +20% handling
}
if (player->spindashboost) // Spindash boost
{
const fixed_t MAXCHARGESPEED = K_GetSpindashChargeSpeed(player);
@ -3098,7 +3131,7 @@ static void K_GetKartBoostPower(player_t *player)
if (player->startboost) // Startup Boost
{
ADDBOOST(FRACUNIT, 4*FRACUNIT, sliptidehandling); // + 100% top speed, + 400% acceleration, +50% handling
ADDBOOST(FRACUNIT, 4*FRACUNIT, SLIPTIDEHANDLING); // + 100% top speed, + 400% acceleration, +50% handling
}
if (player->driftboost) // Drift Boost
@ -3128,7 +3161,7 @@ static void K_GetKartBoostPower(player_t *player)
if (player->gateBoost) // SPB Juicebox boost
{
ADDBOOST(3*FRACUNIT/4, 4*FRACUNIT, sliptidehandling/2); // + 75% top speed, + 400% acceleration, +25% handling
ADDBOOST(3*FRACUNIT/4, 4*FRACUNIT, SLIPTIDEHANDLING/2); // + 75% top speed, + 400% acceleration, +25% handling
}
if (player->ringboost) // Ring Boost
@ -3218,27 +3251,42 @@ fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed)
fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower, boolean dorubberband)
{
const boolean mobjValid = (player->mo != NULL && P_MobjWasRemoved(player->mo) == false);
fixed_t finalspeed = K_GetKartSpeedFromStat(player->kartspeed);
fixed_t finalspeed = 0;
if (gametyperules & GTR_BUMPERS && player->bumpers <= 0)
finalspeed = 3 * finalspeed / 2;
if (player->spheres > 0)
if (K_PodiumSequence() == true)
{
fixed_t sphereAdd = (FRACUNIT/40); // 100% at max
finalspeed = FixedMul(finalspeed, FRACUNIT + (sphereAdd * player->spheres));
// Make 1st reach their podium faster!
finalspeed = K_GetKartSpeedFromStat(max(1, 11 - (player->position * 3)));
// Ignore other speed boosts.
doboostpower = dorubberband = false;
}
if (K_PlayerUsesBotMovement(player))
else
{
// Increase bot speed by 1-10% depending on difficulty
fixed_t add = (player->botvars.difficulty * (FRACUNIT/10)) / DIFFICULTBOT;
finalspeed = FixedMul(finalspeed, FRACUNIT + add);
finalspeed = K_GetKartSpeedFromStat(player->kartspeed);
if (player->bot && player->botvars.rival)
if (gametyperules & GTR_BUMPERS && player->bumpers <= 0)
{
// +10% top speed for the rival
finalspeed = FixedMul(finalspeed, 11*FRACUNIT/10);
finalspeed = 3 * finalspeed / 2;
}
if (player->spheres > 0)
{
fixed_t sphereAdd = (FRACUNIT/40); // 100% at max
finalspeed = FixedMul(finalspeed, FRACUNIT + (sphereAdd * player->spheres));
}
if (K_PlayerUsesBotMovement(player))
{
// Increase bot speed by 1-10% depending on difficulty
fixed_t add = (player->botvars.difficulty * (FRACUNIT/10)) / DIFFICULTBOT;
finalspeed = FixedMul(finalspeed, FRACUNIT + add);
if (player->bot && player->botvars.rival)
{
// +10% top speed for the rival
finalspeed = FixedMul(finalspeed, 11*FRACUNIT/10);
}
}
}
@ -3266,16 +3314,32 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower, boolean dorubberb
fixed_t K_GetKartAccel(player_t *player)
{
fixed_t k_accel = 121;
UINT8 stat = (9 - player->kartspeed);
k_accel += 17 * (9 - player->kartspeed); // 121 - 257
if (K_PodiumSequence() == true)
{
// Normalize to Metal's accel
stat = 1;
}
k_accel += 17 * stat; // 121 - 257
if (K_PodiumSequence() == true)
{
return FixedMul(k_accel, FRACUNIT / 4);
}
// karma bomb gets 2x acceleration
if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0)
{
k_accel *= 2;
}
// Marble Garden Top gets 1200% accel
if (player->curshield == KSHIELD_TOP)
{
k_accel *= 12;
}
return FixedMul(k_accel, (FRACUNIT + player->accelboost) / 4);
}
@ -3406,7 +3470,7 @@ fixed_t K_GetNewSpeed(player_t *player)
// 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), K_PlayerBaseFriction(ORIG_FRICTION));
finalspeed = newspeed - oldspeed;
@ -3876,6 +3940,31 @@ void K_InitStumbleIndicator(player_t *player)
P_SetTarget(&new->target, player->mo);
}
void K_InitSliptideZipIndicator(player_t *player)
{
mobj_t *new = NULL;
if (player == NULL)
{
return;
}
if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true)
{
return;
}
if (player->stumbleIndicator != NULL && P_MobjWasRemoved(player->sliptideZipIndicator) == false)
{
P_RemoveMobj(player->sliptideZipIndicator);
}
new = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_SLIPTIDEZIP);
P_SetTarget(&player->sliptideZipIndicator, new);
P_SetTarget(&new->target, player->mo);
}
void K_UpdateStumbleIndicator(player_t *player)
{
const angle_t fudge = ANG15;
@ -3978,6 +4067,78 @@ void K_UpdateStumbleIndicator(player_t *player)
}
}
static boolean K_IsLosingSliptideZip(player_t *player)
{
if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true)
return true;
if (!K_Sliptiding(player) && player->drift == 0 && P_IsObjectOnGround(player->mo) && player->sneakertimer == 0)
return true;
return false;
}
void K_UpdateSliptideZipIndicator(player_t *player)
{
mobj_t *mobj = NULL;
if (player == NULL)
{
return;
}
if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true)
{
return;
}
if (player->stumbleIndicator == NULL || P_MobjWasRemoved(player->stumbleIndicator) == true)
{
K_InitSliptideZipIndicator(player);
return;
}
mobj = player->sliptideZipIndicator;
angle_t momentumAngle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy);
P_MoveOrigin(mobj, player->mo->x - FixedMul(40*mapobjectscale, FINECOSINE(momentumAngle >> ANGLETOFINESHIFT)),
player->mo->y - FixedMul(40*mapobjectscale, FINESINE(momentumAngle >> ANGLETOFINESHIFT)),
player->mo->z + (player->mo->height / 2));
mobj->angle = momentumAngle + ANGLE_90;
P_SetScale(mobj, 3 * player->mo->scale / 2);
// No stored boost
if (player->sliptideZip == 0)
{
mobj->renderflags |= RF_DONTDRAW;
mobj->frame = 7;
return;
}
mobj->renderflags &= ~RF_DONTDRAW;
UINT32 chargeFrame = 7 - min(7, player->sliptideZip / 10);
UINT32 decayFrame = min(7, player->sliptideZipDelay / 5);
if (max(chargeFrame, decayFrame) > mobj->frame)
mobj->frame++;
else if (max(chargeFrame, decayFrame) < mobj->frame)
mobj->frame--;
mobj->renderflags &= ~RF_TRANSMASK;
mobj->renderflags |= RF_PAPERSPRITE;
if (K_IsLosingSliptideZip(player))
{
// Decay timer's ticking
mobj->rollangle += 3*ANG30/4;
if (leveltime % 2 == 0)
mobj->renderflags |= RF_TRANS50;
}
else
{
// Storing boost
mobj->rollangle += 3*ANG15/4;
}
}
static boolean K_LastTumbleBounceCondition(player_t *player)
{
return (player->tumbleBounces > TUMBLEBOUNCES && player->tumbleHeight < 60);
@ -4195,35 +4356,35 @@ void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers)
{
player->pflags |= (PF_NOCONTEST|PF_ELIMINATED);
}
P_KillMobj(player->mo, NULL, NULL, DMG_NORMAL);
}
K_CalculateBattleWanted();
K_CheckBumpers();
}
void K_DestroyBumpers(player_t *player, UINT8 amount)
UINT8 K_DestroyBumpers(player_t *player, UINT8 amount)
{
UINT8 oldBumpers = player->bumpers;
if (!(gametyperules & GTR_BUMPERS))
{
return;
return 0;
}
amount = min(amount, player->bumpers);
if (amount == 0)
{
return;
return 0;
}
player->bumpers -= amount;
K_HandleBumperChanges(player, oldBumpers);
return amount;
}
void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount)
UINT8 K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount)
{
UINT8 oldPlayerBumpers = player->bumpers;
UINT8 oldVictimBumpers = victim->bumpers;
@ -4232,14 +4393,14 @@ void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount)
if (!(gametyperules & GTR_BUMPERS))
{
return;
return 0;
}
amount = min(amount, victim->bumpers);
if (amount == 0)
{
return;
return 0;
}
while ((tookBumpers < amount) && (victim->bumpers > 0))
@ -4294,7 +4455,7 @@ void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount)
if (tookBumpers == 0)
{
// No change occured.
return;
return 0;
}
// Play steal sound
@ -4302,6 +4463,8 @@ void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount)
K_HandleBumperChanges(player, oldPlayerBumpers);
K_HandleBumperChanges(victim, oldVictimBumpers);
return tookBumpers;
}
#define MINEQUAKEDIST 4096
@ -6983,6 +7146,13 @@ static void K_UpdateEngineSounds(player_t *player)
INT32 targetsnd = 0;
INT32 i;
if (leveltime < 8 || player->spectator || gamestate != GS_LEVEL)
{
// Silence the engines, and reset sound number while we're at it.
player->karthud[khud_enginesnd] = 0;
return;
}
s = (player->kartspeed - 1) / 3;
w = (player->kartweight - 1) / 3;
@ -6995,13 +7165,6 @@ static void K_UpdateEngineSounds(player_t *player)
class = s + (3*w);
if (leveltime < 8 || player->spectator)
{
// Silence the engines, and reset sound number while we're at it.
player->karthud[khud_enginesnd] = 0;
return;
}
#if 0
if ((leveltime % 8) != ((player-players) % 8)) // Per-player offset, to make engines sound distinct!
#else
@ -7493,7 +7656,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
if (player->sneakertimer || player->ringboost
|| player->driftboost || player->startboost
|| player->eggmanexplode || player->trickboost
|| player->gateBoost)
|| player->gateBoost || player->sliptideZipBoost)
{
#if 0
if (player->invincibilitytimer)
@ -7755,6 +7918,11 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
player->startboost--;
}
if (player->sliptideZipBoost > 0 && onground == true)
{
player->sliptideZipBoost--;
}
if (player->spindashboost)
{
player->spindashboost--;
@ -8124,6 +8292,8 @@ void K_KartPlayerAfterThink(player_t *player)
K_UpdateStumbleIndicator(player);
K_UpdateSliptideZipIndicator(player);
// Move held objects (Bananas, Orbinaut, etc)
K_MoveHeldObjects(player);
@ -8479,6 +8649,12 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player)
--------------------------------------------------*/
void K_UpdateDistanceFromFinishLine(player_t *const player)
{
if (K_PodiumSequence() == true)
{
K_UpdatePodiumWaypoints(player);
return;
}
if ((player != NULL) && (player->mo != NULL))
{
waypoint_t *finishline = K_GetFinishLineWaypoint();
@ -8845,6 +9021,15 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue)
p_speed = min(currentSpeed, (p_maxspeed * 2));
}
if (K_PodiumSequence() == true)
{
// Normalize turning for podium
weightadjust = FixedDiv((p_maxspeed * 3), (p_maxspeed * 3) + (2 * FRACUNIT));
turnfixed = FixedMul(turnfixed, weightadjust);
turnfixed *= 2;
return (turnfixed / FRACUNIT);
}
weightadjust = FixedDiv((p_maxspeed * 3) - p_speed, (p_maxspeed * 3) + (player->kartweight * FRACUNIT));
if (K_PlayerUsesBotMovement(player))
@ -9201,7 +9386,7 @@ static void K_KartDrift(player_t *player, boolean onground)
player->pflags &= ~PF_DRIFTEND;
}
if ((player->handleboost == 0)
if ((player->handleboost < (SLIPTIDEHANDLING/2))
|| (!player->steering)
|| (!player->aizdriftstrat)
|| (player->steering > 0) != (player->aizdriftstrat > 0))
@ -9215,6 +9400,8 @@ static void K_KartDrift(player_t *player, boolean onground)
{
K_SpawnAIZDust(player);
player->sliptideZip++;
if (abs(player->aizdrifttilt) < ANGLE_22h)
{
player->aizdrifttilt =
@ -9232,6 +9419,40 @@ static void K_KartDrift(player_t *player, boolean onground)
if (!K_Sliptiding(player))
{
if (K_IsLosingSliptideZip(player) && player->sliptideZip > 0)
{
player->sliptideZipDelay++;
if (player->sliptideZipDelay > TICRATE)
{
fixed_t maxZipPower = 2*FRACUNIT;
fixed_t minZipPower = 1*FRACUNIT;
fixed_t powerSpread = maxZipPower - minZipPower;
int minPenalty = 2*1 + (9-9); // Kinda doing a similar thing to driftspark stage timers here.
int maxPenalty = 2*9 + (9-1); // 1/9 gets max, 9/1 gets min, everyone else gets something in between.
int penaltySpread = maxPenalty - minPenalty;
int yourPenalty = 2*player->kartspeed + (9 - player->kartweight); // And like driftsparks, speed hurts double.
yourPenalty -= minPenalty; // Normalize; minimum penalty should take away 0 power.
fixed_t yourPowerReduction = FixedDiv(yourPenalty * FRACUNIT, penaltySpread * FRACUNIT);
fixed_t yourPower = maxZipPower - FixedMul(yourPowerReduction, powerSpread);
int yourBoost = FixedInt(FixedMul(yourPower, player->sliptideZip * FRACUNIT));
/*
CONS_Printf("SZ %d MZ %d mZ %d pwS %d mP %d MP %d peS %d yPe %d yPR %d yPw %d yB %d\n",
player->sliptideZip, maxZipPower, minZipPower, powerSpread, minPenalty, maxPenalty, penaltySpread, yourPenalty, yourPowerReduction, yourPower, yourBoost);
*/
player->sliptideZipBoost += yourBoost;
K_SpawnDriftBoostExplosion(player, 0);
player->sliptideZip = 0;
player->sliptideZipDelay = 0;
S_StartSound(player->mo, sfx_s3kb6);
}
}
player->aizdrifttilt -= player->aizdrifttilt / 4;
player->aizdriftturn -= player->aizdriftturn / 4;
@ -9240,6 +9461,10 @@ static void K_KartDrift(player_t *player, boolean onground)
if (abs(player->aizdriftturn) < ANGLE_11hh)
player->aizdriftturn = 0;
}
else
{
player->sliptideZipDelay = 0;
}
if (player->drift
&& ((buttons & BT_BRAKE)
@ -9271,64 +9496,79 @@ void K_KartUpdatePosition(player_t *player)
return;
}
for (i = 0; i < MAXPLAYERS; i++)
if (K_PodiumSequence() == true)
{
if (!playeringame[i] || players[i].spectator || !players[i].mo)
continue;
position = K_GetPodiumPosition(player);
realplayers++;
if (gametyperules & GTR_CIRCUIT)
for (i = 0; i < MAXPLAYERS; i++)
{
if (player->exiting) // End of match standings
{
// Only time matters
if (players[i].realtime < player->realtime)
position++;
}
else
{
// I'm a lap behind this player OR
// My distance to the finish line is higher, so I'm behind
if ((players[i].laps > player->laps)
|| (players[i].distancetofinish < player->distancetofinish))
{
position++;
}
}
if (!playeringame[i] || players[i].spectator || !players[i].mo)
continue;
realplayers++;
}
else
}
else
{
for (i = 0; i < MAXPLAYERS; i++)
{
if (player->exiting) // End of match standings
{
// Only score matters
if (players[i].roundscore > player->roundscore)
position++;
}
else
{
UINT8 myEmeralds = K_NumEmeralds(player);
UINT8 yourEmeralds = K_NumEmeralds(&players[i]);
if (!playeringame[i] || players[i].spectator || !players[i].mo)
continue;
// First compare all points
if (players[i].roundscore > player->roundscore)
realplayers++;
if (gametyperules & GTR_CIRCUIT)
{
if (player->exiting) // End of match standings
{
position++;
// Only time matters
if (players[i].realtime < player->realtime)
position++;
}
else if (players[i].roundscore == player->roundscore)
else
{
// Emeralds are a tie breaker
if (yourEmeralds > myEmeralds)
// I'm a lap behind this player OR
// My distance to the finish line is higher, so I'm behind
if ((players[i].laps > player->laps)
|| (players[i].distancetofinish < player->distancetofinish))
{
position++;
}
else if (yourEmeralds == myEmeralds)
}
}
else
{
if (player->exiting) // End of match standings
{
// Only score matters
if (players[i].roundscore > player->roundscore)
position++;
}
else
{
UINT8 myEmeralds = K_NumEmeralds(player);
UINT8 yourEmeralds = K_NumEmeralds(&players[i]);
// First compare all points
if (players[i].roundscore > player->roundscore)
{
// Bumpers are the second tier tie breaker
if (players[i].bumpers > player->bumpers)
position++;
}
else if (players[i].roundscore == player->roundscore)
{
// Emeralds are a tie breaker
if (yourEmeralds > myEmeralds)
{
position++;
}
else if (yourEmeralds == myEmeralds)
{
// Bumpers are the second tier tie breaker
if (players[i].bumpers > player->bumpers)
{
position++;
}
}
}
}
}
@ -9386,6 +9626,29 @@ void K_KartUpdatePosition(player_t *player)
player->position = position;
}
void K_UpdateAllPlayerPositions(void)
{
INT32 i;
// First loop: Ensure all players' distance to the finish line are all accurate
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
{
K_UpdateDistanceFromFinishLine(&players[i]);
}
}
// Second loop: Ensure all player positions reflect everyone's distances
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
{
K_KartUpdatePosition(&players[i]);
}
}
}
//
// K_StripItems
//
@ -9680,6 +9943,9 @@ static void K_KartSpindashWind(mobj_t *parent)
P_SetTarget(&wind->target, parent);
if (parent->player && parent->player->sliptideZipBoost)
P_SetScale(wind, wind->scale * 2);
if (parent->momx || parent->momy)
wind->angle = R_PointToAngle2(0, 0, parent->momx, parent->momy);
else
@ -9755,6 +10021,11 @@ static void K_KartSpindash(player_t *player)
K_KartSpindashWind(player->mo);
}
if ((player->sliptideZipBoost > 0) && (spawnWind == true))
{
K_KartSpindashWind(player->mo);
}
if (player->spindashboost > (TICRATE/2))
{
K_KartSpindashDust(player->mo);
@ -9925,18 +10196,38 @@ static void K_AirFailsafe(player_t *player)
}
}
//
// K_PlayerBaseFriction
//
fixed_t K_PlayerBaseFriction(fixed_t original)
{
fixed_t frict = original;
if (K_PodiumSequence() == true)
{
frict -= FRACUNIT >> 4;
}
if (frict > FRACUNIT) { frict = FRACUNIT; }
if (frict < 0) { frict = 0; }
return frict;
}
//
// K_AdjustPlayerFriction
//
void K_AdjustPlayerFriction(player_t *player)
{
fixed_t prevfriction = player->mo->friction;
const fixed_t prevfriction = K_PlayerBaseFriction(player->mo->friction);
if (P_IsObjectOnGround(player->mo) == false)
{
return;
}
player->mo->friction = prevfriction;
// Reduce friction after hitting a spring
if (player->tiregrease)
{

View file

@ -99,12 +99,14 @@ angle_t K_StumbleSlope(angle_t angle, angle_t pitch, angle_t roll);
void K_StumblePlayer(player_t *player);
boolean K_CheckStumble(player_t *player, angle_t oldPitch, angle_t oldRoll, boolean fromAir);
void K_InitStumbleIndicator(player_t *player);
void K_InitSliptideZipIndicator(player_t *player);
void K_UpdateStumbleIndicator(player_t *player);
void K_UpdateSliptideZipIndicator(player_t *player);
INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source);
void K_DebtStingPlayer(player_t *player, mobj_t *source);
void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers);
void K_DestroyBumpers(player_t *player, UINT8 amount);
void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount);
UINT8 K_DestroyBumpers(player_t *player, UINT8 amount);
UINT8 K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount);
void K_MineFlashScreen(mobj_t *source);
void K_SpawnMineExplosion(mobj_t *source, UINT8 color, tic_t delay);
void K_RunFinishLineBeam(void);
@ -139,6 +141,7 @@ INT32 K_GetKartDriftSparkValueForStage(player_t *player, UINT8 stage);
void K_SpawnDriftBoostExplosion(player_t *player, int stage);
void K_SpawnDriftElectricSparks(player_t *player, int color, boolean shockwave);
void K_KartUpdatePosition(player_t *player);
void K_UpdateAllPlayerPositions(void);
SINT8 K_GetTotallyRandomResult(UINT8 useodds);
mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT8 amount);
void K_DropItems(player_t *player);
@ -176,6 +179,7 @@ fixed_t K_3dKartMovement(player_t *player);
boolean K_PlayerEBrake(player_t *player);
SINT8 K_Sliptiding(player_t *player);
boolean K_FastFallBounce(player_t *player);
fixed_t K_PlayerBaseFriction(fixed_t original);
void K_AdjustPlayerFriction(player_t *player);
void K_MoveKartPlayer(player_t *player, boolean onground);
void K_CheckSpectateStatus(void);

View file

@ -571,6 +571,7 @@ void Addons_option_Onchange(void);
void M_SortServerList(void);
void M_MapMenuControls(event_t *ev);
void M_UpdateMenuCMD(UINT8 i);
boolean M_Responder(event_t *ev);
boolean M_MenuButtonPressed(UINT8 pid, UINT32 bt);
boolean M_MenuButtonHeld(UINT8 pid, UINT32 bt);

View file

@ -202,6 +202,23 @@ boolean M_PrevOpt(void)
return true;
}
static boolean M_GamestateCanOpenMenu(void)
{
switch (gamestate)
{
case GS_INTRO:
case GS_CUTSCENE:
case GS_GAMEEND:
case GS_CREDITS:
case GS_EVALUATION:
case GS_CEREMONY:
return false;
default:
return true;
}
}
//
// M_Responder
//
@ -209,9 +226,9 @@ boolean M_Responder(event_t *ev)
{
menuKey = -1;
if (dedicated || (demo.playback && demo.title)
|| gamestate == GS_INTRO || gamestate == GS_CUTSCENE || gamestate == GS_GAMEEND
|| gamestate == GS_CREDITS || gamestate == GS_EVALUATION)
if (dedicated
|| (demo.playback && demo.title)
|| M_GamestateCanOpenMenu() == false)
{
return false;
}
@ -713,7 +730,7 @@ void M_SetMenuDelay(UINT8 i)
}
}
static void M_UpdateMenuCMD(UINT8 i)
void M_UpdateMenuCMD(UINT8 i)
{
UINT8 mp = max(1, setup_numplayers);
@ -749,19 +766,11 @@ static void M_UpdateMenuCMD(UINT8 i)
void M_MapMenuControls(event_t *ev)
{
INT32 i;
if (ev)
{
// update keys current state
G_MapEventsToControls(ev);
}
// Update menu CMD
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
M_UpdateMenuCMD(i);
}
}
boolean M_MenuButtonPressed(UINT8 pid, UINT32 bt)

504
src/k_podium.c Normal file
View file

@ -0,0 +1,504 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) by Sally "TehRealSalt" Cochenour
// Copyright (C) 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_podium.c
/// \brief Grand Prix podium cutscene
#include "k_podium.h"
#include "doomdef.h"
#include "doomstat.h"
#include "d_main.h"
#include "d_netcmd.h"
#include "f_finale.h"
#include "g_game.h"
#include "hu_stuff.h"
#include "r_local.h"
#include "s_sound.h"
#include "i_time.h"
#include "i_video.h"
#include "v_video.h"
#include "w_wad.h"
#include "z_zone.h"
#include "i_system.h"
#include "i_threads.h"
#include "dehacked.h"
#include "g_input.h"
#include "console.h"
#include "m_random.h"
#include "m_misc.h" // moviemode functionality
#include "y_inter.h"
#include "m_cond.h"
#include "p_local.h"
#include "p_setup.h"
#include "st_stuff.h" // hud hiding
#include "fastcmp.h"
#include "lua_hud.h"
#include "lua_hook.h"
#include "k_menu.h"
#include "k_grandprix.h"
#include "k_rank.h"
static struct podiumData_s
{
boolean ranking;
gpRank_t rank;
gp_rank_e grade;
UINT8 state;
UINT8 delay;
UINT8 fade;
} podiumData;
#define PODIUM_STATES (9) // TODO: enum when this actually gets made
/*--------------------------------------------------
boolean K_PodiumSequence(void)
See header file for description.
--------------------------------------------------*/
boolean K_PodiumSequence(void)
{
return (gamestate == GS_CEREMONY);
}
/*--------------------------------------------------
UINT8 K_GetPodiumPosition(player_t *player)
See header file for description.
--------------------------------------------------*/
UINT8 K_GetPodiumPosition(player_t *player)
{
UINT8 position = 1;
INT32 i;
for (i = 0; i < MAXPLAYERS; i++)
{
player_t *other = NULL;
if (playeringame[i] == false)
{
continue;
}
other = &players[i];
if (other->spectator == true)
{
continue;
}
if (other->score > player->score)
{
// Final score is the important part.
position++;
}
else if (other->score == player->score)
{
if (other->bot == false && player->bot == true)
{
// Bots are never as important as players.
position++;
}
else if (i < player - players)
{
// Port priority is the final tie breaker.
position++;
}
}
}
return position;
}
/*--------------------------------------------------
static void K_SetPodiumWaypoint(player_t *const player, waypoint_t *const waypoint)
Changes the player's current and next waypoints, for
use during the podium sequence.
Input Arguments:-
player - The player to update the waypoints of.
waypoint - The new current waypoint.
Return:-
None
--------------------------------------------------*/
static void K_SetPodiumWaypoint(player_t *const player, waypoint_t *const waypoint)
{
// Set the new waypoint.
player->currentwaypoint = waypoint;
if ((waypoint == NULL)
|| (waypoint->nextwaypoints == NULL)
|| (waypoint->numnextwaypoints == 0U))
{
// No waypoint, or no next waypoint.
player->nextwaypoint = NULL;
return;
}
// Simply use the first available next waypoint.
// No need for split paths in these cutscenes.
player->nextwaypoint = waypoint->nextwaypoints[0];
}
/*--------------------------------------------------
void K_InitializePodiumWaypoint(player_t *const player)
See header file for description.
--------------------------------------------------*/
void K_InitializePodiumWaypoint(player_t *const player)
{
if ((player != NULL) && (player->mo != NULL))
{
player->position = K_GetPodiumPosition(player);
if (player->position > 0 && player->position <= MAXPLAYERS)
{
// Initialize our first waypoint to the one that
// matches our position.
K_SetPodiumWaypoint(player, K_GetWaypointFromID(player->position));
}
else
{
// None does, so remove it if we happen to have one.
K_SetPodiumWaypoint(player, NULL);
}
}
}
/*--------------------------------------------------
void K_UpdatePodiumWaypoints(player_t *const player)
See header file for description.
--------------------------------------------------*/
void K_UpdatePodiumWaypoints(player_t *const player)
{
if ((player != NULL) && (player->mo != NULL))
{
if (player->currentwaypoint != NULL)
{
const fixed_t xydist = P_AproxDistance(
player->mo->x - player->currentwaypoint->mobj->x,
player->mo->y - player->currentwaypoint->mobj->y
);
const fixed_t xyzdist = P_AproxDistance(
xydist,
player->mo->z - player->currentwaypoint->mobj->z
);
//const fixed_t speed = P_AproxDistance(player->mo->momx, player->mo->momy);
if (xyzdist <= player->mo->radius + player->currentwaypoint->mobj->radius)
{
// Reached waypoint, go to the next waypoint.
K_SetPodiumWaypoint(player, player->nextwaypoint);
}
}
}
}
/*--------------------------------------------------
boolean K_StartCeremony(void)
See header file for description.
--------------------------------------------------*/
boolean K_StartCeremony(void)
{
INT32 podiumMapNum = nummapheaders;
INT32 i;
if (grandprixinfo.gp == false)
{
return false;
}
if (podiummap
&& ((podiumMapNum = G_MapNumber(podiummap)) < nummapheaders)
&& mapheaderinfo[podiumMapNum]
&& mapheaderinfo[podiumMapNum]->lumpnum != LUMPERROR)
{
P_SetTarget(&titlemapcam.mobj, NULL);
gamemap = podiumMapNum+1;
maptol = mapheaderinfo[gamemap-1]->typeoflevel;
globalweather = mapheaderinfo[gamemap-1]->weather;
// Make sure all of the GAME OVER'd players can spawn
// and be present for the podium
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator && !players[i].bot)
{
players[i].lives = max(1, players[i].lives);
}
}
G_SetGametype(GT_RACE);
G_DoLoadLevelEx(false, GS_CEREMONY);
r_splitscreen = 0; // Only one screen for the ceremony
R_ExecuteSetViewSize();
return true;
}
return false;
}
/*--------------------------------------------------
void K_FinishCeremony(void)
See header file for description.
--------------------------------------------------*/
void K_FinishCeremony(void)
{
if (K_PodiumSequence() == false)
{
return;
}
podiumData.ranking = true;
}
/*--------------------------------------------------
void K_ResetCeremony(void)
See header file for description.
--------------------------------------------------*/
void K_ResetCeremony(void)
{
memset(&podiumData, 0, sizeof(struct podiumData_s));
if (K_PodiumSequence() == false)
{
return;
}
podiumData.rank = grandprixinfo.rank;
podiumData.grade = K_CalculateGPGrade(&podiumData.rank);
}
/*--------------------------------------------------
void K_CeremonyTicker(boolean run)
See header file for description.
--------------------------------------------------*/
void K_CeremonyTicker(boolean run)
{
// don't trigger if doing anything besides idling
if (gameaction != ga_nothing || gamestate != GS_CEREMONY)
{
return;
}
P_TickAltView(&titlemapcam);
if (titlemapcam.mobj != NULL)
{
camera[0].x = titlemapcam.mobj->x;
camera[0].y = titlemapcam.mobj->y;
camera[0].z = titlemapcam.mobj->z;
camera[0].angle = titlemapcam.mobj->angle;
camera[0].aiming = titlemapcam.mobj->pitch;
camera[0].subsector = titlemapcam.mobj->subsector;
}
if (podiumData.ranking == false)
{
return;
}
if (run == true)
{
if (podiumData.fade < 16)
{
podiumData.fade++;
}
else
{
if (podiumData.state < PODIUM_STATES)
{
podiumData.delay++;
if (podiumData.delay > TICRATE/2)
{
podiumData.state++;
podiumData.delay = 0;
}
}
}
}
}
/*--------------------------------------------------
boolean K_CeremonyResponder(event_t *event)
See header file for description.
--------------------------------------------------*/
boolean K_CeremonyResponder(event_t *event)
{
INT32 key = event->data1;
if (podiumData.ranking == false || podiumData.state < PODIUM_STATES)
{
return false;
}
// remap virtual keys (mouse & joystick buttons)
switch (key)
{
case KEY_MOUSE1:
key = KEY_ENTER;
break;
case KEY_MOUSE1 + 1:
key = KEY_BACKSPACE;
break;
case KEY_JOY1:
case KEY_JOY1 + 2:
key = KEY_ENTER;
break;
case KEY_JOY1 + 3:
key = 'n';
break;
case KEY_JOY1 + 1:
key = KEY_BACKSPACE;
break;
case KEY_HAT1:
key = KEY_UPARROW;
break;
case KEY_HAT1 + 1:
key = KEY_DOWNARROW;
break;
case KEY_HAT1 + 2:
key = KEY_LEFTARROW;
break;
case KEY_HAT1 + 3:
key = KEY_RIGHTARROW;
break;
}
if (event->type != ev_keydown)
{
return false;
}
if (key != KEY_ESCAPE && key != KEY_ENTER && key != KEY_BACKSPACE)
{
return false;
}
return true;
}
/*--------------------------------------------------
void K_CeremonyDrawer(void)
See header file for description.
--------------------------------------------------*/
void K_CeremonyDrawer(void)
{
if (podiumData.ranking == true)
{
char gradeChar = '?';
INT32 x = 64;
INT32 y = 48;
INT32 i;
switch (podiumData.grade)
{
case GRADE_E: { gradeChar = 'E'; break; }
case GRADE_D: { gradeChar = 'D'; break; }
case GRADE_C: { gradeChar = 'C'; break; }
case GRADE_B: { gradeChar = 'B'; break; }
case GRADE_A: { gradeChar = 'A'; break; }
case GRADE_S: { gradeChar = 'S'; break; }
default: { break; }
}
V_DrawFadeScreen(0xFF00, podiumData.fade);
for (i = 0; i <= podiumData.state; i++)
{
switch (i)
{
case 1:
{
V_DrawString(x, y, V_ALLOWLOWERCASE,
va("POS: %d / %d", podiumData.rank.position, RANK_NEUTRAL_POSITION)
);
break;
}
case 2:
{
V_DrawString(x, y, V_ALLOWLOWERCASE,
va("PTS: %d / %d", podiumData.rank.winPoints, podiumData.rank.totalPoints)
);
break;
}
case 3:
{
V_DrawString(x, y, V_ALLOWLOWERCASE,
va("LAPS: %d / %d", podiumData.rank.laps, podiumData.rank.totalLaps)
);
break;
}
case 4:
{
V_DrawString(x, y, V_ALLOWLOWERCASE,
va("CONTINUES: %d", podiumData.rank.continuesUsed)
);
break;
}
case 5:
{
V_DrawString(x, y, V_ALLOWLOWERCASE,
va("CAPSULES: %d / %d", podiumData.rank.capsules, podiumData.rank.totalCapsules)
);
break;
}
case 6:
{
V_DrawString(x, y, V_ALLOWLOWERCASE,
va("RINGS: %d / %d", podiumData.rank.rings, podiumData.rank.totalRings)
);
break;
}
case 7:
{
V_DrawString(x, y, V_ALLOWLOWERCASE,
va("EMERALD: %s", (podiumData.rank.specialWon == true) ? "YES" : "NO")
);
break;
}
case 8:
{
V_DrawString(x, y + 10, V_YELLOWMAP|V_ALLOWLOWERCASE,
va(" ** FINAL GRADE: %c", gradeChar)
);
break;
}
case 9:
{
V_DrawThinString(2, BASEVIDHEIGHT - 10, V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
"Press some button type deal to continue"
);
break;
}
}
y += 10;
}
}
if (timeinmap < 16)
{
// Level fade-in
V_DrawCustomFadeScreen(((levelfadecol == 0) ? "FADEMAP1" : "FADEMAP0"), 31-(timeinmap*2));
}
}

178
src/k_podium.h Normal file
View file

@ -0,0 +1,178 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) by Sally "TehRealSalt" Cochenour
// Copyright (C) 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_podium.h
/// \brief Grand Prix podium cutscene
#ifndef __K_PODIUM__
#define __K_PODIUM__
#include "doomtype.h"
#include "d_event.h"
#include "p_mobj.h"
#ifdef __cplusplus
extern "C" {
#endif
/*--------------------------------------------------
boolean K_PodiumSequence(void);
Returns whenver or not we are in the podium
cutscene mode.
Input Arguments:-
N/A
Return:-
true if we're in GS_CEREMONY, otherwise false.
--------------------------------------------------*/
boolean K_PodiumSequence(void);
/*--------------------------------------------------
UINT8 K_GetPodiumPosition(player_t *player);
Calculates what the player's position would
be at the final standings.
Input Arguments:-
player - The player to do the calculation for.
Return:-
The player's final position, as a number
between 1 and MAXPLAYERS.
--------------------------------------------------*/
UINT8 K_GetPodiumPosition(player_t *player);
/*--------------------------------------------------
void K_InitializePodiumWaypoint(player_t *const player);
Sets a bot's current waypoint to one matching
their final podium position.
Input Arguments:-
player - The podium bot to update.
Return:-
N/A
--------------------------------------------------*/
void K_InitializePodiumWaypoint(player_t *const player);
/*--------------------------------------------------
void K_UpdatePodiumWaypoints(player_t *const player);
Helps a bot move along a predetermined path by
updating their current and next waypoints as
they move. Intended for the podium sequence.
Input Arguments:-
player - The podium bot to update.
Return:-
N/A
--------------------------------------------------*/
void K_UpdatePodiumWaypoints(player_t *const player);
/*--------------------------------------------------
boolean K_StartCeremony(void);
Loads the podium map and changes the gamestate
to the podium cutscene mode.
Input Arguments:-
N/A
Return:-
true if successful, otherwise false. Can fail
if there is no podium map defined.
--------------------------------------------------*/
boolean K_StartCeremony(void);
/*--------------------------------------------------
void K_FinishCeremony(void);
Called at the end of the podium cutscene,
displays the ranking screen and starts
accepting input.
--------------------------------------------------*/
void K_FinishCeremony(void);
/*--------------------------------------------------
void K_ResetCeremony(void);
Called on level load, to reset all of the
podium variables.
--------------------------------------------------*/
void K_ResetCeremony(void);
/*--------------------------------------------------
void K_CeremonyTicker(boolean run);
Ticker function to be ran during the podium
cutscene mode gamestate. Handles updating
the camera.
Input Arguments:-
run - Set to true when we're running a
new game frame.
Return:-
N/A
--------------------------------------------------*/
void K_CeremonyTicker(boolean run);
/*--------------------------------------------------
void K_CeremonyResponder(event_t *ev);
Responder function to be ran during the podium
cutscene mode gamestate. Handles key presses
ending the podium scene.
Input Arguments:-
ev - The player input event.
Return:-
true to end the podium cutscene and return
to the title screen, otherwise false.
--------------------------------------------------*/
boolean K_CeremonyResponder(event_t *ev);
/*--------------------------------------------------
void K_CeremonyDrawer(void);
Handles the ranking screen and other HUD for
the podium cutscene.
--------------------------------------------------*/
void K_CeremonyDrawer(void);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // __K_PODIUM__

416
src/k_rank.c Normal file
View file

@ -0,0 +1,416 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) by Sally "TehRealSalt" Cochenour
// Copyright (C) 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_rank.c
/// \brief Grand Prix mode ranking
#include "k_rank.h"
#include "k_grandprix.h"
#include "k_specialstage.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"
#include "fastcmp.h"
#include "byteptr.h"
// I was ALMOST tempted to start tearing apart all
// of the map loading code and turning it into C++
// and making it properly split between read-only
// and true level loading and clean up all of the
// global variable garbage it uses ... but I stopped
// myself. So here's code duplication hell instead.
static UINT32 g_rankCapsules_mapthingsPos[UINT16_MAX];
static size_t g_rankCapsules_nummapthings = 0;
static boolean g_rankCapsules_udmf = false;
static UINT32 g_rankCapsules_count = 0;
/*--------------------------------------------------
static void RankCapsules_TextmapCount(size_t size)
Counts the number of map things and records
the structure positions, for the result of
RankCapsules_CountFromMap.
Input Arguments:-
size - Length of the TEXTMAP lump.
Return:-
N/A
--------------------------------------------------*/
static UINT32 RankCapsules_TextmapCount(size_t size)
{
const char *tkn = M_TokenizerRead(0);
UINT8 brackets = 0;
g_rankCapsules_nummapthings = 0;
// Look for namespace at the beginning.
if (!fastcmp(tkn, "namespace"))
{
return false;
}
// Check if namespace is valid.
tkn = M_TokenizerRead(0);
while ((tkn = M_TokenizerRead(0)) && M_TokenizerGetEndPos() < size)
{
// Avoid anything inside bracketed stuff, only look for external keywords.
if (brackets)
{
if (fastcmp(tkn, "}"))
brackets--;
}
else if (fastcmp(tkn, "{"))
brackets++;
// Check for valid fields.
else if (fastcmp(tkn, "thing"))
g_rankCapsules_mapthingsPos[g_rankCapsules_nummapthings++] = M_TokenizerGetEndPos();
}
if (brackets)
{
return false;
}
return true;
}
/*--------------------------------------------------
static void RankCapsules_LoadTextmap(void)
Loads UDMF map data for the result of
RankCapsules_CountFromMap.
--------------------------------------------------*/
static void RankCapsules_LoadTextmap(void)
{
size_t i;
for (i = 0; i < g_rankCapsules_nummapthings; i++)
{
const char *param, *val;
M_TokenizerSetEndPos(g_rankCapsules_mapthingsPos[i]);
param = M_TokenizerRead(0);
if (!fastcmp(param, "{"))
{
continue;
}
while (true)
{
param = M_TokenizerRead(0);
if (fastcmp(param, "}"))
{
break;
}
val = M_TokenizerRead(1);
if (fastcmp(param, "type"))
{
UINT16 type = atol(val);
if (type == mobjinfo[MT_BATTLECAPSULE].doomednum)
{
g_rankCapsules_count++;
}
break;
}
}
}
}
/*--------------------------------------------------
static void RankCapsules_LoadThingsLump(UINT8 *data)
Loads binary map data for the result of
RankCapsules_CountFromMap.
Input Arguments:-
data - Pointer to a THINGS lump.
Return:-
N/A
--------------------------------------------------*/
static void RankCapsules_LoadThingsLump(UINT8 *data)
{
size_t i;
for (i = 0; i < g_rankCapsules_nummapthings; i++)
{
UINT16 type = 0;
data += 2; // x
data += 2; // y
data += 2; // angle
type = READUINT16(data); // type
type &= 4095;
data += 2; // options
if (type == mobjinfo[MT_BATTLECAPSULE].doomednum)
{
g_rankCapsules_count++;
}
}
}
/*--------------------------------------------------
static boolean RankCapsules_LoadMapData(const virtres_t *virt)
Loads either UDMF or binary map data, for the
result of RankCapsules_CountFromMap.
Input Arguments:-
virt - Pointer to the map's virtual resource.
Return:-
true if we could successfully load the map data,
otherwise false.
--------------------------------------------------*/
static boolean RankCapsules_LoadMapData(const virtres_t *virt)
{
virtlump_t *virtthings = NULL;
// Count map data.
if (g_rankCapsules_udmf) // Count how many entries for each type we got in textmap.
{
virtlump_t *textmap = vres_Find(virt, "TEXTMAP");
M_TokenizerOpen((char *)textmap->data);
if (!RankCapsules_TextmapCount(textmap->size))
{
M_TokenizerClose();
return false;
}
}
else
{
virtthings = vres_Find(virt, "THINGS");
if (!virtthings)
{
return false;
}
// Traditional doom map format just assumes the number of elements from the lump sizes.
g_rankCapsules_nummapthings = virtthings->size / (5 * sizeof (INT16));
}
// Load map data.
if (g_rankCapsules_udmf)
{
RankCapsules_LoadTextmap();
M_TokenizerClose();
}
else
{
RankCapsules_LoadThingsLump(virtthings->data);
}
return true;
}
/*--------------------------------------------------
static UINT32 RankCapsules_CountFromMap(const virtres_t *virt)
Counts the number of capsules in a map, without
needing to fully load it.
Input Arguments:-
virt - Pointer to the map's virtual resource.
Return:-
Number of MT_BATTLECAPSULE instances found.
--------------------------------------------------*/
static UINT32 RankCapsules_CountFromMap(const virtres_t *virt)
{
virtlump_t *textmap = vres_Find(virt, "TEXTMAP");
g_rankCapsules_udmf = (textmap != NULL);
g_rankCapsules_count = 0;
if (RankCapsules_LoadMapData(virt) == true)
{
return g_rankCapsules_count;
}
return 0;
}
/*--------------------------------------------------
void K_InitGrandPrixRank(gpRank_t *rankData)
See header file for description.
--------------------------------------------------*/
void K_InitGrandPrixRank(gpRank_t *rankData)
{
UINT8 numHumans = 0;
UINT32 laps = 0;
INT32 i;
memset(rankData, 0, sizeof(gpRank_t));
if (grandprixinfo.cup == NULL)
{
return;
}
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
{
if (numHumans < MAXSPLITSCREENPLAYERS && players[i].spectator == false)
{
numHumans++;
}
}
}
// Calculate players
rankData->players = numHumans;
rankData->totalPlayers = K_GetGPPlayerCount(numHumans);
// Initialize to the neutral value.
rankData->position = RANK_NEUTRAL_POSITION;
// Calculate total of points
// (Should this account for all coop players?)
for (i = 0; i < numHumans; i++)
{
rankData->totalPoints += grandprixinfo.cup->numlevels * K_CalculateGPRankPoints(i + 1, rankData->totalPlayers);
}
rankData->totalRings = grandprixinfo.cup->numlevels * numHumans * 20;
for (i = 0; i < grandprixinfo.cup->numlevels; i++)
{
const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[i];
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum] != NULL)
{
laps += mapheaderinfo[cupLevelNum]->numlaps;
}
}
// +1, since 1st place laps are worth 2 pts.
for (i = 0; i < numHumans+1; i++)
{
rankData->totalLaps += laps;
}
// Search through all of the cup's bonus levels
// for an accurate count of how many capsules they have.
for (i = 0; i < grandprixinfo.cup->numbonus; i++)
{
const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_BONUS + i];
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum] != NULL)
{
lumpnum_t lp = mapheaderinfo[cupLevelNum]->lumpnum;
virtres_t *virt = NULL;
if (lp == LUMPERROR)
{
continue;
}
virt = vres_GetMap(lp);
if (virt == NULL)
{
continue;
}
rankData->totalCapsules += RankCapsules_CountFromMap(virt);
vres_Free(virt);
}
}
}
/*--------------------------------------------------
gp_rank_e K_CalculateGPGrade(gpRank_t *rankData)
See header file for description.
--------------------------------------------------*/
gp_rank_e K_CalculateGPGrade(gpRank_t *rankData)
{
static const fixed_t gradePercents[GRADE_A] = {
7*FRACUNIT/20, // D: 35% or higher
10*FRACUNIT/20, // C: 50% or higher
14*FRACUNIT/20, // B: 70% or higher
17*FRACUNIT/20 // A: 85% or higher
};
gp_rank_e retGrade = GRADE_E;
const INT32 positionWeight = 150;
const INT32 pointsWeight = 100;
const INT32 lapsWeight = 100;
const INT32 capsulesWeight = 100;
const INT32 ringsWeight = 50;
const INT32 total = positionWeight + pointsWeight + lapsWeight + capsulesWeight + ringsWeight;
const INT32 continuesPenalty = 20;
INT32 ours = 0;
fixed_t percent = 0;
if (rankData->position > 0)
{
const INT32 sc = (rankData->position - 1);
const INT32 loser = (RANK_NEUTRAL_POSITION - 1);
ours += ((loser - sc) * positionWeight) / loser;
}
if (rankData->totalPoints > 0)
{
ours += (rankData->winPoints * pointsWeight) / rankData->totalPoints;
}
if (rankData->totalLaps > 0)
{
ours += (rankData->laps * lapsWeight) / rankData->totalLaps;
}
if (rankData->totalCapsules > 0)
{
ours += (rankData->capsules * capsulesWeight) / rankData->totalCapsules;
}
if (rankData->totalRings > 0)
{
ours += (rankData->rings * ringsWeight) / rankData->totalRings;
}
ours -= rankData->continuesUsed * continuesPenalty;
percent = FixedDiv(ours, total);
for (retGrade = 0; retGrade < GRADE_A; retGrade++)
{
if (percent < gradePercents[retGrade])
{
break;
}
}
if (rankData->specialWon == true)
{
// Winning the Special Stage gives you
// a free grade increase.
retGrade++;
}
return retGrade;
}

96
src/k_rank.h Normal file
View file

@ -0,0 +1,96 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) by Sally "TehRealSalt" Cochenour
// Copyright (C) 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_rank.h
/// \brief Grand Prix mode ranking
#ifndef __K_RANK__
#define __K_RANK__
#include "doomdef.h"
#include "doomstat.h"
#ifdef __cplusplus
extern "C" {
#endif
struct gpRank_t
{
UINT8 players;
UINT8 totalPlayers;
UINT8 position;
UINT32 winPoints;
UINT32 totalPoints;
UINT32 laps;
UINT32 totalLaps;
UINT32 continuesUsed;
UINT32 capsules;
UINT32 totalCapsules;
UINT32 rings;
UINT32 totalRings;
boolean specialWon;
};
typedef enum
{
GRADE_E,
GRADE_D,
GRADE_C,
GRADE_B,
GRADE_A,
GRADE_S
} gp_rank_e;
// 3rd place is neutral, anything below is a penalty
#define RANK_NEUTRAL_POSITION (3)
/*--------------------------------------------------
void K_InitGrandPrixRank(gpRank_t *rankData);
Calculates rank requirements for a GP session.
Input Arguments:-
rankData - Pointer to struct that contains all
of the information required to calculate GP rank.
Return:-
N/A
--------------------------------------------------*/
void K_InitGrandPrixRank(gpRank_t *rankData);
/*--------------------------------------------------
gp_rank_e K_CalculateGPGrade(gpRank_t *rankData);
Calculates the player's grade using the
variables from gpRank.
Input Arguments:-
rankData - struct containing existing rank data.
Return:-
gp_rank_e representing the total grade.
--------------------------------------------------*/
gp_rank_e K_CalculateGPGrade(gpRank_t *rankData);
#ifdef __cplusplus
} // extern "C"
#endif
#endif

View file

@ -150,8 +150,10 @@ void K_DoIngameRespawn(player_t *player)
// FAULT
if ((gametyperules & GTR_CIRCUIT) && leveltime < starttime)
{
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_SECTIONRACE))
player->respawn.wp = K_GetFinishLineWaypoint()->prevwaypoints[0];
const waypoint_t *finish = K_GetFinishLineWaypoint();
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_SECTIONRACE) && finish != NULL)
player->respawn.wp = finish->prevwaypoints[0];
K_DoFault(player);
}
@ -159,6 +161,7 @@ void K_DoIngameRespawn(player_t *player)
player->ringboost = 0;
player->driftboost = player->strongdriftboost = 0;
player->gateBoost = 0;
player->sliptideZip = player->sliptideZipBoost = player->sliptideZipDelay = 0;
K_TumbleInterrupt(player);
P_ResetPlayer(player);

View file

@ -235,6 +235,29 @@ INT32 K_GetWaypointID(waypoint_t *waypoint)
return waypointid;
}
/*--------------------------------------------------
waypoint_t *K_GetWaypointFromID(INT32 waypointID)
See header file for description.
--------------------------------------------------*/
waypoint_t *K_GetWaypointFromID(INT32 waypointID)
{
waypoint_t *waypoint = NULL;
size_t i = SIZE_MAX;
for (i = 0; i < numwaypoints; i++)
{
waypoint = &waypointheap[i];
if (K_GetWaypointID(waypoint) == waypointID)
{
return waypoint;
}
}
return NULL;
}
/*--------------------------------------------------
UINT32 K_GetCircuitLength(void)
@ -578,7 +601,7 @@ static void K_DebugWaypointDrawRadius(waypoint_t *const waypoint)
spawnX = waypointmobj->x;
spawnY = waypointmobj->y;
spawnZ = waypointmobj->z + 16*mapobjectscale;
spawnZ = waypointmobj->z;
radiusOrb = P_SpawnMobj(spawnX, spawnY, spawnZ, MT_SPARK);
@ -586,8 +609,9 @@ static void K_DebugWaypointDrawRadius(waypoint_t *const waypoint)
radiusOrb->tics = 1;
radiusOrb->frame &= ~FF_TRANSMASK;
radiusOrb->frame |= FF_FULLBRIGHT;
radiusOrb->frame |= FF_FULLBRIGHT|FF_REVERSESUBTRACT;
radiusOrb->color = SKINCOLOR_PURPLE;
radiusOrb->renderflags |= RF_ALWAYSONTOP;
radiusOrb->destscale = FixedDiv(waypointmobj->radius, spriteRadius);
P_SetScale(radiusOrb, radiusOrb->destscale);
@ -627,6 +651,7 @@ void K_DebugWaypointsVisualise(void)
debugmobj->frame &= ~FF_TRANSMASK;
debugmobj->frame |= FF_FULLBRIGHT; //FF_TRANS20
debugmobj->renderflags |= RF_ALWAYSONTOP;
// There's a waypoint setup for this mobj! So draw that it's a valid waypoint and draw lines to its connections
if (waypoint != NULL)

View file

@ -141,9 +141,25 @@ INT32 K_GetWaypointNextID(waypoint_t *waypoint);
Return:-
The waypoint ID, -1 if there is no waypoint or mobj.
--------------------------------------------------*/
INT32 K_GetWaypointID(waypoint_t *waypoint);
/*--------------------------------------------------
waypoint_t *K_GetWaypointFromID(INT32 waypointID)
Returns the first waypoint with the specified ID.
Input Arguments:-
waypointID - The ID of the waypoint to get
Return:-
The first waypoint with this ID, NULL if the ID doesn't exist at all in the map
--------------------------------------------------*/
waypoint_t *K_GetWaypointFromID(INT32 waypointID);
/*--------------------------------------------------
UINT32 K_GetCircuitLength(void)

View file

@ -308,6 +308,12 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->tripwireLeniency);
else if (fastcmp(field,"tripwireReboundDelay"))
lua_pushinteger(L, plr->tripwireReboundDelay);
else if (fastcmp(field,"sliptideZip"))
lua_pushinteger(L, plr->sliptideZip);
else if (fastcmp(field,"sliptideZipDelay"))
lua_pushinteger(L, plr->sliptideZipDelay);
else if (fastcmp(field,"sliptideZipBoost"))
lua_pushinteger(L, plr->sliptideZipBoost);
/*
else if (fastcmp(field,"itemroulette"))
lua_pushinteger(L, plr->itemroulette);
@ -482,12 +488,10 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->timeshitprev);
else if (fastcmp(field,"onconveyor"))
lua_pushinteger(L, plr->onconveyor);
else if (fastcmp(field,"awayviewmobj"))
LUA_PushUserdata(L, plr->awayviewmobj, META_MOBJ);
else if (fastcmp(field,"awayviewtics"))
lua_pushinteger(L, plr->awayviewtics);
else if (fastcmp(field,"awayviewaiming"))
lua_pushangle(L, plr->awayviewaiming);
else if (fastcmp(field,"awayviewmobj")) // FIXME: struct
LUA_PushUserdata(L, plr->awayview.mobj, META_MOBJ);
else if (fastcmp(field,"awayviewtics")) // FIXME: struct
lua_pushinteger(L, plr->awayview.tics);
else if (fastcmp(field,"spectator"))
lua_pushboolean(L, plr->spectator);
@ -688,6 +692,12 @@ static int player_set(lua_State *L)
plr->tripwireLeniency = luaL_checkinteger(L, 3);
else if (fastcmp(field,"tripwireReboundDelay"))
plr->tripwireReboundDelay = luaL_checkinteger(L, 3);
else if (fastcmp(field,"sliptideZip"))
plr->sliptideZip = luaL_checkinteger(L, 3);
else if (fastcmp(field,"sliptideZipDelay"))
plr->sliptideZipDelay = luaL_checkinteger(L, 3);
else if (fastcmp(field,"sliptideZipBoost"))
plr->sliptideZipBoost = luaL_checkinteger(L, 3);
/*
else if (fastcmp(field,"itemroulette"))
plr->itemroulette = luaL_checkinteger(L, 3);
@ -848,21 +858,19 @@ static int player_set(lua_State *L)
plr->timeshitprev = (UINT8)luaL_checkinteger(L, 3);
else if (fastcmp(field,"onconveyor"))
plr->onconveyor = (INT32)luaL_checkinteger(L, 3);
else if (fastcmp(field,"awayviewmobj"))
else if (fastcmp(field,"awayviewmobj")) // FIXME: struct
{
mobj_t *mo = NULL;
if (!lua_isnil(L, 3))
mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
P_SetTarget(&plr->awayviewmobj, mo);
P_SetTarget(&plr->awayview.mobj, mo);
}
else if (fastcmp(field,"awayviewtics"))
else if (fastcmp(field,"awayviewtics")) // FIXME: struct
{
plr->awayviewtics = (INT32)luaL_checkinteger(L, 3);
if (plr->awayviewtics && !plr->awayviewmobj) // awayviewtics must ALWAYS have an awayviewmobj set!!
P_SetTarget(&plr->awayviewmobj, plr->mo); // but since the script might set awayviewmobj immediately AFTER setting awayviewtics, use player mobj as filler for now.
plr->awayview.tics = (INT32)luaL_checkinteger(L, 3);
if (plr->awayview.tics && !plr->awayview.mobj) // awayviewtics must ALWAYS have an awayviewmobj set!!
P_SetTarget(&plr->awayview.mobj, plr->mo); // but since the script might set awayviewmobj immediately AFTER setting awayviewtics, use player mobj as filler for now.
}
else if (fastcmp(field,"awayviewaiming"))
plr->awayviewaiming = luaL_checkangle(L, 3);
else if (fastcmp(field,"spectator"))
plr->spectator = lua_toboolean(L, 3);
else if (fastcmp(field,"bot"))

View file

@ -212,7 +212,7 @@ int LUA_PushGlobals(lua_State *L, const char *word)
lua_pushstring(L, titlemap);
return 1;
} else if (fastcmp(word,"titlemapinaction")) {
lua_pushboolean(L, (titlemapinaction != TITLEMAP_OFF));
lua_pushboolean(L, titlemapinaction);
return 1;
} else if (fastcmp(word,"bootmap")) {
lua_pushstring(L, bootmap);
@ -223,6 +223,9 @@ int LUA_PushGlobals(lua_State *L, const char *word)
} else if (fastcmp(word,"tutorialmode")) {
lua_pushboolean(L, tutorialmode);
return 1;
} else if (fastcmp(word,"podiummap")) {
lua_pushstring(L, podiummap);
return 1;
// end map vars
// begin CTF colors
} else if (fastcmp(word,"skincolor_redteam")) {

View file

@ -139,7 +139,7 @@ void COM_Lua_f(void);
// #define HAVE_LUA_SEGS
#define ISINLEVEL \
(gamestate == GS_LEVEL || titlemapinaction)
(G_GamestateUsesLevel())
#define INLEVEL if (! ISINLEVEL)\
return luaL_error(L, "This can only be used in a level!");

View file

@ -255,10 +255,7 @@ static void M_DrawRenderStats(void)
perfstatcol_t batchcalls_col = {220, 200, V_PURPLEMAP, batchcalls_row};
boolean rendering = (
gamestate == GS_LEVEL ||
(gamestate == GS_TITLESCREEN && titlemapinaction)
);
boolean rendering = G_GamestateUsesLevel();
draw_row = 10;
M_DrawPerfTiming(&frametime_col);
@ -619,8 +616,9 @@ void M_DrawPerfStats(void)
}
else if (cv_perfstats.value == PS_THINKFRAME) // lua thinkframe
{
if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction)))
if (G_GamestateUsesLevel() == false)
return;
if (vid.width < 640 || vid.height < 400) // low resolution
{
// it's not gonna fit very well..

View file

@ -4165,8 +4165,8 @@ void A_OverlayThink(mobj_t *actor)
{
angle_t viewingangle;
if (players[displayplayers[0]].awayviewtics)
viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayers[0]].awayviewmobj->x, players[displayplayers[0]].awayviewmobj->y);
if (players[displayplayers[0]].awayview.tics)
viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayers[0]].awayview.mobj->x, players[displayplayers[0]].awayview.mobj->y);
else if (!camera[0].chase && players[displayplayers[0]].mo)
viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayers[0]].mo->x, players[displayplayers[0]].mo->y);
else
@ -9390,6 +9390,10 @@ void A_SetScale(mobj_t *actor)
}
locvar1 = FixedMul(locvar1, mapobjectscale); // SRB2Kart
if (target->spawnpoint != NULL)
{
locvar1 = FixedMul(locvar1, target->spawnpoint->scale);
}
target->destscale = locvar1; // destination scale
if (!(locvar2 & 65535))

View file

@ -1934,14 +1934,13 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source,
return false;
}
K_DestroyBumpers(player, 1);
switch (type)
{
case DMG_DEATHPIT:
// Respawn kill types
player->roundconditions.fell_off = true;
K_DoIngameRespawn(player);
player->mo->health -= K_DestroyBumpers(player, 1);
return false;
case DMG_SPECTATOR:
// disappearifies, but still gotta put items back in play
@ -1998,10 +1997,11 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source,
P_SetTarget(&boom->target, player->mo);
}
K_DestroyBumpers(player, player->bumpers);
player->pflags |= PF_ELIMINATED;
}
K_DestroyBumpers(player, player->bumpers);
return true;
}
@ -2164,6 +2164,9 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
const boolean hardhit = (type == DMG_EXPLODE || type == DMG_KARMA || type == DMG_TUMBLE); // This damage type can do evil stuff like ALWAYS combo
INT16 ringburst = 5;
// Do not die from damage outside of bumpers health system
damage = 0;
// Check if the player is allowed to be damaged!
// If not, then spawn the instashield effect instead.
if (!force)
@ -2293,12 +2296,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
K_TryHurtSoundExchange(target, source);
K_BattleAwardHit(source->player, player, inflictor, takeBumpers);
K_TakeBumpersFromPlayer(source->player, player, takeBumpers);
damage = K_TakeBumpersFromPlayer(source->player, player, takeBumpers);
if (type == DMG_KARMA)
{
// Destroy any remainder bumpers from the player for karma comeback damage
K_DestroyBumpers(player, player->bumpers);
damage = K_DestroyBumpers(player, player->bumpers);
}
else
{
@ -2321,7 +2324,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
}
else
{
K_DestroyBumpers(player, takeBumpers);
damage = K_DestroyBumpers(player, takeBumpers);
}
if (!(damagetype & DMG_STEAL))
@ -2407,15 +2410,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (type != DMG_STUMBLE)
{
player->instashield = 15;
K_SetHitLagForObjects(target, inflictor, laglength, true);
}
if (inflictor && !P_MobjWasRemoved(inflictor) && inflictor->type == MT_BANANA)
{
player->flipDI = true;
}
return true;
}
}
else
@ -2451,16 +2451,16 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
//K_SetHitLagForObjects(target, inflictor, laglength, true);
if (player)
P_ResetPlayer(target->player);
else
if (!player)
{
P_SetMobjState(target, target->info->painstate);
if (!P_MobjWasRemoved(target))
{
// if not intent on another player,
// chase after this one
P_SetTarget(&target->target, source);
if (!P_MobjWasRemoved(target))
{
// if not intent on another player,
// chase after this one
P_SetTarget(&target->target, source);
}
}
return true;

View file

@ -66,6 +66,10 @@ extern "C" {
#define P_GetPlayerViewHeight(player) (41*player->mo->height/48)
#ifdef PARANOIA
#define SCRAMBLE_REMOVED // Force debug build to crash when Removed mobj is accessed
#endif
typedef enum
{
THINK_POLYOBJ,
@ -197,6 +201,8 @@ void P_HaltPlayerOrbit(player_t *player);
void P_ExitPlayerOrbit(player_t *player);
boolean P_PlayerOrbit(player_t *player);
void P_TickAltView(altview_t *view);
void P_MovePlayer(player_t *player);
void P_PlayerThink(player_t *player);
void P_PlayerAfterThink(player_t *player);
@ -282,6 +288,9 @@ extern mapthing_t *itemrespawnque[ITEMQUESIZE];
extern tic_t itemrespawntime[ITEMQUESIZE];
extern size_t iquehead, iquetail;
extern consvar_t cv_gravity, cv_movebob;
#ifdef SCRAMBLE_REMOVED
extern consvar_t cv_scrambleremoved;
#endif
void P_RespawnBattleBoxes(void);
mobjtype_t P_GetMobjtype(UINT16 mthingtype);

View file

@ -47,10 +47,14 @@
#include "k_objects.h"
#include "k_grandprix.h"
#include "k_director.h"
#include "m_easing.h"
#include "k_podium.h"
static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}};
consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL);
consvar_t cv_scrambleremoved = CVAR_INIT ("scrambleremoved", "On", CV_NETVAR, CV_OnOff, NULL);
actioncache_t actioncachehead;
static mobj_t *overlaycap = NULL;
@ -3783,15 +3787,16 @@ void P_CalcChasePostImg(player_t *player, camera_t *thiscam)
{
postimg = postimg_mirror;
}
else if (player->awayviewtics && player->awayviewmobj && !P_MobjWasRemoved(player->awayviewmobj)) // Camera must obviously exist
else if (player->awayview.tics && player->awayview.mobj && !P_MobjWasRemoved(player->awayview.mobj)) // Camera must obviously exist
{
camera_t dummycam;
dummycam.subsector = player->awayviewmobj->subsector;
dummycam.x = player->awayviewmobj->x;
dummycam.y = player->awayviewmobj->y;
dummycam.z = player->awayviewmobj->z;
//dummycam.height = 40*FRACUNIT; // alt view height is 20*FRACUNIT
dummycam.height = 0; // Why? Remote viewpoint cameras have no height.
dummycam.subsector = player->awayview.mobj->subsector;
dummycam.x = player->awayview.mobj->x;
dummycam.y = player->awayview.mobj->y;
dummycam.z = player->awayview.mobj->z;
dummycam.height = 0;
// Are we in water?
if (P_CameraCheckWater(&dummycam))
postimg = postimg_water;
@ -5269,6 +5274,11 @@ static boolean P_IsTrackerType(INT32 type)
case MT_PLAYER:
return true;
case MT_OVERTIME_CENTER:
case MT_MONITOR:
case MT_EMERALD:
return true;
default:
return false;
}
@ -6390,6 +6400,7 @@ static void P_MobjSceneryThink(mobj_t *mobj)
numx->destscale = scale;
}
#if 0
if (K_IsPlayerWanted(mobj->target->player) && mobj->movecount != 1)
{
mobj_t *wanted = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_PLAYERWANTED);
@ -6400,6 +6411,7 @@ static void P_MobjSceneryThink(mobj_t *mobj)
mobj->movecount = 1;
}
else if (!K_IsPlayerWanted(mobj->target->player))
#endif
mobj->movecount = 0;
}
else
@ -8092,8 +8104,8 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
angle_t viewingangle;
statenum_t curstate = ((mobj->tics == 1) ? (mobj->state->nextstate) : ((statenum_t)(mobj->state-states)));
if (players[displayplayers[0]].awayviewtics)
viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].awayviewmobj->x, players[displayplayers[0]].awayviewmobj->y);
if (players[displayplayers[0]].awayview.tics)
viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].awayview.mobj->x, players[displayplayers[0]].awayview.mobj->y);
else if (!camera[0].chase && players[displayplayers[0]].mo)
viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].mo->x, players[displayplayers[0]].mo->y);
else
@ -8223,8 +8235,8 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
{
angle_t viewingangle;
if (players[displayplayers[0]].awayviewtics)
viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].awayviewmobj->x, players[displayplayers[0]].awayviewmobj->y);
if (players[displayplayers[0]].awayview.tics)
viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].awayview.mobj->x, players[displayplayers[0]].awayview.mobj->y);
else if (!camera[0].chase && players[displayplayers[0]].mo)
viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].mo->x, players[displayplayers[0]].mo->y);
else
@ -8328,8 +8340,8 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
{
angle_t viewingangle;
if (players[displayplayers[0]].awayviewtics)
viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].awayviewmobj->x, players[displayplayers[0]].awayviewmobj->y);
if (players[displayplayers[0]].awayview.tics)
viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].awayview.mobj->x, players[displayplayers[0]].awayview.mobj->y);
else if (!camera[0].chase && players[displayplayers[0]].mo)
viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].mo->x, players[displayplayers[0]].mo->y);
else
@ -9491,6 +9503,66 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
case MT_MONITOR_PART:
Obj_MonitorPartThink(mobj);
break;
case MT_ALTVIEWMAN:
{
mobj->momx = mobj->momy = mobj->momz = 0;
if (mobj->movefactor <= 0)
{
mobj->movefactor = FRACUNIT / TICRATE; // default speed
}
if (mobj->tracer != NULL && P_MobjWasRemoved(mobj->tracer) == false)
{
fixed_t newX = Easing_Linear(mobj->movecount, mobj->extravalue1, mobj->tracer->x);
fixed_t newY = Easing_Linear(mobj->movecount, mobj->extravalue2, mobj->tracer->y);
fixed_t newZ = Easing_Linear(mobj->movecount, mobj->cusval, mobj->tracer->z);
mobj->angle = Easing_Linear(mobj->movecount, mobj->movedir, mobj->tracer->angle);
mobj->pitch = Easing_Linear(mobj->movecount, mobj->lastlook, mobj->tracer->pitch);
mobj->momx = newX - mobj->x;
mobj->momy = newY - mobj->y;
mobj->momz = newZ - mobj->z;
mobj->movecount += mobj->movefactor;
if (mobj->movecount >= FRACUNIT)
{
mobj->movecount = mobj->movecount % FRACUNIT; // time
mobj->movedir = mobj->tracer->angle; // start angle
mobj->lastlook = mobj->tracer->pitch; // start pitch
mobj->extravalue1 = mobj->tracer->x; // start x
mobj->extravalue2 = mobj->tracer->y; // start y
mobj->cusval = mobj->tracer->z; // start z
P_SetTarget(&mobj->tracer, P_GetNextTubeWaypoint(mobj->tracer, false));
}
}
// If target is valid, then we'll focus on it.
if (mobj->target != NULL && P_MobjWasRemoved(mobj->target) == false)
{
mobj->angle = R_PointToAngle2(
mobj->x,
mobj->y,
mobj->target->x,
mobj->target->y
);
mobj->pitch = R_PointToAngle2(
0,
mobj->z,
R_PointToDist2(
mobj->x, mobj->y,
mobj->target->x, mobj->target->y
),
mobj->target->z + (mobj->target->height >> 1)
);
}
}
break;
default:
// check mobj against possible water content, before movement code
P_MobjCheckWater(mobj);
@ -10468,9 +10540,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
else
switch (mobj->type)
{
case MT_ALTVIEWMAN:
if (titlemapinaction) mobj->flags &= ~MF_NOTHINK;
break;
case MT_LOCKONINF:
P_SetScale(mobj, (mobj->destscale = 3*mobj->scale));
break;
@ -11056,9 +11125,6 @@ mapthing_t *itemrespawnque[ITEMQUESIZE];
tic_t itemrespawntime[ITEMQUESIZE];
size_t iquehead, iquetail;
#ifdef PARANOIA
#define SCRAMBLE_REMOVED // Force debug build to crash when Removed mobj is accessed
#endif
void P_RemoveMobj(mobj_t *mobj)
{
I_Assert(mobj != NULL);
@ -11194,7 +11260,10 @@ void P_RemoveMobj(mobj_t *mobj)
// DBG: set everything in mobj_t to 0xFF instead of leaving it. debug memory error.
#ifdef SCRAMBLE_REMOVED
// Invalidate mobj_t data to cause crashes if accessed!
memset((UINT8 *)mobj + sizeof(thinker_t), 0xff, sizeof(mobj_t) - sizeof(thinker_t));
if (cv_scrambleremoved.value)
{
memset((UINT8 *)mobj + sizeof(thinker_t), 0xff, sizeof(mobj_t) - sizeof(thinker_t));
}
#endif
}
@ -11665,7 +11734,9 @@ void P_SpawnPlayer(INT32 playernum)
mobj_t *mobj;
if (p->playerstate == PST_REBORN)
G_PlayerReborn(playernum, false);
{
G_PlayerReborn(playernum, (p->jointime <= 1));
}
for (i = 0; i < MAXPLAYERS; i++)
{
@ -11690,7 +11761,8 @@ void P_SpawnPlayer(INT32 playernum)
}
else if (p->bot)
{
if (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE)
if (K_PodiumSequence() == false
&& (!(gametyperules & GTR_BOTS) || (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE)))
{
// Bots aren't supposed to be here.
p->spectator = true;
@ -11754,7 +11826,7 @@ void P_SpawnPlayer(INT32 playernum)
p->skincolor = skincolor_blueteam;
}
if (leveltime > introtime)
if (leveltime > introtime && K_PodiumSequence() == false)
p->flashing = K_GetKartFlashing(p); // Babysitting deterrent
mobj = P_SpawnMobj(0, 0, 0, MT_PLAYER);
@ -11798,6 +11870,8 @@ void P_SpawnPlayer(INT32 playernum)
K_InitStumbleIndicator(p);
K_InitSliptideZipIndicator(p);
if (gametyperules & GTR_ITEMARROWS)
{
mobj_t *overheadarrow = P_SpawnMobj(mobj->x, mobj->y, mobj->z + mobj->height + 16*FRACUNIT, MT_PLAYERARROW);
@ -11833,6 +11907,11 @@ void P_SpawnPlayer(INT32 playernum)
p->bumpers = K_StartingBumperCount();
K_SpawnPlayerBattleBumpers(p);
}
if (p->bumpers > 0)
{
mobj->health = p->bumpers;
}
}
// I'm not refactoring the loop at the top of this file.
@ -11894,6 +11973,11 @@ void P_AfterPlayerSpawn(INT32 playernum)
if (CheckForReverseGravity)
P_CheckGravity(mobj, false);
if (K_PodiumSequence() == true)
{
K_InitializePodiumWaypoint(p);
}
}
// spawn it at a playerspawn mapthing
@ -11976,7 +12060,7 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing)
mobj->angle = angle;
// FAULT
if (leveltime > introtime && !p->spectator)
if (gamestate == GS_LEVEL && leveltime > introtime && !p->spectator)
{
K_DoIngameRespawn(p);
}
@ -12054,6 +12138,7 @@ void P_MovePlayerToStarpost(INT32 playernum)
fixed_t P_GetMobjSpawnHeight(const mobjtype_t mobjtype, const fixed_t x, const fixed_t y, const fixed_t dz, const fixed_t offset, const boolean flip, const fixed_t scale)
{
const fixed_t finalScale = FixedMul(scale, mapobjectscale);
const subsector_t *ss = R_PointInSubsector(x, y);
// Axis objects snap to the floor.
@ -12062,9 +12147,9 @@ fixed_t P_GetMobjSpawnHeight(const mobjtype_t mobjtype, const fixed_t x, const f
// Establish height.
if (flip)
return P_GetSectorCeilingZAt(ss->sector, x, y) - dz - FixedMul(scale, offset + mobjinfo[mobjtype].height);
return P_GetSectorCeilingZAt(ss->sector, x, y) - dz - FixedMul(finalScale, offset + mobjinfo[mobjtype].height);
else
return P_GetSectorFloorZAt(ss->sector, x, y) + dz + FixedMul(scale, offset);
return P_GetSectorFloorZAt(ss->sector, x, y) + dz + FixedMul(finalScale, offset);
}
fixed_t P_GetMapThingSpawnHeight(const mobjtype_t mobjtype, const mapthing_t* mthing, const fixed_t x, const fixed_t y)
@ -13363,16 +13448,14 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y, fixed_t z, mobjtype_t i)
{
fixed_t relativise = FixedDiv(mthing->scale, mapobjectscale);
mobj_t *mobj = NULL;
boolean doangle = true;
mobj = P_SpawnMobj(x, y, z, i);
mobj->spawnpoint = mthing;
P_SetScale(mobj, FixedMul(mobj->scale, relativise));
mobj->destscale = FixedMul(mobj->destscale, relativise);
P_SetScale(mobj, FixedMul(mobj->scale, mthing->scale));
mobj->destscale = FixedMul(mobj->destscale, mthing->scale);
if (!P_SetupSpawnedMapThing(mthing, mobj, &doangle))
return mobj;

View file

@ -67,6 +67,7 @@ typedef enum
SKYBOXCENTER = 0x10,
HOVERHYUDORO = 0x20,
STUMBLE = 0x40,
SLIPTIDEZIP = 0x80
} player_saveflags;
static inline void P_ArchivePlayer(savebuffer_t *save)
@ -137,8 +138,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEANGLE(save->p, players[i].drawangle);
WRITEANGLE(save->p, players[i].viewrollangle);
WRITEANGLE(save->p, players[i].tilt);
WRITEANGLE(save->p, players[i].awayviewaiming);
WRITEINT32(save->p, players[i].awayviewtics);
WRITEINT32(save->p, players[i].awayview.tics);
WRITEUINT8(save->p, players[i].playerstate);
WRITEUINT32(save->p, players[i].pflags);
@ -179,6 +179,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT32(save->p, players[i].realtime);
WRITEUINT8(save->p, players[i].laps);
WRITEUINT8(save->p, players[i].latestlap);
WRITEUINT32(save->p, players[i].lapPoints);
WRITEINT32(save->p, players[i].starpostnum);
WRITEUINT8(save->p, players[i].ctfteam);
@ -197,7 +198,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT8(save->p, players[i].splitscreenindex);
if (players[i].awayviewmobj)
if (players[i].awayview.mobj)
flags |= AWAYVIEW;
if (players[i].followmobj)
@ -218,6 +219,9 @@ static void P_NetArchivePlayers(savebuffer_t *save)
if (players[i].stumbleIndicator)
flags |= STUMBLE;
if (players[i].sliptideZipIndicator)
flags |= SLIPTIDEZIP;
WRITEUINT16(save->p, flags);
if (flags & SKYBOXVIEW)
@ -227,7 +231,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT32(save->p, players[i].skybox.centerpoint->mobjnum);
if (flags & AWAYVIEW)
WRITEUINT32(save->p, players[i].awayviewmobj->mobjnum);
WRITEUINT32(save->p, players[i].awayview.mobj->mobjnum);
if (flags & FOLLOWITEM)
WRITEUINT32(save->p, players[i].followmobj->mobjnum);
@ -238,6 +242,9 @@ static void P_NetArchivePlayers(savebuffer_t *save)
if (flags & STUMBLE)
WRITEUINT32(save->p, players[i].stumbleIndicator->mobjnum);
if (flags & SLIPTIDEZIP)
WRITEUINT32(save->p, players[i].sliptideZipIndicator->mobjnum);
WRITEUINT32(save->p, (UINT32)players[i].followitem);
WRITEUINT32(save->p, players[i].charflags);
@ -397,6 +404,10 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT8(save->p, players[i].tripwireReboundDelay);
WRITEUINT16(save->p, players[i].sliptideZip);
WRITEUINT8(save->p, players[i].sliptideZipDelay);
WRITEUINT16(save->p, players[i].sliptideZipBoost);
// respawnvars_t
WRITEUINT8(save->p, players[i].respawn.state);
WRITEUINT32(save->p, K_GetWaypointHeapIndex(players[i].respawn.wp));
@ -527,8 +538,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].drawangle = players[i].old_drawangle = READANGLE(save->p);
players[i].viewrollangle = READANGLE(save->p);
players[i].tilt = READANGLE(save->p);
players[i].awayviewaiming = READANGLE(save->p);
players[i].awayviewtics = READINT32(save->p);
players[i].awayview.tics = READINT32(save->p);
players[i].playerstate = READUINT8(save->p);
players[i].pflags = READUINT32(save->p);
@ -569,6 +579,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].realtime = READUINT32(save->p); // integer replacement for leveltime
players[i].laps = READUINT8(save->p); // Number of laps (optional)
players[i].latestlap = READUINT8(save->p);
players[i].lapPoints = READUINT32(save->p);
players[i].starpostnum = READINT32(save->p);
players[i].ctfteam = READUINT8(save->p); // 1 == Red, 2 == Blue
@ -596,7 +607,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].skybox.centerpoint = (mobj_t *)(size_t)READUINT32(save->p);
if (flags & AWAYVIEW)
players[i].awayviewmobj = (mobj_t *)(size_t)READUINT32(save->p);
players[i].awayview.mobj = (mobj_t *)(size_t)READUINT32(save->p);
if (flags & FOLLOWITEM)
players[i].followmobj = (mobj_t *)(size_t)READUINT32(save->p);
@ -607,6 +618,9 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
if (flags & STUMBLE)
players[i].stumbleIndicator = (mobj_t *)(size_t)READUINT32(save->p);
if (flags & SLIPTIDEZIP)
players[i].sliptideZipIndicator = (mobj_t *)(size_t)READUINT32(save->p);
players[i].followitem = (mobjtype_t)READUINT32(save->p);
//SetPlayerSkinByNum(i, players[i].skin);
@ -767,6 +781,10 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].tripwireReboundDelay = READUINT8(save->p);
players[i].sliptideZip = READUINT16(save->p);
players[i].sliptideZipDelay = READUINT8(save->p);
players[i].sliptideZipBoost = READUINT16(save->p);
// respawnvars_t
players[i].respawn.state = READUINT8(save->p);
players[i].respawn.wp = (waypoint_t *)(size_t)READUINT32(save->p);
@ -4684,12 +4702,12 @@ static void P_RelinkPointers(void)
if (!P_SetTarget(&players[i].skybox.centerpoint, P_FindNewPosition(temp)))
CONS_Debug(DBG_GAMELOGIC, "skybox.centerpoint not found on player %d\n", i);
}
if (players[i].awayviewmobj)
if (players[i].awayview.mobj)
{
temp = (UINT32)(size_t)players[i].awayviewmobj;
players[i].awayviewmobj = NULL;
if (!P_SetTarget(&players[i].awayviewmobj, P_FindNewPosition(temp)))
CONS_Debug(DBG_GAMELOGIC, "awayviewmobj not found on player %d\n", i);
temp = (UINT32)(size_t)players[i].awayview.mobj;
players[i].awayview.mobj = NULL;
if (!P_SetTarget(&players[i].awayview.mobj, P_FindNewPosition(temp)))
CONS_Debug(DBG_GAMELOGIC, "awayview.mobj not found on player %d\n", i);
}
if (players[i].followmobj)
{
@ -4746,6 +4764,13 @@ static void P_RelinkPointers(void)
if (!P_SetTarget(&players[i].stumbleIndicator, P_FindNewPosition(temp)))
CONS_Debug(DBG_GAMELOGIC, "stumbleIndicator not found on player %d\n", i);
}
if (players[i].sliptideZipIndicator)
{
temp = (UINT32)(size_t)players[i].sliptideZipIndicator;
players[i].sliptideZipIndicator = NULL;
if (!P_SetTarget(&players[i].sliptideZipIndicator, P_FindNewPosition(temp)))
CONS_Debug(DBG_GAMELOGIC, "sliptideZipIndicator not found on player %d\n", i);
}
}
}
@ -4983,8 +5008,6 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending)
WRITESINT8(save->p, speedscramble);
WRITESINT8(save->p, encorescramble);
WRITEUINT32(save->p, g_hiscore);
// battleovertime_t
WRITEUINT16(save->p, battleovertime.enabled);
WRITEFIXED(save->p, battleovertime.radius);
@ -5154,8 +5177,6 @@ static inline boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading)
speedscramble = READSINT8(save->p);
encorescramble = READSINT8(save->p);
g_hiscore = READUINT32(save->p);
// battleovertime_t
battleovertime.enabled = READUINT16(save->p);
battleovertime.radius = READFIXED(save->p);

View file

@ -101,6 +101,8 @@
#include "k_specialstage.h"
#include "acs/interface.h"
#include "doomstat.h" // MAXMUSNAMES
#include "k_podium.h"
#include "k_rank.h"
// Replay names have time
#if !defined (UNDER_CE)
@ -1278,7 +1280,7 @@ static void P_LoadThings(UINT8 *data)
mt->options = READUINT16(data);
mt->extrainfo = (UINT8)(mt->type >> 12);
Tag_FSet(&mt->tags, 0);
mt->scale = mapobjectscale;
mt->scale = FRACUNIT;
memset(mt->args, 0, NUMMAPTHINGARGS*sizeof(*mt->args));
memset(mt->stringargs, 0x00, NUMMAPTHINGSTRINGARGS*sizeof(*mt->stringargs));
mt->special = 0;
@ -1783,7 +1785,7 @@ static void ParseTextmapThingParameter(UINT32 i, const char *param, const char *
else if (fastcmp(param, "type"))
mapthings[i].type = atol(val);
else if (fastcmp(param, "scale") || fastcmp(param, "scalex") || fastcmp(param, "scaley"))
mapthings[i].scale = FixedMul(mapobjectscale, FLOAT_TO_FIXED(atof(val)));
mapthings[i].scale = FLOAT_TO_FIXED(atof(val));
// Flags
else if (fastcmp(param, "flip") && fastcmp("true", val))
mapthings[i].options |= MTF_OBJECTFLIP;
@ -2731,7 +2733,7 @@ static void P_LoadTextmap(void)
mt->z = 0;
mt->extrainfo = 0;
Tag_FSet(&mt->tags, 0);
mt->scale = mapobjectscale;
mt->scale = FRACUNIT;
memset(mt->args, 0, NUMMAPTHINGARGS*sizeof(*mt->args));
memset(mt->stringargs, 0x00, NUMMAPTHINGSTRINGARGS*sizeof(*mt->stringargs));
mt->special = 0;
@ -7083,7 +7085,11 @@ static void P_InitLevelSettings(void)
gamespeed = KARTSPEED_EASY;
franticitems = false;
if (grandprixinfo.gp == true)
if (K_PodiumSequence() == true)
{
; // NOP
}
else if (grandprixinfo.gp == true)
{
if (gametyperules & GTR_CIRCUIT)
{
@ -7109,8 +7115,6 @@ static void P_InitLevelSettings(void)
franticitems = (boolean)cv_kartfrantic.value;
}
g_hiscore = 0;
memset(&battleovertime, 0, sizeof(struct battleovertime));
speedscramble = encorescramble = -1;
@ -7359,7 +7363,7 @@ static void P_InitPlayers(void)
players[i].mo = NULL;
if (!(gametyperules & GTR_CIRCUIT))
if (!(gametyperules & GTR_CIRCUIT) && K_PodiumSequence() == false)
{
G_DoReborn(i);
}
@ -7368,6 +7372,8 @@ static void P_InitPlayers(void)
G_SpawnPlayer(i);
}
}
K_UpdateAllPlayerPositions();
}
static void P_InitGametype(void)
@ -7377,6 +7383,26 @@ static void P_InitGametype(void)
spectateGriefed = 0;
K_CashInPowerLevels(); // Pushes power level changes even if intermission was skipped
if (grandprixinfo.gp == true)
{
if (grandprixinfo.initalize == true)
{
K_InitGrandPrixRank(&grandprixinfo.rank);
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_InitPlayers();
if (modeattacking && !demo.playback)
@ -7419,7 +7445,7 @@ static void P_InitGametype(void)
// Start recording replay in multiplayer with a temp filename
//@TODO I'd like to fix dedis crashing when recording replays for the future too...
if (!demo.playback && multiplayer && !dedicated)
if (gamestate == GS_LEVEL && !demo.playback && multiplayer && !dedicated)
{
char buf[MAX_WADPATH];
char ver[128];
@ -7698,7 +7724,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
// Fade out music here. Deduct 2 tics so the fade volume actually reaches 0.
// But don't halt the music! S_Start will take care of that. This dodges a MIDI crash bug.
if (!(reloadinggamestate || titlemapinaction))
if (!(reloadinggamestate || gamestate != GS_LEVEL))
S_FadeMusic(0, FixedMul(
FixedDiv((F_GetWipeLength(wipedefs[wipe_level_toblack])-2)*NEWTICRATERATIO, NEWTICRATE), MUSICRATE));
@ -7706,7 +7732,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
if (rendermode != render_none)
V_ReloadPalette(); // Set the level palette
if (!(reloadinggamestate || titlemapinaction))
if (!(reloadinggamestate || gamestate != GS_LEVEL))
{
if (ranspecialwipe == 2)
{
@ -7746,8 +7772,11 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
F_RunWipe(wipetype, wipedefs[wipetype], false, ((levelfadecol == 0) ? "FADEMAP1" : "FADEMAP0"), false, false);
}
/*if (!titlemapinaction)
wipegamestate = GS_LEVEL;*/
/*
if (!titlemapinaction)
wipegamestate = GS_LEVEL;
*/
// Close text prompt before freeing the old level
F_EndTextPrompt(false, true);
@ -7949,7 +7978,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
P_MapEnd(); // tm.thing is no longer needed from this point onwards
// Took me 3 hours to figure out why my progression kept on getting overwritten with the titlemap...
if (!titlemapinaction)
if (gamestate == GS_LEVEL)
{
if (!lastmaploaded) // Start a new game?
{
@ -7962,27 +7991,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
lastmaploaded = gamemap; // HAS to be set after saving!!
}
if (reloadinggamestate)
;
else 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();
}
if (!fromnetsave) // uglier hack
{ // to make a newly loaded level start on the second frame.
INT32 buf = gametic % BACKUPTICS;

View file

@ -2008,6 +2008,40 @@ static void K_HandleLapIncrement(player_t *player)
}
}
if (player->laps > player->latestlap)
{
if (player->laps > 1)
{
// save best lap for record attack
if (modeattacking && player == &players[consoleplayer])
{
if (curlap < bestlap || bestlap == 0)
{
bestlap = curlap;
}
curlap = 0;
}
// Update power levels for this lap.
K_UpdatePowerLevels(player, player->laps, false);
if (nump > 1 && K_IsPlayerLosing(player) == false)
{
if (nump > 2 && player->position == 1) // 1st place in 1v1 uses thumbs up
{
player->lapPoints += 2;
}
else
{
player->lapPoints++;
}
}
}
player->latestlap = player->laps;
}
// finished race exit setup
if (player->laps > numlaps)
{
@ -2035,32 +2069,8 @@ static void K_HandleLapIncrement(player_t *player)
SetRandomFakePlayerSkin(player, true);
}
}
if (player->laps > player->latestlap)
{
if (player->laps > 1)
{
// save best lap for record attack
if (modeattacking && player == &players[consoleplayer])
{
if (curlap < bestlap || bestlap == 0)
{
bestlap = curlap;
}
curlap = 0;
}
// Update power levels for this lap.
K_UpdatePowerLevels(player, player->laps, false);
}
player->latestlap = player->laps;
}
thwompsactive = true; // Lap 2 effects
lowestLap = P_FindLowestLap();
for (i = 0; i < numlines; i++)
@ -2413,7 +2423,7 @@ static void P_SwitchSkybox(INT32 args, player_t *player, skybox_t *skybox)
}
}
static mobj_t* P_FindObjectTypeFromTag(mobjtype_t type, mtag_t tag)
mobj_t* P_FindObjectTypeFromTag(mobjtype_t type, mtag_t tag)
{
if (udmf)
{
@ -3002,35 +3012,139 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha
case 422: // Cut away to another view
{
mobj_t *altview;
INT32 aim;
altview_t *modifyView = NULL;
mobj_t *newViewMobj = NULL;
if ((!mo || !mo->player) && !titlemapinaction) // only players have views, and title screens
return false;
altview = P_FindObjectTypeFromTag(MT_ALTVIEWMAN, args[0]);
if (!altview || !altview->spawnpoint)
return false;
// If titlemap, set the camera ref for title's thinker
// This is not revoked until overwritten; awayviewtics is ignored
if (titlemapinaction)
titlemapcameraref = altview;
if (gamestate != GS_LEVEL)
{
modifyView = &titlemapcam;
}
else if (mo != NULL && mo->player != NULL)
{
modifyView = &mo->player->awayview;
}
else
{
P_SetTarget(&mo->player->awayviewmobj, altview);
mo->player->awayviewtics = args[1];
return false;
}
aim = (backwardsCompat) ? args[2] : altview->spawnpoint->pitch;
aim = (aim + 360) % 360;
aim *= (ANGLE_90>>8);
aim /= 90;
aim <<= 8;
if (titlemapinaction)
titlemapcameraref->cusval = (angle_t)aim;
newViewMobj = P_FindObjectTypeFromTag(MT_ALTVIEWMAN, args[0]);
if (newViewMobj == NULL || newViewMobj->spawnpoint == NULL)
{
return false;
}
P_SetTarget(&modifyView->mobj, newViewMobj);
if (gamestate != GS_LEVEL)
{
// If titlemap, awayview.tics is ignored
modifyView->tics = -1;
}
else
mo->player->awayviewaiming = (angle_t)aim;
{
modifyView->tics = args[1];
}
if (args[2] != 0)
{
switch (args[2])
{
case TMCAM_FIRST:
case TMCAM_SECOND:
case TMCAM_THIRD:
{
mobj_t *firstPlace = NULL;
INT32 i;
for (i = 0; i < MAXPLAYERS; i++)
{
player_t *player = NULL;
if (playeringame[i] == false)
{
continue;
}
player = &players[i];
if (player->spectator == true)
{
continue;
}
if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true)
{
continue;
}
if (player->position == abs(args[2])) // a bit of a hack
{
firstPlace = player->mo;
break;
}
}
P_SetTarget(
&newViewMobj->target,
firstPlace
);
break;
}
case TMCAM_CONSOLE:
{
mobj_t *consoleMo = NULL;
if (playeringame[consoleplayer] == true)
{
consoleMo = players[consoleplayer].mo;
}
P_SetTarget(
&newViewMobj->target,
consoleMo
);
break;
}
default:
{
P_SetTarget(
&newViewMobj->target,
P_FindMobjFromTID(args[2], NULL, NULL)
);
break;
}
}
}
else
{
P_SetTarget(&newViewMobj->target, NULL);
}
if (args[3] > 0 && args[3] <= NUMTUBEWAYPOINTSEQUENCES)
{
P_SetTarget(
&newViewMobj->tracer,
P_GetFirstTubeWaypoint(args[3] - 1)
);
newViewMobj->movecount = 0; // time
newViewMobj->movedir = newViewMobj->angle; // start angle
newViewMobj->lastlook = newViewMobj->pitch; // start pitch
newViewMobj->extravalue1 = newViewMobj->x; // start x
newViewMobj->extravalue2 = newViewMobj->y; // start y
newViewMobj->cusval = newViewMobj->z; // start z
if (args[4] > 0)
{
newViewMobj->movefactor = FRACUNIT / args[4];
}
else
{
newViewMobj->movefactor = FRACUNIT / TICRATE; // default speed
}
}
else
{
P_SetTarget(&newViewMobj->tracer, NULL);
}
}
break;

View file

@ -519,6 +519,14 @@ typedef enum
TMLOOP_BETA = 1,
} textmaploopendpointtype_t;
typedef enum
{
TMCAM_FIRST = -1,
TMCAM_SECOND = -2,
TMCAM_THIRD = -3,
TMCAM_CONSOLE = -4,
} textmapcamerafollow_t;
// GETSECSPECIAL (specialval, section)
//
// Pulls out the special # from a particular section.
@ -574,6 +582,8 @@ void P_CrossSpecialLine(line_t *line, INT32 side, mobj_t *thing);
void P_PushSpecialLine(line_t *line, mobj_t *thing);
void P_ActivateThingSpecial(mobj_t *mo, mobj_t *source);
mobj_t* P_FindObjectTypeFromTag(mobjtype_t type, mtag_t tag);
//
// Special activation info
//

View file

@ -78,7 +78,7 @@ void Command_Numthinkers_f(void)
thinklistnum_t end = NUM_THINKERLISTS - 1;
thinklistnum_t i;
if (gamestate != GS_LEVEL)
if (G_GamestateUsesLevel() == false)
{
CONS_Printf(M_GetText("You must be in a level to use this.\n"));
return;
@ -149,7 +149,7 @@ void Command_CountMobjs_f(void)
mobjtype_t i;
INT32 count;
if (gamestate != GS_LEVEL)
if (G_GamestateUsesLevel() == false)
{
CONS_Printf(M_GetText("You must be in a level to use this.\n"));
return;
@ -532,10 +532,12 @@ void P_Ticker(boolean run)
// Increment jointime and quittime even if paused
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
{
players[i].jointime++;
}
}
if (objectplacing)
{
@ -602,32 +604,16 @@ void P_Ticker(boolean run)
ps_playerthink_time = I_GetPreciseTime();
#define PLAYERCONDITION(i) (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
// First loop: Ensure all players' distance to the finish line are all accurate
for (i = 0; i < MAXPLAYERS; i++)
{
if (!PLAYERCONDITION(i))
continue;
K_UpdateDistanceFromFinishLine(&players[i]);
}
// Second loop: Ensure all player positions reflect everyone's distances
for (i = 0; i < MAXPLAYERS; i++)
{
if (!PLAYERCONDITION(i))
continue;
K_KartUpdatePosition(&players[i]);
}
K_UpdateAllPlayerPositions();
// OK! Now that we got all of that sorted, players can think!
for (i = 0; i < MAXPLAYERS; i++)
{
if (!PLAYERCONDITION(i))
if (!(playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)))
continue;
P_PlayerThink(&players[i]);
K_KartPlayerHUDUpdate(&players[i]);
}
#undef PLAYERCONDITION
ps_playerthink_time = I_GetPreciseTime() - ps_playerthink_time;
@ -800,10 +786,12 @@ void P_Ticker(boolean run)
}
}
K_UpdateDirector();
// Always move the camera.
P_RunChaseCameras();
if (gamestate == GS_LEVEL)
{
// Move the camera during levels.
K_UpdateDirector();
P_RunChaseCameras();
}
LUA_HOOK(PostThinkFrame);
@ -872,19 +860,11 @@ void P_PreTicker(INT32 frames)
R_UpdateMobjInterpolators();
// First loop: Ensure all players' distance to the finish line are all accurate
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
K_UpdateDistanceFromFinishLine(&players[i]);
// Second loop: Ensure all player positions reflect everyone's distances
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
K_KartUpdatePosition(&players[i]);
// OK! Now that we got all of that sorted, players can think!
LUA_HOOK(PreThinkFrame);
K_UpdateAllPlayerPositions();
// OK! Now that we got all of that sorted, players can think!
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
{

View file

@ -58,6 +58,9 @@
#include "k_terrain.h" // K_SpawnSplashForMobj
#include "k_color.h"
#include "k_follower.h"
#include "k_battle.h"
#include "k_rank.h"
#include "k_director.h"
#ifdef HW3SOUND
#include "hardware/hw3sound.h"
@ -1294,7 +1297,7 @@ void P_DoPlayerExit(player_t *player)
if ((gametyperules & GTR_CIRCUIT)) // If in Race Mode, allow
{
K_KartUpdatePosition(player);
K_UpdateAllPlayerPositions();
if (cv_kartvoices.value)
{
@ -1363,6 +1366,7 @@ void P_DoPlayerExit(player_t *player)
if (RINGTOTAL(player) > 0)
{
player->totalring += RINGTOTAL(player);
grandprixinfo.rank.rings += RINGTOTAL(player);
extra = player->totalring / lifethreshold;
@ -1373,6 +1377,16 @@ void P_DoPlayerExit(player_t *player)
player->xtralife = extra;
}
}
if (grandprixinfo.eventmode == GPEVENT_NONE)
{
grandprixinfo.rank.winPoints += K_CalculateGPRankPoints(player->position, grandprixinfo.rank.totalPlayers);
grandprixinfo.rank.laps += player->lapPoints;
}
else if (grandprixinfo.eventmode == GPEVENT_SPECIAL)
{
grandprixinfo.rank.specialWon = true;
}
}
}
}
@ -2783,11 +2797,23 @@ static void P_DeathThink(player_t *player)
}
}
if ((player->pflags & PF_ELIMINATED) && (gametyperules & GTR_BUMPERS))
{
playerGone = true;
}
if (playerGone == false && player->deadtimer > TICRATE)
{
player->playerstate = PST_REBORN;
}
// TODO: support splitscreen
// Spectate another player after 2 seconds
if (player == &players[consoleplayer] && playerGone == true && (gametyperules & GTR_BUMPERS) && player->deadtimer == 2*TICRATE)
{
K_ToggleDirector(true);
}
// Keep time rolling
if (!(player->exiting || mapreset) && !(player->pflags & PF_NOCONTEST) && !stoppedclock)
{
@ -3680,10 +3706,10 @@ static void P_CalcPostImg(player_t *player)
else
pviewheight = player->mo->z + player->viewheight;
if (player->awayviewtics && player->awayviewmobj && !P_MobjWasRemoved(player->awayviewmobj))
if (player->awayview.tics && player->awayview.mobj && !P_MobjWasRemoved(player->awayview.mobj))
{
sector = player->awayviewmobj->subsector->sector;
pviewheight = player->awayviewmobj->z + 20*FRACUNIT;
sector = player->awayview.mobj->subsector->sector;
pviewheight = player->awayview.mobj->z;
}
for (i = 0; i <= (unsigned)r_splitscreen; i++)
@ -4007,6 +4033,25 @@ DoABarrelRoll (player_t *player)
player->tilt = slope;
}
void P_TickAltView(altview_t *view)
{
if (view->mobj != NULL && P_MobjWasRemoved(view->mobj) == true)
{
P_SetTarget(&view->mobj, NULL); // remove view->mobj asap if invalid
view->tics = 0; // reset to zero
}
if (view->tics > 0)
{
view->tics--;
if (view->tics == 0)
{
P_SetTarget(&view->mobj, NULL);
}
}
}
//
// P_PlayerThink
//
@ -4030,18 +4075,11 @@ void P_PlayerThink(player_t *player)
player->old_drawangle = player->drawangle;
if (player->awayviewmobj && P_MobjWasRemoved(player->awayviewmobj))
{
P_SetTarget(&player->awayviewmobj, NULL); // remove awayviewmobj asap if invalid
player->awayviewtics = 0; // reset to zero
}
P_TickAltView(&player->awayview);
if (player->flashcount)
player->flashcount--;
if (player->awayviewtics && player->awayviewtics != -1)
player->awayviewtics--;
// Track airtime
if (P_IsObjectOnGround(player->mo)
&& !P_PlayerInPain(player)) // This isn't airtime, but it's control loss all the same.

105
src/r_debug.cpp Normal file
View file

@ -0,0 +1,105 @@
// RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2023 by James Robert Roman.
//
// 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 r_debug.cpp
/// \brief Software renderer debugging
#include <algorithm> // std::clamp
#include "cxxutil.hpp"
#include "r_debug_detail.hpp"
#include "command.h"
#include "m_fixed.h"
#include "r_draw.h"
#include "r_main.h"
using namespace srb2::r_debug;
namespace
{
CV_PossibleValue_t contrast_cons_t[] = {{-FRACUNIT, "MIN"}, {FRACUNIT, "MAX"}, {}};
}; // namespace
consvar_t cv_debugrender_contrast =
CVAR_INIT("debugrender_contrast", "0.0", CV_CHEAT | CV_FLOAT, contrast_cons_t, nullptr);
consvar_t cv_debugrender_spriteclip = CVAR_INIT("debugrender_spriteclip", "Off", CV_CHEAT, CV_OnOff, nullptr);
UINT32 debugrender_highlight;
void R_CheckDebugHighlight(debugrender_highlight_t k)
{
// If highlighting is enabled for anything, surfaces
// must be highlighted in one of two colors, depending on
// whether they fall under focus of the debug.
if (debugrender_highlight)
{
r8_flatcolor = (debugrender_highlight & (1 << k)) ? detail::kHighlightOptions[k].color : 0x1F;
}
}
INT32 R_AdjustLightLevel(INT32 light)
{
if (!debugrender_highlight && cv_debugrender_contrast.value == 0)
{
return light;
}
constexpr fixed_t kRange = (LIGHTLEVELS - 1) * FRACUNIT;
const fixed_t adjust = FixedMul(cv_debugrender_contrast.value, kRange);
if (debugrender_highlight)
{
light = (kRange / 2) - (adjust / 2);
SRB2_ASSERT(light >= 0);
SRB2_ASSERT(light <= kRange);
}
else
{
light = std::clamp((light * FRACUNIT) - adjust, 0, kRange);
}
return light / FRACUNIT;
}
void Command_Debugrender_highlight(void)
{
const bool changes = COM_Argc() > 1;
if (!CV_CheatsEnabled())
{
CONS_Printf("Cheats must be enabled.\n");
return;
}
if (changes)
{
const char* arg = COM_Argv(1);
debugrender_highlight = 0; // always reset
if (COM_Argc() > 2 ||
// approximate match "none"
strncasecmp(arg, "none", strlen(arg)))
{
char* p = COM_Args();
while (p)
{
p = detail::parse_highlight_arg(p);
}
}
}
detail::highlight_help(changes);
}

42
src/r_debug_detail.hpp Normal file
View file

@ -0,0 +1,42 @@
// RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2023 by James Robert Roman.
//
// 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 r_debug_detail.cpp
/// \brief Software renderer debugging, internal header
#ifndef __R_DEBUG_DETAIL__
#define __R_DEBUG_DETAIL__
#include "r_main.h"
namespace srb2::r_debug::detail
{
struct HighlightDesc
{
uint8_t color;
const char* label;
const char* description;
};
constexpr HighlightDesc kHighlightOptions[NUM_SW_HI] = {
{0x96, "planes", "Sector floor/ceiling"},
{0x49, "fofplanes", "FOF top/bottom"},
{0xB6, "fofsides", "FOF sides"},
{0x7A, "midtextures", "Two-sided midtexture"},
{0xC9, "walls", "Sector upper/lower texture, one-sided midtexture"},
{0x23, "sprites", "Sprites"},
{0x0F, "sky", "Sky texture"}};
char* skip_alnum(char* p, int mode);
char* parse_highlight_arg(char* p);
void highlight_help(bool only_on);
}; // srb2::r_debug::detail
#endif // __R_DEBUG_DETAIL__

95
src/r_debug_parser.cpp Normal file
View file

@ -0,0 +1,95 @@
// RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2023 by James Robert Roman.
//
// 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 r_debug_parser.cpp
/// \brief Helper functions for the debugrender_highlight command
#include "r_debug_detail.hpp"
#include "doomdef.h"
#include "r_main.h"
using namespace srb2::r_debug;
using namespace srb2::r_debug::detail;
char* detail::skip_alnum(char* p, int mode)
{
while (*p != '\0' && !isalnum(*p) == !mode)
{
p++;
}
return p;
}
char* detail::parse_highlight_arg(char* p)
{
INT32 k;
const HighlightDesc* key;
const auto old = static_cast<debugrender_highlight_t>(debugrender_highlight);
char* t;
int c;
p = skip_alnum(p, 0); // skip "whitespace"
if (*p == '\0')
{
return NULL;
}
t = skip_alnum(p, 1); // find end of word
c = *t; // save to restore afterward
*t = '\0'; // isolate word string
for (k = 0; (key = &kHighlightOptions[k]), k < NUM_SW_HI; ++k)
{
// allow an approximate match
if (!strncasecmp(p, key->label, (t - p)))
{
debugrender_highlight |= (1 << k);
// keep going to match multiple with same
// prefix
}
}
if (debugrender_highlight == old)
{
// no change? Foolish user
CONS_Alert(CONS_WARNING, "\"%s\" makes no sense\n", p);
}
*t = c; // restore
return t; // skip to end of word
}
void detail::highlight_help(bool only_on)
{
int32_t k;
const HighlightDesc* key;
for (k = 0; (key = &kHighlightOptions[k]), k < NUM_SW_HI; ++k)
{
const bool on = (debugrender_highlight & (1 << k)) != 0;
if (!only_on || on)
{
CONS_Printf("%s\x80 \x87%s\x80 - %s\n", on ? "\x83 ON" : "\x85OFF", key->label, key->description);
}
}
if (!only_on)
{
CONS_Printf("\nYou can change the highlights by using a command like:\n\n"
"\x87 debugrender_highlight planes sprites\n"
"\x87 debugrender_highlight none\n");
}
}

View file

@ -932,6 +932,8 @@ typedef enum
RF_NOCOLORMAPS = 0x00000400, // Sprite is not drawn with colormaps
RF_ALWAYSONTOP = 0x00000800, // Sprite is drawn on top of level geometry
RF_SPRITETYPEMASK = 0x00003000, // --Different sprite types
RF_PAPERSPRITE = 0x00001000, // Paper sprite
RF_FLOORSPRITE = 0x00002000, // Floor sprite

View file

@ -68,6 +68,8 @@ INT32 columnofs[MAXVIDWIDTH*4];
UINT8 *topleft;
UINT8 r8_flatcolor;
// =========================================================================
// COLUMN DRAWING CODE STUFF
// =========================================================================
@ -81,6 +83,7 @@ UINT8 dc_hires; // under MSVC boolean is a byte, while on other systems, it a bi
// soo lets make it a byte on all system for the ASM code
UINT8 *dc_source;
UINT8 *dc_brightmap;
UINT8 *dc_lightmap;
// -----------------------
// translucency stuff here
@ -638,6 +641,7 @@ void R_DrawViewBorder(void)
#include "r_draw8.c"
#include "r_draw8_npo2.c"
#include "r_draw8_flat.c"
// ==========================================================================
// INCLUDE 16bpp DRAWING CODE HERE

View file

@ -30,6 +30,7 @@ extern UINT8 *ylookup3[MAXVIDHEIGHT*4];
extern UINT8 *ylookup4[MAXVIDHEIGHT*4];
extern INT32 columnofs[MAXVIDWIDTH*4];
extern UINT8 *topleft;
extern UINT8 r8_flatcolor;
// -------------------------
// COLUMN DRAWING CODE STUFF
@ -43,6 +44,7 @@ extern UINT8 dc_hires;
extern UINT8 *dc_source; // first pixel in a column
extern UINT8 *dc_brightmap; // brightmap texture column, can be NULL
extern UINT8 *dc_lightmap; // lighting only
// translucency stuff here
extern UINT8 *dc_transmap;
@ -76,6 +78,8 @@ extern UINT8 *ds_source;
extern UINT8 *ds_brightmap;
extern UINT8 *ds_transmap;
extern UINT8 ds_flatcolor;
struct floatv3_t {
float x, y, z;
};
@ -232,6 +236,11 @@ void R_DrawTiltedTranslucentFloorSprite_NPO2_8(void);
void R_DrawTranslucentWaterSpan_NPO2_8(void);
void R_DrawTiltedTranslucentWaterSpan_NPO2_8(void);
// Debugging - highlight surfaces in flat colors
void R_DrawColumn_Flat_8(void);
void R_DrawSpan_Flat_8(void);
void R_DrawTiltedSpan_Flat_8(void);
#ifdef USEASM
void ASMCALL R_DrawColumn_8_ASM(void);
void ASMCALL R_DrawShadeColumn_8_ASM(void);

79
src/r_draw8_flat.c Normal file
View file

@ -0,0 +1,79 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
// Copyright (C) 2023 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 r_draw8_flat.c
/// \brief 8bpp span/column drawer functions for debugging (draws in flat colors only)
/// \note no includes because this is included as part of r_draw.c
void R_DrawColumn_Flat_8 (void)
{
INT32 count;
UINT8 color = dc_lightmap[r8_flatcolor];
register UINT8 *dest;
count = dc_yh - dc_yl;
if (count < 0) // Zero length, column does not exceed a pixel.
return;
#ifdef RANGECHECK
if ((unsigned)dc_x >= (unsigned)vid.width || dc_yl < 0 || dc_yh >= vid.height)
return;
#endif
// Framebuffer destination address.
// Use ylookup LUT to avoid multiply with ScreenWidth.
// Use columnofs LUT for subwindows?
//dest = ylookup[dc_yl] + columnofs[dc_x];
dest = &topleft[dc_yl*vid.width + dc_x];
count++;
do
{
*dest = color;
dest += vid.width;
} while (--count);
}
void R_DrawSpan_Flat_8 (void)
{
UINT8 *dest = ylookup[ds_y] + columnofs[ds_x1];
memset(dest, ds_colormap[r8_flatcolor], (ds_x2 - ds_x1) + 1);
}
void R_DrawTiltedSpan_Flat_8 (void)
{
// x1, x2 = ds_x1, ds_x2
int width = ds_x2 - ds_x1;
double iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx);
UINT8 *dest = ylookup[ds_y];
// Lighting is simple. It's just linear interpolation from start to end
{
float planelightfloat = PLANELIGHTFLOAT;
float lightstart, lightend;
lightend = (iz + ds_szp->x*width) * planelightfloat;
lightstart = iz * planelightfloat;
R_CalcTiltedLighting(FLOAT_TO_FIXED(lightstart), FLOAT_TO_FIXED(lightend));
//CONS_Printf("tilted lighting %f to %f (foc %f)\n", lightstart, lightend, focallengthf);
}
while (ds_x1 <= ds_x2)
{
dest[ds_x1] = planezlight[tiltlighting[ds_x1]][r8_flatcolor];
ds_x1++;
}
}

View file

@ -928,7 +928,22 @@ void R_ApplyViewMorph(int s)
angle_t R_ViewRollAngle(const player_t *player)
{
angle_t roll = player->viewrollangle;
angle_t roll = 0;
if (gamestate != GS_LEVEL)
{
// FIXME: The way this is implemented is totally
// incompatible with cameras that aren't directly
// tied to the player. (podium, titlemap,
// MT_ALTVIEWMAN in general)
// All of these player variables should affect their
// camera_t in P_MoveChaseCamera, and then this
// just returns that variable instead.
return 0;
}
roll = player->viewrollangle;
if (cv_tilting.value)
{
@ -1192,10 +1207,10 @@ static void R_SetupAimingFrame(int s)
player_t *player = &players[displayplayers[s]];
camera_t *thiscam = &camera[s];
if (player->awayviewtics)
if (player->awayview.tics)
{
newview->aim = player->awayviewaiming;
newview->angle = player->awayviewmobj->angle;
newview->aim = player->awayview.mobj->pitch;
newview->angle = player->awayview.mobj->angle;
}
else if (thiscam && thiscam->chase)
{
@ -1237,15 +1252,15 @@ void R_SetupFrame(int s)
R_SetupAimingFrame(s);
if (player->awayviewtics)
if (player->awayview.tics)
{
// cut-away view stuff
r_viewmobj = player->awayviewmobj; // should be a MT_ALTVIEWMAN
r_viewmobj = player->awayview.mobj; // should be a MT_ALTVIEWMAN
I_Assert(r_viewmobj != NULL);
newview->x = r_viewmobj->x;
newview->y = r_viewmobj->y;
newview->z = r_viewmobj->z + 20*FRACUNIT;
newview->z = r_viewmobj->z;
R_SetupCommonFrame(player, r_viewmobj->subsector);
}
@ -1306,10 +1321,10 @@ void R_SkyboxFrame(int s)
vector3_t campos = {0,0,0}; // Position of player's actual view point
mobj_t *center = player->skybox.centerpoint;
if (player->awayviewtics) {
campos.x = player->awayviewmobj->x;
campos.y = player->awayviewmobj->y;
campos.z = player->awayviewmobj->z + 20*FRACUNIT;
if (player->awayview.tics) {
campos.x = player->awayview.mobj->x;
campos.y = player->awayview.mobj->y;
campos.z = player->awayview.mobj->z;
} else if (thiscam->chase) {
campos.x = thiscam->x;
campos.y = thiscam->y;
@ -1403,7 +1418,7 @@ boolean R_IsViewpointThirdPerson(player_t *player, boolean skybox)
boolean chasecam = R_ViewpointHasChasecam(player);
// cut-away view stuff
if (player->awayviewtics || skybox)
if (player->awayview.tics || skybox)
return chasecam;
// use outside cam view
else if (!player->spectator && chasecam)
@ -1651,9 +1666,19 @@ void R_RegisterEngineStuff(void)
CV_RegisterVar(&cv_maxportals);
CV_RegisterVar(&cv_movebob);
#ifdef SCRAMBLE_REMOVED
CV_RegisterVar(&cv_scrambleremoved);
#endif
// Frame interpolation/uncapped
CV_RegisterVar(&cv_fpscap);
CV_RegisterVar(&cv_drawpickups);
// debugging
CV_RegisterVar(&cv_debugrender_contrast);
CV_RegisterVar(&cv_debugrender_spriteclip);
COM_AddCommand("debugrender_highlight", Command_Debugrender_highlight);
}

View file

@ -123,6 +123,31 @@ extern consvar_t cv_skybox;
extern consvar_t cv_tailspickup;
extern consvar_t cv_drawpickups;
// debugging
typedef enum {
SW_HI_PLANES,
SW_HI_FOFPLANES,
SW_HI_FOFSIDES,
SW_HI_MIDTEXTURES,
SW_HI_WALLS,
SW_HI_THINGS,
SW_HI_SKY,
NUM_SW_HI
} debugrender_highlight_t;
extern UINT32 debugrender_highlight;
void R_CheckDebugHighlight(debugrender_highlight_t type);
INT32 R_AdjustLightLevel(INT32 light);
void Command_Debugrender_highlight(void);
extern consvar_t
cv_debugrender_contrast,
cv_debugrender_spriteclip;
// Called by startup code.
void R_Init(void);

View file

@ -208,14 +208,17 @@ static void R_MapPlane(INT32 y, INT32 x1, INT32 x2)
pindex = MAXLIGHTZ - 1;
ds_colormap = planezlight[pindex];
if (currentplane->extra_colormap)
ds_colormap = currentplane->extra_colormap->colormap + (ds_colormap - colormaps);
ds_fullbright = colormaps;
if (encoremap && !currentplane->noencore)
if (!debugrender_highlight)
{
ds_colormap += COLORMAP_REMAPOFFSET;
ds_fullbright += COLORMAP_REMAPOFFSET;
if (currentplane->extra_colormap)
ds_colormap = currentplane->extra_colormap->colormap + (ds_colormap - colormaps);
ds_fullbright = colormaps;
if (encoremap && !currentplane->noencore)
{
ds_colormap += COLORMAP_REMAPOFFSET;
ds_fullbright += COLORMAP_REMAPOFFSET;
}
}
ds_y = y;
@ -613,6 +616,8 @@ static void R_DrawSkyPlane(visplane_t *pl)
INT32 x;
INT32 angle;
R_CheckDebugHighlight(SW_HI_SKY);
// Reset column drawer function (note: couldn't we just call walldrawerfunc directly?)
// (that is, unless we'll need to switch drawers in future for some reason)
R_SetColumnFunc(BASEDRAWFUNC, false);
@ -631,6 +636,7 @@ static void R_DrawSkyPlane(visplane_t *pl)
dc_colormap += COLORMAP_REMAPOFFSET;
dc_fullbright += COLORMAP_REMAPOFFSET;
}
dc_lightmap = colormaps;
dc_texturemid = skytexturemid;
dc_texheight = textureheight[skytexture]
>>FRACBITS;
@ -831,6 +837,7 @@ void R_DrawSinglePlane(visplane_t *pl)
INT32 x, stop;
ffloor_t *rover;
INT32 type, spanfunctype = BASEDRAWFUNC;
debugrender_highlight_t debug = 0;
void (*mapfunc)(INT32, INT32, INT32) = R_MapPlane;
if (!(pl->minx <= pl->maxx))
@ -911,10 +918,16 @@ void R_DrawSinglePlane(visplane_t *pl)
light = (pl->lightlevel >> LIGHTSEGSHIFT);
}
else light = (pl->lightlevel >> LIGHTSEGSHIFT);
debug = SW_HI_FOFPLANES;
}
else
{
light = (pl->lightlevel >> LIGHTSEGSHIFT);
debug = SW_HI_PLANES;
}
#ifndef NOWATER
if (pl->ripple)
{
@ -1030,6 +1043,8 @@ void R_DrawSinglePlane(visplane_t *pl)
if (light < 0)
light = 0;
light = R_AdjustLightLevel(light);
if (pl->slope)
{
mapfunc = R_MapTiltedPlane;
@ -1083,6 +1098,8 @@ void R_DrawSinglePlane(visplane_t *pl)
}
R_CheckDebugHighlight(debug);
// Use the correct span drawer depending on the powers-of-twoness
R_SetSpanFunc(spanfunctype, !ds_powersoftwo, ds_brightmap != NULL);

View file

@ -185,6 +185,8 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
transtable = 0;
}
R_CheckDebugHighlight(SW_HI_MIDTEXTURES);
if (blendmode == AST_FOG)
{
R_SetColumnFunc(COLDRAWFUNC_FOG, bmnum != 0);
@ -298,6 +300,8 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
else if (P_ApplyLightOffset(lightnum))
lightnum += curline->lightOffset;
lightnum = R_AdjustLightLevel(lightnum);
if (lightnum < 0)
walllights = scalelight[0];
else if (lightnum >= LIGHTLEVELS)
@ -413,12 +417,14 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
if ((rlight->flags & FOF_NOSHADE))
continue;
if (rlight->lightnum < 0)
lightnum = R_AdjustLightLevel(rlight->lightnum);
if (lightnum < 0)
xwalllights = scalelight[0];
else if (rlight->lightnum >= LIGHTLEVELS)
else if (lightnum >= LIGHTLEVELS)
xwalllights = scalelight[LIGHTLEVELS-1];
else
xwalllights = scalelight[rlight->lightnum];
xwalllights = scalelight[lightnum];
pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT;
@ -436,6 +442,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
if (height <= windowtop)
{
dc_colormap = rlight->rcolormap;
dc_lightmap = xwalllights[pindex];
dc_fullbright = colormaps;
if (encoremap && !(ldef->flags & ML_TFERLINE))
{
@ -461,6 +468,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
colfunc_2s(col, bmCol, -1);
windowtop = windowbottom + 1;
dc_colormap = rlight->rcolormap;
dc_lightmap = xwalllights[pindex];
dc_fullbright = colormaps;
if (encoremap && !(ldef->flags & ML_TFERLINE))
{
@ -483,6 +491,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
pindex = MAXLIGHTSCALE - 1;
dc_colormap = walllights[pindex];
dc_lightmap = walllights[pindex];
dc_fullbright = colormaps;
if (encoremap && !(ldef->flags & ML_TFERLINE))
{
@ -638,6 +647,8 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
texnum = R_GetTextureNum(sides[pfloor->master->sidenum[0]].midtexture);
bmnum = R_GetTextureBrightmap(texnum);
R_CheckDebugHighlight(SW_HI_FOFSIDES);
R_SetColumnFunc(BASEDRAWFUNC, bmnum != 0);
if (pfloor->master->flags & ML_TFERLINE)
@ -789,6 +800,8 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
else if (P_ApplyLightOffset(lightnum))
lightnum += curline->lightOffset;
lightnum = R_AdjustLightLevel(lightnum);
if (lightnum < 0)
walllights = scalelight[0];
else if (lightnum >= LIGHTLEVELS)
@ -954,16 +967,15 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
fixed_t height;
fixed_t bheight = 0;
INT32 solid = 0;
INT32 lighteffect = 0;
for (i = 0; i < dc_numlights; i++)
{
// Check if the current light effects the colormap/lightlevel
rlight = &dc_lightlist[i];
lighteffect = !(dc_lightlist[i].flags & FOF_NOSHADE);
if (lighteffect)
xwalllights = NULL;
if (!(dc_lightlist[i].flags & FOF_NOSHADE))
{
lightnum = rlight->lightnum;
lightnum = R_AdjustLightLevel(rlight->lightnum);
if (lightnum < 0)
xwalllights = scalelight[0];
@ -1024,9 +1036,10 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
if (height <= windowtop)
{
if (lighteffect)
if (xwalllights)
{
dc_colormap = rlight->rcolormap;
dc_lightmap = xwalllights[pindex];
dc_fullbright = colormaps;
if (encoremap && !(curline->linedef->flags & ML_TFERLINE))
{
@ -1060,9 +1073,10 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
windowtop = bheight;
else
windowtop = windowbottom + 1;
if (lighteffect)
if (xwalllights)
{
dc_colormap = rlight->rcolormap;
dc_lightmap = xwalllights[pindex];
dc_fullbright = colormaps;
if (encoremap && !(curline->linedef->flags & ML_TFERLINE))
{
@ -1087,6 +1101,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
pindex = MAXLIGHTSCALE - 1;
dc_colormap = walllights[pindex];
dc_lightmap = walllights[pindex];
dc_fullbright = colormaps;
if (encoremap && !(curline->linedef->flags & ML_TFERLINE))
@ -1353,6 +1368,7 @@ static void R_RenderSegLoop (void)
pindex = MAXLIGHTSCALE-1;
dc_colormap = walllights[pindex];
dc_lightmap = walllights[pindex];
dc_fullbright = colormaps;
if (encoremap && !(curline->linedef->flags & ML_TFERLINE))
{
@ -1379,6 +1395,8 @@ static void R_RenderSegLoop (void)
else if (P_ApplyLightOffset(lightnum))
lightnum += curline->lightOffset;
lightnum = R_AdjustLightLevel(lightnum);
if (lightnum < 0)
xwalllights = scalelight[0];
else if (lightnum >= LIGHTLEVELS)
@ -1618,6 +1636,8 @@ void R_StoreWallRange(INT32 start, INT32 stop)
memset(&segleft, 0x00, sizeof(segleft));
memset(&segright, 0x00, sizeof(segright));
R_CheckDebugHighlight(SW_HI_WALLS);
R_SetColumnFunc(BASEDRAWFUNC, false);
if (ds_p == drawsegs+maxdrawsegs)
@ -2425,6 +2445,8 @@ void R_StoreWallRange(INT32 start, INT32 stop)
if (P_ApplyLightOffset(lightnum))
lightnum += curline->lightOffset;
lightnum = R_AdjustLightLevel(lightnum);
if (lightnum < 0)
walllights = scalelight[0];
else if (lightnum >= LIGHTLEVELS)

View file

@ -939,6 +939,8 @@ static void R_DrawVisSprite(vissprite_t *vis)
if (!dc_colormap)
dc_colormap = colormaps;
dc_lightmap = colormaps;
dc_fullbright = colormaps;
if (encoremap && !vis->mobj->color && !(vis->mobj->flags & MF_DONTENCOREMAP))
@ -1143,6 +1145,8 @@ static void R_DrawPrecipitationVisSprite(vissprite_t *vis)
dc_fullbright += COLORMAP_REMAPOFFSET;
}
dc_lightmap = colormaps;
dc_iscale = FixedDiv(FRACUNIT, vis->scale);
dc_texturemid = FixedDiv(vis->texturemid, this_scale);
dc_texheight = 0;
@ -2971,6 +2975,9 @@ static void R_CreateDrawNodes(maskcount_t* mask, drawnode_t* head, boolean temps
for (rover = vsprsortedhead.prev; rover != &vsprsortedhead; rover = rover->prev)
{
const boolean alwaysontop = cv_debugrender_spriteclip.value || (rover->renderflags & RF_ALWAYSONTOP);
const INT32 ontopflag = cv_debugrender_spriteclip.value ? 0 : (rover->renderflags & RF_ALWAYSONTOP);
if (rover->szt > vid.height || rover->sz < 0)
continue;
@ -2978,6 +2985,25 @@ static void R_CreateDrawNodes(maskcount_t* mask, drawnode_t* head, boolean temps
for (r2 = head->next; r2 != head; r2 = r2->next)
{
if (alwaysontop)
{
// Only sort behind other sprites; sorts in
// front of everything else.
if (!r2->sprite)
{
continue;
}
// Only sort behind other RF_ALWAYSONTOP sprites.
// This avoids sorting behind a sprite that is
// behind level geometry and thus sorting this
// one behind level geometry too.
if (r2->sprite->renderflags ^ ontopflag)
{
continue;
}
}
if (r2->plane)
{
fixed_t planeobjectz, planecameraz;
@ -3210,6 +3236,8 @@ static void R_DrawSprite(vissprite_t *spr)
mfloorclip = spr->clipbot;
mceilingclip = spr->cliptop;
R_CheckDebugHighlight(SW_HI_THINGS);
if (spr->cut & SC_BBOX)
R_DrawThingBoundingBox(spr);
else if (spr->cut & SC_SPLAT)
@ -3238,6 +3266,16 @@ void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, portal_t* portal)
fixed_t lowscale;
INT32 silhouette;
if ((spr->renderflags & RF_ALWAYSONTOP) || cv_debugrender_spriteclip.value)
{
for (x = x1; x <= x2; x++)
{
spr->clipbot[x] = (INT16)viewheight;
spr->cliptop[x] = (INT16)con_clipviewtop;
}
return;
}
for (x = x1; x <= x2; x++)
{
spr->clipbot[x] = spr->cliptop[x] = CLIP_UNDEF;

View file

@ -540,9 +540,9 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume)
continue;
}
if (player->awayviewtics)
if (player->awayview.tics)
{
listenmobj[i] = player->awayviewmobj;
listenmobj[i] = player->awayview.mobj;
}
else
{
@ -572,7 +572,7 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume)
continue;
}
if (camera[i].chase && !player->awayviewtics)
if (camera[i].chase && !player->awayview.tics)
{
listener[i].x = camera[i].x;
listener[i].y = camera[i].y;
@ -827,9 +827,9 @@ void S_UpdateSounds(void)
continue;
}
if (player->awayviewtics)
if (player->awayview.tics)
{
listenmobj[i] = player->awayviewmobj;
listenmobj[i] = player->awayview.mobj;
}
else
{
@ -858,7 +858,7 @@ void S_UpdateSounds(void)
continue;
}
if (camera[i].chase && !player->awayviewtics)
if (camera[i].chase && !player->awayview.tics)
{
listener[i].x = camera[i].x;
listener[i].y = camera[i].y;

View file

@ -64,6 +64,7 @@ void (*spanfuncs_npo2[SPANDRAWFUNC_MAX])(void);
#ifdef USE_COL_SPAN_ASM
void (*spanfuncs_asm[SPANDRAWFUNC_MAX])(void);
#endif
void (*spanfuncs_flat[SPANDRAWFUNC_MAX])(void);
// ------------------
// global video state
@ -170,6 +171,22 @@ void SCR_SetDrawFuncs(void)
spanfuncs_npo2[SPANDRAWFUNC_TILTEDWATER] = R_DrawTiltedTranslucentWaterSpan_NPO2_8;
spanfuncs_npo2[SPANDRAWFUNC_FOG] = NULL; // Not needed
// Debugging - highlight surfaces in flat colors
spanfuncs_flat[BASEDRAWFUNC] = R_DrawSpan_Flat_8;
spanfuncs_flat[SPANDRAWFUNC_TRANS] = R_DrawSpan_Flat_8;
spanfuncs_flat[SPANDRAWFUNC_TILTED] = R_DrawTiltedSpan_Flat_8;
spanfuncs_flat[SPANDRAWFUNC_TILTEDTRANS] = R_DrawTiltedSpan_Flat_8;
spanfuncs_flat[SPANDRAWFUNC_SPLAT] = R_DrawSpan_Flat_8;
spanfuncs_flat[SPANDRAWFUNC_TRANSSPLAT] = R_DrawSpan_Flat_8;
spanfuncs_flat[SPANDRAWFUNC_TILTEDSPLAT] = R_DrawTiltedSpan_Flat_8;
spanfuncs_flat[SPANDRAWFUNC_SPRITE] = R_DrawSpan_Flat_8;
spanfuncs_flat[SPANDRAWFUNC_TRANSSPRITE] = R_DrawSpan_Flat_8;
spanfuncs_flat[SPANDRAWFUNC_TILTEDSPRITE] = R_DrawTiltedSpan_Flat_8;
spanfuncs_flat[SPANDRAWFUNC_TILTEDTRANSSPRITE] = R_DrawTiltedSpan_Flat_8;
spanfuncs_flat[SPANDRAWFUNC_WATER] = R_DrawSpan_Flat_8;
spanfuncs_flat[SPANDRAWFUNC_TILTEDWATER] = R_DrawTiltedSpan_Flat_8;
spanfuncs_flat[SPANDRAWFUNC_FOG] = R_DrawSpan_Flat_8; // Not needed
#if (defined(RUSEASM) && defined(USE_COL_SPAN_ASM))
if (R_ASM)
{
@ -220,13 +237,17 @@ void R_SetColumnFunc(size_t id, boolean brightmapped)
colfunctype = id;
if (debugrender_highlight != 0)
{
colfunc = R_DrawColumn_Flat_8;
}
#ifdef USE_COL_SPAN_ASM
if (colfuncs_asm[id] != NULL && brightmapped == false)
else if (colfuncs_asm[id] != NULL && brightmapped == false)
{
colfunc = colfuncs_asm[id];
}
else
#endif
else
{
colfunc = colfuncs[id];
}
@ -236,7 +257,11 @@ void R_SetSpanFunc(size_t id, boolean npo2, boolean brightmapped)
{
I_Assert(id < SPANDRAWFUNC_MAX);
if (spanfuncs_npo2[id] != NULL && npo2 == true)
if (spanfuncs_flat[id] != NULL && debugrender_highlight != 0)
{
spanfunc = spanfuncs_flat[id];
}
else if (spanfuncs_npo2[id] != NULL && npo2 == true)
{
spanfunc = spanfuncs_npo2[id];
}

View file

@ -179,6 +179,7 @@ extern void (*spanfuncs_npo2[SPANDRAWFUNC_MAX])(void);
#ifdef USE_COL_SPAN_ASM
extern void (*spanfuncs_asm[SPANDRAWFUNC_MAX])(void);
#endif
extern void (*spanfuncs_flat[SPANDRAWFUNC_MAX])(void);
// -----
// CPUID

View file

@ -47,6 +47,7 @@ TYPEDEF (botvars_t);
TYPEDEF (roundconditions_t);
TYPEDEF (skybox_t);
TYPEDEF (itemroulette_t);
TYPEDEF (altview_t);
TYPEDEF (player_t);
// d_clisrv.h
@ -198,6 +199,9 @@ TYPEDEF (t_floor_t);
// k_waypoint.h
TYPEDEF (waypoint_t);
// k_rank.h
TYPEDEF (gpRank_t);
// lua_hudlib_drawlist.h
typedef struct huddrawlist_s *huddrawlist_h;