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

This commit is contained in:
toaster 2023-08-22 19:57:28 +01:00
commit f7635c4399
54 changed files with 1006 additions and 600 deletions

View file

@ -88,7 +88,7 @@ if(SRB2_CONFIG_ENABLE_TESTS)
# https://github.com/catchorg/Catch2
CPMAddPackage(
NAME Catch2
VERSION 3.1.1
VERSION 3.4.0
GITHUB_REPOSITORY catchorg/Catch2
OPTIONS
"CATCH_INSTALL_DOCS OFF"

View file

@ -43,6 +43,7 @@
#include "../k_podium.h"
#include "../k_bot.h"
#include "../z_zone.h"
#include "../music.h"
#include "call-funcs.hpp"
@ -1802,6 +1803,7 @@ bool CallFunc_MapWarp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Wor
if (argV[1] == 0)
skipstats = 1;
G_BeginLevelExit();
exitcountdown = 1;
if (server)
@ -1856,6 +1858,91 @@ bool CallFunc_AddBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word
return false;
}
/*--------------------------------------------------
bool CallFunc_StopLevelExit(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Halts the level exit if it's happening.
--------------------------------------------------*/
bool CallFunc_StopLevelExit(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{
(void)argV;
(void)argC;
exitcountdown = 0;
return false;
}
/*--------------------------------------------------
bool CallFunc_ExitLevel(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Exits the level.
--------------------------------------------------*/
bool CallFunc_ExitLevel(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{
(void)argV;
(void)argC;
if (exitcountdown == 1)
{
// An exit is already in progress.
return false;
}
if (argC >= 1)
{
skipstats = (argV[0] == 0);
}
G_BeginLevelExit();
exitcountdown = 1;
if (server)
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
return false;
}
/*--------------------------------------------------
bool CallFunc_MusicPlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Play a tune. If it's already playing, restart from the
beginning.
--------------------------------------------------*/
bool CallFunc_MusicPlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{
ACSVM::MapScope *map = thread->scopeMap;
Music_Play(map->getString(argV[0])->str);
return false;
}
/*--------------------------------------------------
bool CallFunc_MusicStopAll(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Stop every tune that is currently playing.
--------------------------------------------------*/
bool CallFunc_MusicStopAll(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{
Music_StopAll();
return false;
}
/*--------------------------------------------------
bool CallFunc_MusicRemap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Change the actual song lump that a tune will play.
--------------------------------------------------*/
bool CallFunc_MusicRemap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{
ACSVM::MapScope *map = thread->scopeMap;
Music_Remap(map->getString(argV[0])->str, map->getString(argV[1])->str);
return false;
}
/*--------------------------------------------------
bool CallFunc_Get/SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)

View file

@ -87,6 +87,11 @@ bool CallFunc_SetLineRenderStyle(ACSVM::Thread *thread, const ACSVM::Word *argV,
bool CallFunc_MapWarp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_AddBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_StopLevelExit(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_ExitLevel(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_MusicPlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_MusicStopAll(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_MusicRemap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_GetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
bool CallFunc_SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);

View file

@ -171,6 +171,11 @@ Environment::Environment()
addFuncDataACS0( 503, addCallFunc(CallFunc_SetLineRenderStyle));
addFuncDataACS0( 504, addCallFunc(CallFunc_MapWarp));
addFuncDataACS0( 505, addCallFunc(CallFunc_AddBot));
addFuncDataACS0( 506, addCallFunc(CallFunc_StopLevelExit));
addFuncDataACS0( 507, addCallFunc(CallFunc_ExitLevel));
addFuncDataACS0( 508, addCallFunc(CallFunc_MusicPlay));
addFuncDataACS0( 509, addCallFunc(CallFunc_MusicStopAll));
addFuncDataACS0( 510, addCallFunc(CallFunc_MusicRemap));
}
ACSVM::Thread *Environment::allocThread()

View file

@ -315,6 +315,22 @@ void ACS_RunEmeraldScript(mobj_t *mo)
map->scriptStartType(ACS_ST_EMERALD, scriptInfo);
}
/*--------------------------------------------------
void ACS_RunGameOverScript(void)
See header file for description.
--------------------------------------------------*/
void ACS_RunGameOverScript(void)
{
Environment *env = &ACSEnv;
ACSVM::GlobalScope *const global = env->getGlobalScope(0);
ACSVM::HubScope *const hub = global->getHubScope(0);
ACSVM::MapScope *const map = hub->getMapScope(0);
map->scriptStartType(ACS_ST_GAMEOVER, {});
}
/*--------------------------------------------------
void ACS_Tick(void)

View file

@ -179,6 +179,17 @@ void ACS_RunCatcherScript(mobj_t *mo);
void ACS_RunEmeraldScript(mobj_t *mo);
/*--------------------------------------------------
void ACS_RunGameOverScript(void);
Runs the map's special scripts for exiting
the level, due to a losing condition and
without any extra lives to retry.
--------------------------------------------------*/
void ACS_RunGameOverScript(void);
/*--------------------------------------------------
void ACS_Tick(void);

View file

@ -42,6 +42,7 @@ enum acs_scriptType_e
ACS_ST_OVERTIME = 7, // OVERTIME: Runs when Overtime starts in timed game modes.
ACS_ST_UFO = 8, // UFO: Runs when the UFO Catcher is destroyed in a Special Stage.
ACS_ST_EMERALD = 9, // EMERALD: Runs when the Chaos Emerald is collected in a Special Stage.
ACS_ST_GAMEOVER = 10, // GAMEOVER: Runs when the level ends due to a losing condition and no player has an extra life.
};
//

View file

@ -31,7 +31,7 @@
* Last updated 2020 / 08 / 30 - Kart v1.3 - patch.kart
*/
#define ASSET_HASH_MAIN_KART "00000000000000000000000000000000"
#define ASSET_HASH_BIOS_PK3 "00000000000000000000000000000000"
#define ASSET_HASH_GFX_PK3 "00000000000000000000000000000000"
#define ASSET_HASH_TEXTURES_GENERAL_PK3 "00000000000000000000000000000000"
#define ASSET_HASH_TEXTURES_SEGA_PK3 "00000000000000000000000000000000"

View file

@ -561,6 +561,21 @@ void D_ResetTiccmds(void)
D_Clearticcmd(textcmds[i]->tic);
}
void D_ResetTiccmdAngle(UINT8 ss, angle_t angle)
{
INT32 i;
for (i = 0; i < MAXGENTLEMENDELAY; ++i)
{
localcmds[ss][i].angle = angle >> TICCMD_REDUCE;
}
}
ticcmd_t *D_LocalTiccmd(UINT8 ss)
{
return &localcmds[ss][0];
}
void SendKick(UINT8 playernum, UINT8 msg)
{
UINT8 buf[2];

View file

@ -615,7 +615,10 @@ extern UINT8 playerconsole[MAXPLAYERS];
INT32 D_NumPlayers(void);
boolean D_IsPlayerHumanAndGaming(INT32 player_number);
void D_ResetTiccmds(void);
void D_ResetTiccmdAngle(UINT8 ss, angle_t angle);
ticcmd_t *D_LocalTiccmd(UINT8 ss);
tic_t GetLag(INT32 node);
UINT8 GetFreeXCmdSize(UINT8 playerid);

View file

@ -886,11 +886,14 @@ void D_SRB2Loop(void)
{
rendertimefrac = FRACUNIT;
}
rendertimefrac_unpaused = g_time.timefrac;
}
else
{
renderdeltatics = realtics * FRACUNIT;
rendertimefrac = FRACUNIT;
rendertimefrac_unpaused = FRACUNIT;
}
if (interp || doDisplay)
@ -1096,7 +1099,7 @@ static void ChangeDirForUrlHandler(void)
static boolean AddIWAD(void)
{
char * path = va(pandf,srb2path,"main.kart");
char * path = va(pandf,srb2path,"bios.pk3");
if (FIL_ReadFileOK(path))
{
@ -1114,7 +1117,7 @@ static void IdentifyVersion(void)
const char *srb2waddir = NULL;
#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)
// change to the directory where 'main.kart' is found
// change to the directory where 'bios.pk3' is found
srb2waddir = I_LocateWad();
#endif
@ -1134,7 +1137,7 @@ static void IdentifyVersion(void)
// Load the IWAD
if (! AddIWAD())
{
I_Error("MAIN.KART not found! Expected in %s\n", srb2waddir);
I_Error("\"bios.pk3\" not found! Expected in %s\n", srb2waddir);
}
// will be overwritten in case of -cdrom or unix/win home
@ -1478,7 +1481,7 @@ void D_SRB2Main(void)
#endif
// Check MD5s of autoloaded files
// Note: Do not add any files that ignore MD5!
W_VerifyFileMD5(mainwads, ASSET_HASH_MAIN_KART); // main.kart
W_VerifyFileMD5(mainwads, ASSET_HASH_BIOS_PK3); // bios.pk3
mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_GFX_PK3); // gfx.pk3
mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_TEXTURES_GENERAL_PK3); // textures_general.pk3
mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_TEXTURES_SEGA_PK3); // textures_segazones.pk3

View file

@ -3690,11 +3690,14 @@ static void Command_Pause(void)
CONS_Printf(M_GetText("You can't pause here.\n"));
return;
}
// TODO: this would make a great debug feature for release
#ifndef DEVELOP
else if (modeattacking) // in time attack, pausing restarts the map
{
//M_ModeAttackRetry(0); // directly call from m_menu;
return;
}
#endif
SendNetXCmd(XD_PAUSE, &buf, 2);
}
@ -3715,8 +3718,11 @@ static void Got_Pause(UINT8 **cp, INT32 playernum)
return;
}
// TODO: this would make a great debug feature for release
#ifndef DEVELOP
if (modeattacking && !demo.playback)
return;
#endif
paused = READUINT8(*cp);
dedicatedpause = READUINT8(*cp);
@ -5770,7 +5776,7 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum)
if (G_GamestateUsesExitLevel() == false)
return;
G_ExitLevel();
G_FinishExitLevel();
}
static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum)

View file

@ -1426,7 +1426,7 @@ void PT_FileFragment(void)
filename = va("%s", file->filename);
nameonly(filename);
if (!strcmp(filename, "main.kart")
if (!strcmp(filename, "bios.pk3")
|| !strcmp(filename, "gfx.pk3")
|| !strcmp(filename, "textures.pk3")
|| !strcmp(filename, "textures_general.pk3")

View file

@ -504,6 +504,7 @@ struct player_t
fixed_t deltaviewheight;
// bounded/scaled total momentum.
fixed_t bob;
fixed_t cameraOffset;
skybox_t skybox;

View file

@ -1211,6 +1211,8 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_EMERALDSPARK6",
"S_EMERALDSPARK7",
"S_EMERALDFLARE1",
// Emerald hunt shards
"S_SHRD1",
"S_SHRD2",
@ -4793,6 +4795,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_SPRAYCAN",
"MT_EMERALD",
"MT_EMERALDSPARK",
"MT_EMERALDFLARE",
"MT_EMERHUNT", // Emerald Hunt
"MT_EMERALDSPAWN", // Emerald spawner w/ delay

View file

@ -767,8 +767,15 @@ extern UINT8 maxXtraLife; // Max extra lives from rings
extern mobj_t *hunt1, *hunt2, *hunt3; // Emerald hunt locations
struct exitcondition_t
{
boolean losing;
boolean retry;
};
// For racing
extern tic_t racecountdown, exitcountdown, musiccountdown;
extern exitcondition_t g_exit;
#define DEFAULT_GRAVITY (4*FRACUNIT/5)
extern fixed_t gravity;

View file

@ -206,9 +206,17 @@ class TiccmdBuilder
return true;
}
void toggle_freecam_input()
{
if (M_MenuButtonPressed(forplayer(), MBT_C))
{
P_ToggleDemoCamera();
}
}
bool director_input()
{
if (G_IsPartyLocal(displayplayers[forplayer()]) == true)
if (demo.freecam || G_IsPartyLocal(displayplayers[forplayer()]) == true)
{
return false;
}
@ -239,12 +247,14 @@ class TiccmdBuilder
}
}
toggle_freecam_input();
return true;
}
bool spectator_analog_input()
{
if (!player()->spectator && !objectplacing)
if (!player()->spectator && !objectplacing && !demo.freecam)
{
return false;
}
@ -261,7 +271,7 @@ class TiccmdBuilder
if (G_PlayerInputDown(forplayer(), gc_lookback, 0))
{
cmd->aiming -= joystickvector.yaxis;
cmd->aiming -= (joystickvector.yaxis * KART_FULLTURN) / JOYAXISRANGE;
}
else
{
@ -361,10 +371,28 @@ public:
explicit TiccmdBuilder(ticcmd_t* cmd_, INT32 realtics_, UINT8 ssplayer_) :
cmd(cmd_), realtics(realtics_), ssplayer(ssplayer_), viewnum(G_PartyPosition(g_localplayers[forplayer()]))
{
*cmd = {}; // blank ticcmd
if (demo.playback)
auto regular_input = [this]
{
analog_input();
common_button_input();
};
if (demo.playback || demo.freecam || player()->spectator)
{
// freecam is controllable even while paused
*cmd = {};
if (!typing_input() && !director_input())
{
regular_input();
if (demo.freecam)
{
toggle_freecam_input();
}
}
return;
}
@ -373,6 +401,8 @@ public:
return;
}
*cmd = {}; // blank ticcmd
if (gamestate == GS_LEVEL && player()->playerstate == PST_REBORN)
{
return;
@ -391,8 +421,7 @@ public:
if (!overlay)
{
analog_input();
common_button_input();
regular_input();
}
cmd->angle = localangle[viewnum] >> TICCMD_REDUCE;

View file

@ -4127,12 +4127,6 @@ void G_StopDemo(void)
singletics = false;
demo.freecam = false;
// reset democam shit too:
democam.cam = NULL;
democam.soundmobj = NULL;
democam.localangle = 0;
democam.localaiming = 0;
democam.keyboardlook = false;
Z_Free(demo.skinlist);
demo.skinlist = NULL;
@ -4167,7 +4161,7 @@ boolean G_CheckDemoStatus(void)
I_Quit();
if (multiplayer && !demo.title)
G_ExitLevel();
G_FinishExitLevel();
else
{
G_StopDemo();

View file

@ -76,6 +76,10 @@
#include "discord.h"
#endif
#ifdef HWRENDER
#include "hardware/hw_main.h" // for cv_glshearing
#endif
gameaction_t gameaction;
gamestate_t gamestate = GS_NULL;
UINT8 ultimatemode = false;
@ -272,6 +276,7 @@ mobj_t *hunt2;
mobj_t *hunt3;
tic_t racecountdown, exitcountdown, musiccountdown; // for racing
exitcondition_t g_exit;
fixed_t gravity;
fixed_t mapobjectscale;
@ -803,9 +808,7 @@ const char *G_BuildMapName(INT32 map)
*/
INT32 G_MapNumber(const char * name)
{
#ifdef NEXTMAPINSOC
if (strncasecmp("NEXTMAP_", name, 8) != 0)
#endif
{
INT32 map;
UINT32 hash = quickncasehash(name, MAXMAPLUMPNAME);
@ -824,7 +827,6 @@ INT32 G_MapNumber(const char * name)
return NEXTMAP_INVALID;
}
#ifdef NEXTMAPINSOC
name += 8;
if (strcasecmp("EVALUATION", name) == 0)
@ -833,9 +835,10 @@ INT32 G_MapNumber(const char * name)
return NEXTMAP_CREDITS;
if (strcasecmp("CEREMONY", name) == 0)
return NEXTMAP_CEREMONY;
//if (strcasecmp("TITLE", name) == 0)
if (strcasecmp("TITLE", name) == 0)
return NEXTMAP_TITLE;
#endif
return NEXTMAP_INVALID;
}
/** Clips the console player's mouse aiming to the current view.
@ -863,7 +866,7 @@ INT16 G_SoftwareClipAimingPitch(INT32 *aiming)
INT32 limitangle;
// note: the current software mode implementation doesn't have true perspective
limitangle = ANGLE_90 - ANG10; // Some viewing fun, but not too far down...
limitangle = ANGLE_45; // Some viewing fun, but not too far down...
if (*aiming > limitangle)
*aiming = limitangle;
@ -873,6 +876,31 @@ INT16 G_SoftwareClipAimingPitch(INT32 *aiming)
return (INT16)((*aiming)>>16);
}
void G_FinalClipAimingPitch(INT32 *aiming, player_t *player, boolean skybox)
{
#ifndef HWRENDER
(void)player;
(void)skybox;
#endif
// clip it in the case we are looking a hardware 90 degrees full aiming
// (lmps, network and use F12...)
if (rendermode == render_soft
#ifdef HWRENDER
|| (rendermode == render_opengl
&& (cv_glshearing.value == 1
|| (cv_glshearing.value == 2 && R_IsViewpointThirdPerson(player, skybox))))
#endif
)
{
G_SoftwareClipAimingPitch(aiming);
}
else
{
G_ClipAimingPitch(aiming);
}
}
static INT32 G_GetValueFromControlTable(INT32 deviceID, INT32 deadzone, INT32 *controltable)
{
INT32 i, failret = NO_BINDS_REACHABLE;
@ -1257,6 +1285,7 @@ void G_StartTitleCard(void)
ST_startTitleCard();
// play the sound
if (gamestate != GS_CEREMONY)
{
sfxenum_t kstart = sfx_kstart;
if (K_CheckBossIntro() == true)
@ -2900,95 +2929,116 @@ void G_AddPlayer(INT32 playernum)
demo_extradata[playernum] |= DXD_JOINDATA|DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN|DXD_FOLLOWER; // Set everything
}
void G_ExitLevel(void)
void G_BeginLevelExit(void)
{
g_exit.losing = true;
g_exit.retry = false;
if (grandprixinfo.gp == true)
{
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator)
{
K_PlayerFinishGrandPrix(&players[i]);
}
}
}
if (!G_GametypeUsesLives() || skipstats != 0)
{
g_exit.losing = false; // never force a retry
}
else if (specialstageinfo.valid == true || (gametyperules & GTR_BOSS))
{
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator && !players[i].bot)
{
if (!K_IsPlayerLosing(&players[i]))
{
g_exit.losing = false;
break;
}
}
}
}
else if (grandprixinfo.gp == true && grandprixinfo.eventmode == GPEVENT_NONE)
{
g_exit.losing = (grandprixinfo.wonround != true);
}
if (g_exit.losing)
{
// You didn't win...
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator && !players[i].bot)
{
if (players[i].lives > 0)
{
g_exit.retry = true;
break;
}
}
}
}
if (g_exit.losing && specialstageinfo.valid)
{
exitcountdown = TICRATE;
}
else
{
exitcountdown = raceexittime+1;
}
if (g_exit.losing)
{
if (!g_exit.retry)
{
ACS_RunGameOverScript();
}
}
}
void G_FinishExitLevel(void)
{
G_ResetAllDeviceRumbles();
if (gamestate == GS_LEVEL)
{
UINT8 i;
boolean doretry = false;
if (grandprixinfo.gp == true)
if (g_exit.retry)
{
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator)
{
K_PlayerFinishGrandPrix(&players[i]);
}
}
}
if (!G_GametypeUsesLives() || skipstats != 0)
; // never force a retry
else if (specialstageinfo.valid == true || (gametyperules & GTR_BOSS))
{
doretry = true;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator && !players[i].bot)
{
if (!K_IsPlayerLosing(&players[i]))
{
doretry = false;
break;
}
}
}
}
else if (grandprixinfo.gp == true && grandprixinfo.eventmode == GPEVENT_NONE)
{
doretry = (grandprixinfo.wonround != true);
}
if (doretry)
{
// You didn't win...
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator && !players[i].bot)
{
if (players[i].lives > 0)
{
break;
}
}
}
if (i == MAXPLAYERS)
{
// GAME OVER, try again from the start!
if (grandprixinfo.gp == true
&& grandprixinfo.eventmode == GPEVENT_SPECIAL)
{
// 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.
G_HandleSaveLevel(true);
D_QuitNetGame();
CL_Reset();
D_ClearState();
M_StartControlPanel();
}
}
else
// Restart cup here whenever we do Online GP
if (!netgame)
{
// We have lives, just redo this one course.
G_SetRetryFlag();
return;
}
}
else if (g_exit.losing)
{
// We were in a Special Stage.
// We can still progress to the podium when we game over here.
const boolean special = grandprixinfo.gp == true && grandprixinfo.eventmode == GPEVENT_SPECIAL;
if (doretry == true)
if (!netgame && !special)
{
// Back to the menu with you.
G_HandleSaveLevel(true);
D_QuitNetGame();
CL_Reset();
D_ClearState();
M_StartControlPanel();
return;
}
}
@ -3291,6 +3341,10 @@ boolean G_GametypeHasTeams(void)
//
boolean G_GametypeHasSpectators(void)
{
// TODO: this would make a great debug feature for release
#ifdef DEVELOP
return true;
#endif
return (netgame || (multiplayer && demo.netgame));
}

View file

@ -126,6 +126,7 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n);
// clip the console player aiming to the view
INT32 G_ClipAimingPitch(INT32 *aiming);
INT16 G_SoftwareClipAimingPitch(INT32 *aiming);
void G_FinalClipAimingPitch(INT32 *aiming, player_t *player, boolean skybox);
extern angle_t localangle[MAXSPLITSCREENPLAYERS];
extern INT32 localaiming[MAXSPLITSCREENPLAYERS]; // should be an angle_t but signed
@ -205,7 +206,8 @@ boolean G_GametypeUsesLives(void);
boolean G_GametypeHasTeams(void);
boolean G_GametypeHasSpectators(void);
INT16 G_SometimesGetDifferentEncore(void);
void G_ExitLevel(void);
void G_BeginLevelExit(void);
void G_FinishExitLevel(void);
void G_NextLevel(void);
void G_GetNextMap(void);
void G_Continue(void);

View file

@ -310,7 +310,7 @@ static FUINT HWR_CalcSlopeLight(FUINT lightnum, pslope_t *slope, const sector_t
{
INT16 finallight = lightnum;
if (slope != NULL && P_ApplyLightOffsetFine(lightnum, sector))
if (slope != NULL && sector != NULL && P_ApplyLightOffsetFine(lightnum, sector))
{
finallight += slope->hwLightOffset;
@ -4773,7 +4773,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
if (spriterotangle != 0
&& !(splat && !(thing->renderflags & RF_NOSPLATROLLANGLE)))
{
rollangle = R_GetRollAngle(vflip
rollangle = R_GetRollAngle(papersprite == vflip
? spriterotangle : InvAngle(spriterotangle));
rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, false, sprinfo, rollangle);

View file

@ -1881,6 +1881,8 @@ state_t states[NUMSTATES] =
{SPR_ESPK, FF_FULLBRIGHT|5, 3, {NULL}, 0, 0, S_EMERALDSPARK7}, // S_EMERALDSPARK6
{SPR_ESPK, FF_FULLBRIGHT|6, 3, {NULL}, 0, 0, S_NULL}, // S_EMERALDSPARK7
{SPR_LENS, FF_FULLBRIGHT|FF_ADD|FF_TRANS10|FF_ANIMATE|11, 8, {NULL}, 7, 1, S_GAINAX_MID2}, // S_EMERALDFLARE1
// Emerald hunt shards
{SPR_SHRD, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHRD1
{SPR_SHRD, 1, -1, {NULL}, 0, 0, S_NULL}, // S_SHRD2
@ -8345,6 +8347,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
{ // MT_EMERALDFLARE
-1, // doomednum
S_INVISIBLE, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
8, // 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
8*FRACUNIT, // radius
8*FRACUNIT, // height
0, // display offset
16, // mass
0, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_EMERHUNT
320, // doomednum
S_SHRD1, // spawnstate

View file

@ -2365,6 +2365,8 @@ typedef enum state
S_EMERALDSPARK6,
S_EMERALDSPARK7,
S_EMERALDFLARE1,
// Emerald hunt shards
S_SHRD1,
S_SHRD2,
@ -5982,6 +5984,7 @@ typedef enum mobj_type
MT_SPRAYCAN,
MT_EMERALD,
MT_EMERALDSPARK,
MT_EMERALDFLARE,
MT_EMERHUNT, // Emerald Hunt
MT_EMERALDSPAWN, // Emerald spawner w/ delay

View file

@ -127,7 +127,10 @@ void K_CheckBumpers(void)
{
if (nobumpers > 0 && nobumpers >= numingame)
{
// TODO: this would make a great debug feature for release
#ifndef DEVELOP
P_DoAllPlayersExit(PF_NOCONTEST, false);
#endif
return;
}
}
@ -275,8 +278,7 @@ void K_DropEmeraldsFromPlayer(player_t *player, UINT32 emeraldType)
if ((player->emeralds & emeraldFlag) && (emeraldFlag & emeraldType))
{
mobj_t *emerald = K_SpawnChaosEmerald(player->mo->x, player->mo->y, player->mo->z, player->mo->angle - ANGLE_90, flip, emeraldFlag);
P_SetTarget(&emerald->target, player->mo);
K_SpawnChaosEmerald(player->mo->x, player->mo->y, player->mo->z, player->mo->angle - ANGLE_90, flip, emeraldFlag);
player->emeralds &= ~emeraldFlag;
break; // Drop only one emerald. Emerald wins are hard enough!

View file

@ -844,6 +844,12 @@ boolean K_CanChangeRules(boolean allowdemos)
--------------------------------------------------*/
void K_PlayerFinishGrandPrix(player_t *player)
{
if (grandprixinfo.wonround == true)
{
// This was already completed.
return;
}
if (player->exiting == false)
{
// You did not finish

View file

@ -5169,7 +5169,8 @@ static void K_DrawDirectorButton(INT32 idx, const char *label, patch_t *kp[2], I
static void K_drawDirectorHUD(void)
{
const INT32 p = G_PartyMember(consoleplayer, R_GetViewNumber());
const UINT8 viewnum = R_GetViewNumber();
const INT32 p = viewnum < G_PartySize(consoleplayer) ? G_PartyMember(consoleplayer, viewnum) : -1;
const char *itemtxt = "Join";
UINT8 offs = 0;
@ -5192,12 +5193,15 @@ static void K_drawDirectorHUD(void)
offs = 2;
}
K_DrawDirectorButton(offs + 1, "Freecam", kp_button_c[0], 0);
if (p == -1 || !playeringame[p] || players[p].spectator == false)
{
return;
}
K_DrawDirectorButton(offs + 1, "Director", kp_button_r,
// TODO: this is too close to the screen bottom
K_DrawDirectorButton(offs + 2, "Director", kp_button_r,
(directorinfo.active ? V_YELLOWMAP : 0));
if (players[p].flashing)
@ -5643,6 +5647,11 @@ void K_drawKartHUD(void)
if (stplyr->karthud[khud_trickcool])
K_drawTrickCool();
if (freecam)
{
K_DrawDirectorButton(3, "Freecam", kp_button_c[0], 0);
}
if (modeattacking || freecam) // everything after here is MP and debug only
return;
@ -5659,7 +5668,7 @@ void K_drawKartHUD(void)
K_drawKartPowerUps();
if (G_IsPartyLocal(displayplayers[viewnum]) == false && !demo.playback)
if (G_IsPartyLocal(displayplayers[viewnum]) == false)
{
K_drawDirectorHUD();
}

View file

@ -7794,6 +7794,8 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
player->mo->spritexoffset = 0;
player->mo->spriteyoffset = 0;
player->cameraOffset = 0;
if (player->curshield == KSHIELD_TOP)
{
mobj_t *top = K_GetGardenTop(player);
@ -7814,6 +7816,21 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
}
}
if (!P_MobjWasRemoved(player->whip) && (player->whip->flags2 & MF2_AMBUSH))
{
// Linear acceleration and deceleration to a peak.
// There is a constant total time to complete but the
// acceleration and deceleration times can be made
// asymmetrical.
const fixed_t hop = 24 * mapobjectscale;
const INT32 duration = 12;
const INT32 mid = (duration / 2) - 1;
const INT32 t = (duration - mid) - player->whip->fuse;
player->cameraOffset = hop - (abs(t * hop) / (t < 0 ? mid : duration - mid));
player->mo->sprzoff += player->cameraOffset;
}
K_UpdateOffroad(player);
K_UpdateDraft(player);
K_UpdateEngineSounds(player); // Thanks, VAda!
@ -10943,9 +10960,13 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
P_SetTarget(&whip->target, player->mo);
K_MatchGenericExtraFlags(whip, player->mo);
P_SpawnFakeShadow(whip, 20);
whip->fuse = 12; // Changing instawhip animation duration? Look here
player->flashing = max(player->flashing, 12);
player->mo->momz += 4*mapobjectscale;
whip->fuse = INSTAWHIP_DURATION;
player->flashing = max(player->flashing, INSTAWHIP_DURATION);
if (P_IsObjectOnGround(player->mo))
{
whip->flags2 |= MF2_AMBUSH;
}
if (!K_PowerUpRemaining(player, POWERUP_BADGE))
{

View file

@ -29,6 +29,7 @@ Make sure this matches the actual number of states
#define GROW_PHYSICS_SCALE (3*FRACUNIT/2)
#define SHRINK_PHYSICS_SCALE (3*FRACUNIT/4)
#define INSTAWHIP_DURATION (12)
#define INSTAWHIP_COOLDOWN (TICRATE*2)
#define INSTAWHIP_STARTOFRACE (255)
#define INSTAWHIP_STARTOFBATTLE (1)

View file

@ -816,7 +816,13 @@ void M_Drawer(void)
// draw pause pic
if (paused && !demo.playback && (menuactive || cv_showhud.value))
{
M_DrawPausedText(0);
// Don't cover the Stereo player!
boolean stereo_open = menuactive && currentMenu == &MISC_SoundTestDef;
if (stereo_open == false)
{
M_DrawPausedText(0);
}
}
// focus lost notification goes on top of everything, even the former everything
@ -5048,7 +5054,7 @@ void M_DrawAddons(void)
m = numwadfiles-(mainwads+musicwads+1);
V_DrawCenteredString(BASEVIDWIDTH/2, y+4, (majormods ? highlightflags : V_TRANSLUCENT), va("%ld ADD-ON%s LOADED", (long)m, (m == 1) ? "" : "S")); //+2 for music, sounds, +1 for main.kart
V_DrawCenteredString(BASEVIDWIDTH/2, y+4, (majormods ? highlightflags : V_TRANSLUCENT), va("%ld ADD-ON%s LOADED", (long)m, (m == 1) ? "" : "S")); //+2 for music, sounds, +1 for bios.pk3
}
#undef addonsseperation

View file

@ -207,6 +207,9 @@ void Obj_SneakerPanelCollide(mobj_t *pad, mobj_t *mo);
/* Emerald */
void Obj_SpawnEmeraldSparks(mobj_t *source);
void Obj_EmeraldThink(mobj_t *emerald);
void Obj_EmeraldFlareThink(mobj_t *flare);
void Obj_BeginEmeraldOrbit(mobj_t *emerald, mobj_t *target, fixed_t radius, INT32 revolution_time, tic_t fuse);
void Obj_GiveEmerald(mobj_t *emerald);
#ifdef __cplusplus
} // extern "C"

View file

@ -2869,7 +2869,8 @@ static int lib_gExitLevel(lua_State *L)
// Moved this bit to G_SetCustomExitVars
if (n >= 1) // Don't run the reset to defaults option
lib_gSetCustomExitVars(L);
G_ExitLevel();
G_BeginLevelExit();
G_FinishExitLevel();
return 0;
}

View file

@ -236,20 +236,7 @@ void M_PlaybackToggleFreecam(INT32 choice)
splitscreen = 0;
R_ExecuteSetViewSize();
P_InitCameraCmd(); // init camera controls
if (!demo.freecam) // toggle on
{
demo.freecam = true;
democam.cam = &camera[0]; // this is rather useful
}
else // toggle off
{
demo.freecam = false;
// reset democam vars:
democam.cam = NULL;
//democam.turnheld = false;
democam.keyboardlook = false; // reset only these. localangle / aiming gets set before the cam does anything anyway
}
P_ToggleDemoCamera();
}
void M_PlaybackQuit(INT32 choice)

View file

@ -1,10 +1,25 @@
#include "../k_battle.h"
#include "../k_objects.h"
#include "../k_specialstage.h"
#include "../info.h"
#include "../m_random.h"
#include "../p_local.h"
#include "../r_main.h"
#include "../s_sound.h"
#include "../tables.h"
#define emerald_type(o) ((o)->extravalue1)
#define emerald_anim_start(o) ((o)->movedir)
#define emerald_revolution_time(o) ((o)->threshold)
#define emerald_start_radius(o) ((o)->movecount)
#define emerald_target_radius(o) ((o)->extravalue2)
#define emerald_z_shift(o) ((o)->reactiontime)
#define emerald_scale_rate(o) ((o)->movefactor)
// Think of this like EMERALD_SPEED_UP / EMERALD_SPEED_UP_RATE
#define EMERALD_SPEED_UP (1) // speed up by this much...
#define EMERALD_SPEED_UP_RATE (1) // ...every N tics
void Obj_SpawnEmeraldSparks(mobj_t *mobj)
{
if (leveltime % 3 != 0)
@ -25,31 +40,95 @@ void Obj_SpawnEmeraldSparks(mobj_t *mobj)
sparkle->sprzoff = mobj->sprzoff;
}
static INT32 get_elapsed(mobj_t *emerald)
{
return leveltime - min((tic_t)emerald_anim_start(emerald), leveltime);
}
static INT32 get_revolve_time(mobj_t *emerald)
{
return max(1, emerald_revolution_time(emerald));
}
static fixed_t get_suck_factor(mobj_t *emerald)
{
const INT32 suck_time = get_revolve_time(emerald) * 2;
return (min(get_elapsed(emerald), suck_time) * FRACUNIT) / suck_time;
}
static fixed_t get_current_radius(mobj_t *emerald)
{
fixed_t s = emerald_start_radius(emerald);
fixed_t t = emerald_target_radius(emerald);
return s + FixedMul(t - s, get_suck_factor(emerald));
}
static fixed_t get_bob(mobj_t *emerald)
{
// With a fuse, the emerald experiences "speed up" and the
// scale also shrinks. All of these these effects caused
// the bob phase shift to look disproportioned.
angle_t phase = emerald->fuse ? 0 : get_elapsed(emerald) * ((ANGLE_MAX / get_revolve_time(emerald)) / 2);
return FixedMul(30 * mapobjectscale, FSIN(emerald->angle + phase));
}
static fixed_t center_of(mobj_t *mobj)
{
return mobj->z + (mobj->height / 2);
}
static fixed_t get_target_z(mobj_t *emerald)
{
fixed_t shift = FixedMul(emerald_z_shift(emerald), FRACUNIT - get_suck_factor(emerald));
return center_of(emerald->target) + get_bob(emerald) + shift;
}
static void speed_up(mobj_t *emerald)
{
// Revolution time shouldn't decrease below zero.
if (emerald_revolution_time(emerald) <= EMERALD_SPEED_UP)
{
return;
}
if (get_elapsed(emerald) % EMERALD_SPEED_UP_RATE)
{
return;
}
// Decrease the fuse proportionally to the revolution time.
const fixed_t ratio = (emerald->fuse * FRACUNIT) / emerald_revolution_time(emerald);
emerald_revolution_time(emerald) -= EMERALD_SPEED_UP;
emerald->fuse = max(1, (emerald_revolution_time(emerald) * ratio) / FRACUNIT);
}
static void Obj_EmeraldOrbitPlayer(mobj_t *emerald)
{
const int kOrbitTics = 64;
const int kPhaseTics = 128;
const fixed_t orbit_radius = 100 * mapobjectscale;
const fixed_t orbit_height = 30 * mapobjectscale;
mobj_t *targ = emerald->target;
angle_t a = emerald->angle;
fixed_t x = FixedMul(orbit_radius, FCOS(a));
fixed_t y = FixedMul(orbit_radius, FSIN(a));
angle_t phase = (ANGLE_MAX / kPhaseTics) * (leveltime % kPhaseTics);
fixed_t r = get_current_radius(emerald);
fixed_t x = FixedMul(r, FCOS(emerald->angle));
fixed_t y = FixedMul(r, FSIN(emerald->angle));
P_MoveOrigin(
emerald,
targ->x + x,
targ->y + y,
targ->z + targ->height + FixedMul(orbit_height, FSIN(a + phase))
emerald->target->x + x,
emerald->target->y + y,
get_target_z(emerald)
);
emerald->angle += ANGLE_MAX / kOrbitTics;
emerald->angle += ANGLE_MAX / get_revolve_time(emerald);
if (emerald->fuse > 0)
{
speed_up(emerald);
P_InstaScale(emerald, emerald->fuse * emerald_scale_rate(emerald));
}
}
void Obj_EmeraldThink(mobj_t *emerald)
@ -84,3 +163,156 @@ void Obj_EmeraldThink(mobj_t *emerald)
K_BattleOvertimeKiller(emerald);
}
static mobj_t *spawn_glow(mobj_t *flare)
{
mobj_t *targ = flare->target;
mobj_t *x = P_SpawnGhostMobj(targ);
x->old_x = targ->old_x;
x->old_y = targ->old_y;
x->old_z = targ->old_z;
x->fuse = 2; // this actually does last one tic
x->extravalue1 = 1;
x->extravalue2 = 0;
x->renderflags = RF_ADD | RF_ALWAYSONTOP;
// FIXME: linkdraw doesn't work consistently, so I drew it on top of everyting (and through walls)
#if 0
P_SetTarget(&x->tracer, targ);
x->flags2 |= MF2_LINKDRAW;
x->dispoffset = 1000;
#endif
return x;
}
static mobj_t *spawn_glow_colorize(mobj_t *flare)
{
mobj_t *x = spawn_glow(flare);
x->color = flare->color;
x->colorized = true;
return x;
}
void Obj_EmeraldFlareThink(mobj_t *flare)
{
const INT32 kExtraTics = 3;
const INT32 flare_tics = states[S_EMERALDFLARE1].tics + kExtraTics;
if (P_MobjWasRemoved(flare->target))
{
P_RemoveMobj(flare);
return;
}
// Target is assumed to be the emerald in orbit. When
// emerald fuse runs out, it shall update player's emerald
// flags. Time the flare animation so it ends with the
// emerald fuse.
if (!flare->fuse && flare->target->fuse > flare_tics)
{
return;
}
if (flare->state == &states[S_INVISIBLE])
{
// In special stages, just follow the emerald.
if (specialstageinfo.valid == false)
{
// Update target to player. We don't need to track
// the emerald anymore.
P_SetTarget(&flare->target, flare->target->target);
if (P_MobjWasRemoved(flare->target))
{
P_RemoveMobj(flare);
return;
}
}
P_SetMobjState(flare, S_EMERALDFLARE1);
flare->fuse = flare_tics;
}
// Focus on center of player.
P_SetOrigin(flare, flare->target->x, flare->target->y, center_of(flare->target));
if (leveltime & 1)
{
// Stacked for more exposure
spawn_glow_colorize(flare);
spawn_glow(flare);
spawn_glow(flare);
}
}
static void spawn_lens_flare(mobj_t *emerald)
{
mobj_t *flare = P_SpawnMobjFromMobj(emerald, 0, 0, 0, MT_EMERALDFLARE);
P_SetTarget(&flare->target, emerald);
P_InstaScale(flare, emerald->target->scale);
flare->color = emerald->color;
flare->colorized = true;
flare->renderflags |= RF_ALWAYSONTOP;
// FIXME: linkdraw doesn't work consistently, so I drew it on top of everyting (and through walls)
#if 0
P_SetTarget(&flare->tracer, emerald->target);
flare->flags2 |= MF2_LINKDRAW;
flare->dispoffset = 1000;
#endif
}
void Obj_BeginEmeraldOrbit(mobj_t *emerald, mobj_t *target, fixed_t radius, INT32 revolution_time, tic_t fuse)
{
P_SetTarget(&emerald->target, target);
emerald_anim_start(emerald) = leveltime;
emerald_revolution_time(emerald) = revolution_time;
emerald_start_radius(emerald) = R_PointToDist2(target->x, target->y, emerald->x, emerald->y);
emerald_target_radius(emerald) = radius;
emerald->fuse = fuse;
if (fuse)
{
emerald_scale_rate(emerald) = emerald->scale / fuse;
}
emerald->angle = R_PointToAngle2(target->x, target->y, emerald->x, emerald->y);
emerald_z_shift(emerald) = emerald->z - get_target_z(emerald);
emerald->flags |= MF_NOGRAVITY | MF_NOCLIP | MF_NOCLIPTHING | MF_NOCLIPHEIGHT;
emerald->shadowscale = 0;
spawn_lens_flare(emerald);
}
void Obj_GiveEmerald(mobj_t *emerald)
{
if (P_MobjWasRemoved(emerald->target))
{
return;
}
player_t *player = emerald->target->player;
if (!player)
{
return;
}
player->emeralds |= emerald_type(emerald);
K_CheckEmeralds(player);
S_StartSound(emerald->target, emerald->info->deathsound);
}

View file

@ -34,7 +34,8 @@ void Obj_InstaWhipThink (mobj_t *whip)
// Visuals
whip->renderflags |= RF_NOSPLATBILLBOARD|RF_FULLBRIGHT;
if (whip->renderflags & RF_DONTDRAW)
// This is opposite of player flashing tics
if (leveltime & 1)
whip->renderflags &= ~RF_DONTDRAW;
else
whip->renderflags |= RF_DONTDRAW;

View file

@ -789,11 +789,7 @@ void Obj_CreateShrinkPohbees(player_t *owner)
pohbees[j].start = GetPohbeeStart(player->nextwaypoint);
pohbees[j].end = endWaypoint;
pohbees[j].lasers = 1;
if (player->position == 1)
{
pohbees[j].first = true;
}
pohbees[j].first = (player->position == 1);
numPohbees++;
}
@ -801,6 +797,7 @@ void Obj_CreateShrinkPohbees(player_t *owner)
for (i = 0; i < numPohbees; i++)
{
// omg pobby hi!!!
CreatePohbee(owner, pohbees[i].start, pohbees[i].end, pohbees[i].lasers);
if (pohbees[i].first == true)

View file

@ -883,7 +883,7 @@ boolean Obj_UFOEmeraldCollect(mobj_t *ufo, mobj_t *toucher)
const int kScaleTics = 16;
// Emerald will now orbit the player
P_SetTarget(&emerald->target, toucher);
Obj_BeginEmeraldOrbit(emerald, toucher, 100 * mapobjectscale, 64, 0);
// Scale down because the emerald is huge
// Super Emerald needs to be scaled down further

View file

@ -464,9 +464,14 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (toucher->hitlag > 0)
return;
player->emeralds |= special->extravalue1;
K_CheckEmeralds(player);
break;
// Emerald will now orbit the player
{
const tic_t orbit = 2*TICRATE;
Obj_BeginEmeraldOrbit(special, toucher, toucher->radius, orbit, orbit * 20);
}
return;
case MT_SPECIAL_UFO:
if (Obj_UFOEmeraldCollect(special, toucher) == false)
{
@ -2031,7 +2036,15 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source,
if (!player->exiting && (specialstageinfo.valid == true || modeattacking & ATTACKING_SPB))
{
// TODO: this would make a great debug feature for release
#ifdef DEVELOP
if (type != DMG_SPECTATOR)
{
P_DoPlayerExit(player, PF_NOCONTEST);
}
#else
P_DoPlayerExit(player, PF_NOCONTEST);
#endif
}
if (player->exiting)

View file

@ -126,6 +126,9 @@ struct camera_t
// SRB2Kart: camera pitches on slopes
angle_t pitch;
// Freecam: aiming needs to be reset after switching from chasecam
boolean reset_aiming;
// Interpolation data
fixed_t old_x, old_y, old_z;
angle_t old_angle, old_aiming;
@ -134,13 +137,7 @@ struct camera_t
// demo freecam or something before i commit die
struct demofreecam_s {
camera_t *cam; // this is useful when the game is paused, notably
mobj_t *soundmobj; // mobj to play sound from, used in s_sound
angle_t localangle; // keeps track of the cam angle for cmds
angle_t localaiming; // ditto with aiming
boolean turnheld; // holding turn button for gradual turn speed
boolean keyboardlook; // keyboard look
UINT8 button_a_held; // A button was held since entering from menu, so don't move camera
};
extern struct demofreecam_s democam;
@ -159,7 +156,7 @@ boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam);
void P_SlideCameraMove(camera_t *thiscam);
void P_DemoCameraMovement(camera_t *cam);
boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcalled);
void P_InitCameraCmd(void);
void P_ToggleDemoCamera(void);
boolean P_PlayerInPain(player_t *player);
void P_ResetPlayer(player_t *player);

View file

@ -7550,6 +7550,14 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
case MT_EMERALD:
Obj_EmeraldThink(mobj);
if (P_MobjWasRemoved(mobj))
{
return false;
}
break;
case MT_EMERALDFLARE:
Obj_EmeraldFlareThink(mobj);
if (P_MobjWasRemoved(mobj))
{
return false;
@ -9824,6 +9832,12 @@ static boolean P_FuseThink(mobj_t *mobj)
break;
}
case MT_EMERALD:
{
Obj_GiveEmerald(mobj);
P_RemoveMobj(mobj);
return false;
}
case MT_PLAYER:
break; // don't remove
default:
@ -11840,7 +11854,10 @@ void P_SpawnPlayer(INT32 playernum)
}
else // Otherwise, never spectator.
{
// TODO: this would make a great debug feature for release
#ifndef DEVELOP
p->spectator = false;
#endif
}
}
@ -11968,6 +11985,15 @@ void P_SpawnPlayer(INT32 playernum)
{
K_ToggleDirector(players[consoleplayer].spectator && pcount > 0);
}
// TODO: handle splitscreen
// Spectators can switch to freecam. This should be
// disabled when they enter the race, or when the level
// changes.
if (playernum == consoleplayer && !demo.playback)
{
demo.freecam = false;
}
}
void P_AfterPlayerSpawn(INT32 playernum)
@ -12001,12 +12027,15 @@ void P_AfterPlayerSpawn(INT32 playernum)
p->drawangle = mobj->angle;
for (i = 0; i <= r_splitscreen; i++)
if (p->spectator == false)
{
if (camera[i].chase)
for (i = 0; i <= r_splitscreen; i++)
{
if (displayplayers[i] == playernum)
P_ResetCamera(p, &camera[i]);
if (camera[i].chase)
{
if (displayplayers[i] == playernum)
P_ResetCamera(p, &camera[i]);
}
}
}

View file

@ -5735,6 +5735,10 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending)
WRITEUINT32(save->p, racecountdown);
WRITEUINT32(save->p, exitcountdown);
// exitcondition_t
WRITEUINT8(save->p, g_exit.losing);
WRITEUINT8(save->p, g_exit.retry);
WRITEFIXED(save->p, gravity);
WRITEFIXED(save->p, mapobjectscale);
@ -5910,6 +5914,10 @@ static boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading)
racecountdown = READUINT32(save->p);
exitcountdown = READUINT32(save->p);
// exitcondition_t
g_exit.losing = READUINT8(save->p);
g_exit.retry = READUINT8(save->p);
gravity = READFIXED(save->p);
mapobjectscale = READFIXED(save->p);

View file

@ -1220,46 +1220,6 @@ static void P_LoadSidedefs(UINT8 *data)
sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
break;
case 413: // Change music
{
if (!isfrontside)
break;
char process[8+1];
sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
if (msd->bottomtexture[0] != '-' || msd->bottomtexture[1] != '\0')
{
M_Memcpy(process,msd->bottomtexture,8);
process[8] = '\0';
sd->bottomtexture = get_number(process);
}
if (!(msd->midtexture[0] == '-' && msd->midtexture[1] == '\0') || msd->midtexture[1] != '\0')
{
M_Memcpy(process,msd->midtexture,8);
process[8] = '\0';
sd->midtexture = get_number(process);
}
if (msd->toptexture[0] != '-' && msd->toptexture[1] != '\0')
{
sd->line->stringargs[0] = Z_Malloc(7, PU_LEVEL, NULL);
M_Memcpy(process,msd->toptexture,8);
process[8] = '\0';
// If they type in O_ or D_ and their music name, just shrug,
// then copy the rest instead.
if ((process[0] == 'O' || process[0] == 'D') && process[7])
M_Memcpy(sd->line->stringargs[0], process+2, 6);
else // Assume it's a proper music name.
M_Memcpy(sd->line->stringargs[0], process, 6);
sd->line->stringargs[0][6] = '\0';
}
break;
}
case 414: // Play SFX
{
sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
@ -5586,28 +5546,6 @@ static void P_ConvertBinaryLinedefTypes(void)
lines[i].args[3] = sides[lines[i].sidenum[0]].rowoffset >> FRACBITS;
lines[i].args[4] = lines[i].frontsector->ceilingheight >> FRACBITS;
break;
case 413: //Change music
if (lines[i].flags & ML_NOCLIMB)
lines[i].args[1] |= TMM_ALLPLAYERS;
if (lines[i].flags & ML_SKEWTD)
lines[i].args[1] |= TMM_OFFSET;
if (lines[i].flags & ML_NOSKEW)
lines[i].args[1] |= TMM_FADE;
if (lines[i].flags & ML_BLOCKPLAYERS)
lines[i].args[1] |= TMM_NORELOAD;
if (lines[i].flags & ML_NOTBOUNCY)
lines[i].args[1] |= TMM_FORCERESET;
if (lines[i].flags & ML_MIDSOLID)
lines[i].args[1] |= TMM_NOLOOP;
if (lines[i].flags & ML_MIDPEG)
lines[i].args[1] |= TMM_NOCREDIT;
lines[i].args[2] = sides[lines[i].sidenum[0]].midtexture;
lines[i].args[3] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS;
lines[i].args[4] = sides[lines[i].sidenum[0]].rowoffset >> FRACBITS;
lines[i].args[5] = (lines[i].sidenum[1] != 0xffff) ? sides[lines[i].sidenum[1]].textureoffset >> FRACBITS : 0;
lines[i].args[6] = (lines[i].sidenum[1] != 0xffff) ? sides[lines[i].sidenum[1]].rowoffset >> FRACBITS : -1;
lines[i].args[7] = sides[lines[i].sidenum[0]].bottomtexture;
break;
case 414: //Play sound effect
lines[i].args[3] = tag;
if (tag != 0)
@ -7524,6 +7462,9 @@ static void P_InitLevelSettings(void)
racecountdown = exitcountdown = musiccountdown = exitfadestarted = 0;
curlap = bestlap = 0; // SRB2Kart
g_exit.losing = false;
g_exit.retry = false;
// Gamespeed and frantic items
gamespeed = KARTSPEED_EASY;
franticitems = false;
@ -7754,6 +7695,8 @@ static void P_SetupCamera(UINT8 pnum, camera_t *cam)
cam->subsector = R_PointInSubsector(cam->x, cam->y); // make sure camera has a subsector set -- Monster Iestyn (12/11/18)
}
}
cam->chase = false; // tell camera to reset its position next tic
}
static void P_InitCamera(void)
@ -8165,7 +8108,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
if (K_PodiumSequence())
{
// mapmusrng is set by local player position in K_ResetCeremony
P_ResetLevelMusic();
P_LoadLevelMusic();
}
else if (gamestate == GS_LEVEL)

View file

@ -2886,88 +2886,6 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha
}
break;
case 413: // Change music
// FIXME: port to new music system
#if 0
// console player only unless TMM_ALLPLAYERS is set
if ((args[1] & TMM_ALLPLAYERS) || (mo && mo->player && P_IsLocalPlayer(mo->player)) || titlemapinaction)
{
boolean musicsame = (!stringargs[0] || !stringargs[0][0] || !strnicmp(stringargs[0], S_MusicName(), 7));
UINT16 tracknum = (UINT16)max(args[7], 0);
INT32 position = (INT32)max(args[2], 0);
UINT32 prefadems = (UINT32)max(args[3], 0);
UINT32 postfadems = (UINT32)max(args[4], 0);
UINT8 fadetarget = (UINT8)max(args[5], 0);
INT16 fadesource = (INT16)max(args[6], -1);
// Seek offset from current song position
if (args[1] & TMM_OFFSET)
{
// adjust for loop point if subtracting
if (position < 0 && S_GetMusicLength() &&
S_GetMusicPosition() > S_GetMusicLoopPoint() &&
S_GetMusicPosition() + position < S_GetMusicLoopPoint())
position = max(S_GetMusicLength() - (S_GetMusicLoopPoint() - (S_GetMusicPosition() + position)), 0);
else
position = max(S_GetMusicPosition() + position, 0);
}
// Fade current music to target volume (if music won't be changed)
if ((args[1] & TMM_FADE) && fadetarget && musicsame)
{
// 0 fadesource means fade from current volume.
// meaning that we can't specify volume 0 as the source volume -- this starts at 1.
if (!fadesource)
fadesource = -1;
if (!postfadems)
S_SetInternalMusicVolume(fadetarget);
else
S_FadeMusicFromVolume(fadetarget, fadesource, postfadems);
if (position)
S_SetMusicPosition(position);
}
// Change the music and apply position/fade operations
else
{
if (!stringargs[0])
break;
strncpy(mapmusname, stringargs[0], 7);
mapmusname[6] = 0;
mapmusflags = tracknum & MUSIC_TRACKMASK;
if (!(args[1] & TMM_NORELOAD))
mapmusflags |= MUSIC_RELOADRESET;
if (args[1] & TMM_FORCERESET)
mapmusflags |= MUSIC_FORCERESET;
mapmusposition = position;
mapmusresume = 0;
S_ChangeMusicEx(mapmusname, mapmusflags, !(args[1] & TMM_NOLOOP), position,
!(args[1] & TMM_FADE) ? prefadems : 0,
!(args[1] & TMM_FADE) ? postfadems : 0);
if (!(args[1] & TMM_NOCREDIT))
S_ShowMusicCredit();
if ((args[1] & TMM_FADE) && fadetarget)
{
if (!postfadems)
S_SetInternalMusicVolume(fadetarget);
else
S_FadeMusicFromVolume(fadetarget, fadesource, postfadems);
}
}
// Except, you can use the TMM_NORELOAD flag to change this behavior.
// if (mapmusflags & MUSIC_RELOADRESET) then it will reset the music in G_PlayerReborn.
}
#endif
break;
case 414: // Play SFX
P_PlaySFX(stringargs[0] ? get_number(stringargs[0]) : sfx_None, mo, callsec, args[3], args[1], args[2]);
break;

View file

@ -729,8 +729,8 @@ void P_Ticker(boolean run)
timeinmap = (timeinmap-1) & ~3;
G_PreviewRewind(leveltime);
}
else if (demo.freecam && democam.cam) // special case: allow freecam to MOVE during pause!
P_DemoCameraMovement(democam.cam);
else
P_RunChaseCameras(); // special case: allow freecam to MOVE during pause!
return;
}

View file

@ -1082,6 +1082,15 @@ boolean P_IsDisplayPlayer(player_t *player)
return false;
}
// Freecam still techically has a player in
// displayplayers. But since the camera is detached, it
// would be weird if sounds were heard from that player's
// perspective.
if (demo.freecam)
{
return false;
}
for (i = 0; i <= r_splitscreen; i++) // DON'T skip P1
{
if (player == &players[displayplayers[i]])
@ -1309,19 +1318,12 @@ void P_DoPlayerExit(player_t *player, pflags_t flags)
if (P_CheckRacers() && !exitcountdown)
{
if (specialout == true)
{
exitcountdown = TICRATE;
}
else
{
exitcountdown = raceexittime+1;
}
G_BeginLevelExit();
}
}
else if (!exitcountdown) // All other gametypes
{
exitcountdown = raceexittime+1;
G_BeginLevelExit();
}
if (grandprixinfo.gp == true && player->bot == false && losing == false)
@ -2350,45 +2352,6 @@ static void P_UpdatePlayerAngle(player_t *player)
}
}
//
// P_SpectatorMovement
//
// Control for spectators in multiplayer
//
static void P_SpectatorMovement(player_t *player)
{
ticcmd_t *cmd = &player->cmd;
P_UpdatePlayerAngle(player);
ticruned++;
if (!(cmd->flags & TICCMD_RECEIVED))
ticmiss++;
if (cmd->buttons & BT_ACCELERATE)
player->mo->z += 32*mapobjectscale;
else if (cmd->buttons & BT_BRAKE)
player->mo->z -= 32*mapobjectscale;
if (!(player->mo->flags & MF_NOCLIPHEIGHT))
{
if (player->mo->z > player->mo->ceilingz - player->mo->height)
player->mo->z = player->mo->ceilingz - player->mo->height;
if (player->mo->z < player->mo->floorz)
player->mo->z = player->mo->floorz;
}
player->mo->momx = player->mo->momy = player->mo->momz = 0;
if (cmd->forwardmove != 0)
{
P_Thrust(player->mo, player->mo->angle, cmd->forwardmove*mapobjectscale);
// Quake-style flying spectators :D
player->mo->momz += FixedMul(cmd->forwardmove*mapobjectscale, AIMINGTOSLOPE(player->aiming));
}
}
//
// P_MovePlayer
void P_MovePlayer(player_t *player)
@ -2415,7 +2378,6 @@ void P_MovePlayer(player_t *player)
if (player->spectator)
{
player->mo->eflags &= ~MFE_VERTICALFLIP; // deflip...
P_SpectatorMovement(player);
return;
}
@ -2970,184 +2932,87 @@ fixed_t t_cam_dist[MAXSPLITSCREENPLAYERS] = {-42,-42,-42,-42};
fixed_t t_cam_height[MAXSPLITSCREENPLAYERS] = {-42,-42,-42,-42};
fixed_t t_cam_rotate[MAXSPLITSCREENPLAYERS] = {-42,-42,-42,-42};
// Heavily simplified version of G_BuildTicCmd that only takes the local first player's control input and converts it to readable ticcmd_t
// we then throw that ticcmd garbage in the camera and make it move
// TODO: please just use the normal ticcmd function somehow
static ticcmd_t cameracmd;
struct demofreecam_s democam;
// called by m_menu to reinit cam input every time it's toggled
void P_InitCameraCmd(void)
{
memset(&cameracmd, 0, sizeof(ticcmd_t)); // initialize cmd
}
static ticcmd_t *P_CameraCmd(camera_t *cam)
{
/*
INT32 forward, axis; //i
// these ones used for multiple conditions
boolean turnleft, turnright, mouseaiming;
boolean invertmouse, lookaxis, usejoystick, kbl;
INT32 player_invert;
INT32 screen_invert;
*/
ticcmd_t *cmd = &cameracmd;
(void)cam;
if (!demo.playback)
return cmd; // empty cmd, no.
/*
kbl = democam.keyboardlook;
G_CopyTiccmd(cmd, I_BaseTiccmd(), 1); // empty, or external driver
mouseaiming = true;
invertmouse = cv_invertmouse.value;
lookaxis = cv_lookaxis[0].value;
usejoystick = true;
turnright = PlayerInputDown(1, gc_turnright);
turnleft = PlayerInputDown(1, gc_turnleft);
axis = PlayerJoyAxis(1, AXISTURN);
if (encoremode)
{
turnright ^= turnleft; // swap these using three XORs
turnleft ^= turnright;
turnright ^= turnleft;
axis = -axis;
}
if (axis != 0)
{
turnright = turnright || (axis > 0);
turnleft = turnleft || (axis < 0);
}
forward = 0;
cmd->turning = 0;
// let movement keys cancel each other out
if (turnright && !(turnleft))
{
cmd->turning -= KART_FULLTURN;
}
else if (turnleft && !(turnright))
{
cmd->turning += KART_FULLTURN;
}
cmd->turning -= (mousex * 8) * (encoremode ? -1 : 1);
axis = PlayerJoyAxis(1, AXISMOVE);
if (PlayerInputDown(1, gc_a) || (usejoystick && axis > 0))
cmd->buttons |= BT_ACCELERATE;
axis = PlayerJoyAxis(1, AXISBRAKE);
if (PlayerInputDown(1, gc_brake) || (usejoystick && axis > 0))
cmd->buttons |= BT_BRAKE;
axis = PlayerJoyAxis(1, AXISAIM);
if (PlayerInputDown(1, gc_aimforward) || (usejoystick && axis < 0))
forward += MAXPLMOVE;
if (PlayerInputDown(1, gc_aimbackward) || (usejoystick && axis > 0))
forward -= MAXPLMOVE;
// fire with any button/key
axis = PlayerJoyAxis(1, AXISFIRE);
if (PlayerInputDown(1, gc_fire) || (usejoystick && axis > 0))
cmd->buttons |= BT_ATTACK;
// spectator aiming shit, ahhhh...
player_invert = invertmouse ? -1 : 1;
screen_invert = 1; // nope
// mouse look stuff (mouse look is not the same as mouse aim)
kbl = false;
// looking up/down
cmd->aiming += (mlooky<<19)*player_invert*screen_invert;
axis = PlayerJoyAxis(1, AXISLOOK);
// spring back if not using keyboard neither mouselookin'
if (!kbl && !lookaxis && !mouseaiming)
cmd->aiming = 0;
if (PlayerInputDown(1, gc_lookup) || (axis < 0))
{
cmd->aiming += KB_LOOKSPEED * screen_invert;
kbl = true;
}
else if (PlayerInputDown(1, gc_lookdown) || (axis > 0))
{
cmd->aiming -= KB_LOOKSPEED * screen_invert;
kbl = true;
}
if (PlayerInputDown(1, gc_centerview)) // No need to put a spectator limit on this one though :V
cmd->aiming = 0;
cmd->forwardmove += (SINT8)forward;
if (cmd->forwardmove > MAXPLMOVE)
cmd->forwardmove = MAXPLMOVE;
else if (cmd->forwardmove < -MAXPLMOVE)
cmd->forwardmove = -MAXPLMOVE;
if (cmd->turning > KART_FULLTURN)
cmd->turning = KART_FULLTURN;
else if (cmd->turning < -KART_FULLTURN)
cmd->turning = -KART_FULLTURN;
democam.keyboardlook = kbl;
*/
return cmd;
}
void P_DemoCameraMovement(camera_t *cam)
{
ticcmd_t *cmd;
angle_t thrustangle;
mobj_t *awayviewmobj_hack;
player_t *lastp;
// update democam stuff with what we got here:
democam.cam = cam;
democam.localangle = cam->angle;
democam.localaiming = cam->aiming;
boolean moving = false;
// first off we need to get button input
cmd = P_CameraCmd(cam);
cmd = D_LocalTiccmd(0);
if (cmd->aiming != 0)
{
cam->aiming += cmd->aiming << TICCMD_REDUCE;
cam->reset_aiming = false;
}
cam->aiming += cmd->aiming << TICCMD_REDUCE;
cam->angle += cmd->turning << TICCMD_REDUCE;
democam.localangle += cmd->turning << TICCMD_REDUCE;
democam.localaiming += cmd->aiming << TICCMD_REDUCE;
cam->aiming = G_ClipAimingPitch((INT32 *)&cam->aiming);
democam.localaiming = G_ClipAimingPitch((INT32 *)&democam.localaiming);
// camera movement:
if (cmd->buttons & BT_ACCELERATE)
cam->z += 32*mapobjectscale;
else if (cmd->buttons & BT_BRAKE)
cam->z -= 32*mapobjectscale;
if (!democam.button_a_held)
{
if (cmd->buttons & BT_ACCELERATE)
{
cam->z += 32*mapobjectscale;
moving = true;
}
else if (cmd->buttons & BT_BRAKE)
{
cam->z -= 32*mapobjectscale;
moving = true;
}
}
if (!(cmd->buttons & (BT_ACCELERATE | BT_DRIFT)) && democam.button_a_held)
{
democam.button_a_held--;
}
// if you hold item, you will lock on to displayplayer. (The last player you were ""f12-ing"")
if (cmd->buttons & BT_ATTACK)
if (demo.freecam && cmd->buttons & BT_ATTACK)
{
lastp = &players[displayplayers[0]]; // Fun fact, I was trying displayplayers[0]->mo as if it was Lua like an absolute idiot.
cam->angle = R_PointToAngle2(cam->x, cam->y, lastp->mo->x, lastp->mo->y);
cam->aiming = R_PointToAngle2(0, cam->z, R_PointToDist2(cam->x, cam->y, lastp->mo->x, lastp->mo->y), lastp->mo->z + lastp->mo->scale*128*P_MobjFlip(lastp->mo)); // This is still unholy. Aim a bit above their heads.
cam->reset_aiming = false;
}
if (cmd->forwardmove != 0)
{
moving = true;
}
// After switching to democam, the vertical angle of
// chasecam is inherited. This is intentional because it
// creates a smooth transition. However, moving
// forward/back will have a slope. So, as long as democam
// controls haven't been used to alter the vertical angle,
// slowly reset it to flat.
if ((cam->reset_aiming && moving) || ((cmd->buttons & BT_DRIFT) && !democam.button_a_held))
{
INT32 aiming = cam->aiming;
INT32 smooth = FixedMul(ANGLE_11hh / 4, FCOS(cam->aiming));
if (abs(smooth) < abs(aiming))
{
cam->aiming -= smooth * intsign(aiming);
}
else
{
cam->aiming = 0;
cam->reset_aiming = false; // completely smoothed out
}
}
G_FinalClipAimingPitch((INT32 *)&cam->aiming, NULL, false);
cam->momx = cam->momy = cam->momz = 0;
if (cmd->forwardmove != 0)
@ -3156,25 +3021,35 @@ void P_DemoCameraMovement(camera_t *cam)
cam->x += FixedMul(cmd->forwardmove*mapobjectscale, FINECOSINE(thrustangle));
cam->y += FixedMul(cmd->forwardmove*mapobjectscale, FINESINE(thrustangle));
cam->z += FixedMul(cmd->forwardmove*mapobjectscale, AIMINGTOSLOPE(cam->aiming));
if (!cam->reset_aiming)
{
cam->z += FixedMul(cmd->forwardmove*mapobjectscale, AIMINGTOSLOPE(cam->aiming));
}
// momentums are useless here, directly add to the coordinates
// this.......... doesn't actually check for floors and walls and whatnot but the function to do that is a pure mess so fuck that.
// besides freecam going inside walls sounds pretty cool on paper.
}
// awayviewmobj hack; this is to prevent us from hearing sounds from the player's perspective
awayviewmobj_hack = P_SpawnMobj(cam->x, cam->y, cam->z, MT_THOK);
awayviewmobj_hack->tics = 2;
awayviewmobj_hack->renderflags |= RF_DONTDRAW;
democam.soundmobj = awayviewmobj_hack;
// update subsector to avoid crashes;
cam->subsector = R_PointInSubsector(cam->x, cam->y);
}
void P_ToggleDemoCamera(void)
{
if (!demo.freecam) // toggle on
{
demo.freecam = true;
democam.button_a_held = 2;
camera[0].reset_aiming = true;
}
else // toggle off
{
demo.freecam = false;
}
}
void P_ResetCamera(player_t *player, camera_t *thiscam)
{
tic_t tries = 0;
@ -3211,6 +3086,8 @@ void P_ResetCamera(player_t *player, camera_t *thiscam)
thiscam->radius = 20*FRACUNIT;
thiscam->height = 16*FRACUNIT;
thiscam->reset_aiming = true;
while (!P_MoveChaseCamera(player,thiscam,true) && ++tries < 2*TICRATE);
}
@ -3239,8 +3116,6 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
thiscam->old_angle = thiscam->angle;
thiscam->old_aiming = thiscam->aiming;
democam.soundmobj = NULL; // reset this each frame, we don't want the game crashing for stupid reasons now do we
// We probably shouldn't move the camera if there is no player or player mobj somehow
if (!player || !player->mo)
return true;
@ -3249,12 +3124,15 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
if (thiscam->subsector == NULL || thiscam->subsector->sector == NULL)
return true;
if (demo.freecam)
if (demo.freecam || player->spectator)
{
P_DemoCameraMovement(thiscam);
return true;
}
if (paused || P_AutoPause())
return true;
playerScale = FixedDiv(player->mo->scale, mapobjectscale);
scaleDiff = playerScale - FRACUNIT;
@ -3542,6 +3420,8 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
z = mo->z + pviewheight + distz;
}
z += player->cameraOffset;
// point viewed by the camera
// this point is just 64 unit forward the player
dist = 64*cameraScale;
@ -3586,13 +3466,13 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
if (mo->eflags & MFE_VERTICALFLIP)
{
angle = R_PointToAngle2(0, thiscam->z + thiscam->height, dist, mo->z + mo->height - player->mo->height);
angle = R_PointToAngle2(0, thiscam->z + thiscam->height, dist, mo->z + mo->height - player->mo->height + player->cameraOffset);
if (thiscam->pitch < ANGLE_180 && thiscam->pitch > angle)
angle += (thiscam->pitch - angle)/2;
}
else
{
angle = R_PointToAngle2(0, thiscam->z, dist, mo->z + player->mo->height);
angle = R_PointToAngle2(0, thiscam->z, dist, mo->z + player->mo->height + player->cameraOffset);
if (thiscam->pitch >= ANGLE_180 && thiscam->pitch < angle)
angle -= (angle - thiscam->pitch)/2;
}
@ -3890,7 +3770,7 @@ void P_DoTimeOver(player_t *player)
if (!exitcountdown)
{
exitcountdown = raceexittime;
G_BeginLevelExit();
}
}
@ -4802,6 +4682,22 @@ void P_ForceLocalAngle(player_t *player, angle_t angle)
break;
}
}
// jartha: OK, I don't really know how ticcmds work. The
// specific problem I'm trying to fix is that, on level
// load, the player angle gets reset. But the ticcmds
// copied in afterward don't match this angle, and they
// influence the player steering.
for (i = 0; i <= splitscreen; i++)
{
if (player == &players[g_localplayers[i]])
{
D_ResetTiccmdAngle(i, angle);
localsteering[i] = angle;
break;
}
}
}
boolean P_PlayerFullbright(player_t *player)

View file

@ -22,9 +22,6 @@
#include "r_state.h"
#include "z_zone.h"
#include "console.h" // con_startup_loadprogress
#ifdef HWRENDER
#include "hardware/hw_main.h" // for cv_glshearing
#endif
static CV_PossibleValue_t fpscap_cons_t[] = {
#ifdef DEVELOP
@ -117,23 +114,7 @@ static vector3_t *R_LerpVector3(const vector3_t *from, const vector3_t *to, fixe
// 18/08/18: (No it's actually 16*viewheight, thanks Jimita for finding this out)
static void R_SetupFreelook(player_t *player, boolean skybox)
{
#ifndef HWRENDER
(void)player;
(void)skybox;
#endif
// clip it in the case we are looking a hardware 90 degrees full aiming
// (lmps, network and use F12...)
if (rendermode == render_soft
#ifdef HWRENDER
|| (rendermode == render_opengl
&& (cv_glshearing.value == 1
|| (cv_glshearing.value == 2 && R_IsViewpointThirdPerson(player, skybox))))
#endif
)
{
G_SoftwareClipAimingPitch((INT32 *)&aimingangle);
}
G_FinalClipAimingPitch((INT32 *)&aimingangle, player, skybox);
centeryfrac = (viewheight/2)<<FRACBITS;

View file

@ -81,6 +81,7 @@ mobj_t *r_viewmobj;
int r_splitscreen;
fixed_t rendertimefrac;
fixed_t rendertimefrac_unpaused;
fixed_t renderdeltatics;
boolean renderisnewtic;
@ -978,7 +979,7 @@ angle_t R_ViewRollAngle(const player_t *player)
if (cv_tilting.value)
{
if (!player->spectator)
if (!player->spectator && !demo.freecam)
{
roll += player->tilt;
}
@ -1227,7 +1228,7 @@ R_SetupCommonFrame
else
newview->sector = R_PointInSubsector(newview->x, newview->y)->sector;
R_InterpolateView(rendertimefrac);
R_InterpolateView(rendertimefrac_unpaused);
}
static void R_SetupAimingFrame(int s)
@ -1265,8 +1266,12 @@ void R_SetupFrame(int s)
R_SetViewContext(VIEWCONTEXT_PLAYER1 + s);
if (player->spectator) // no spectator chasecam
chasecam = false; // force chasecam off
if (player->spectator)
{
// Free flying spectator uses demo freecam. This
// requires chasecam to be enabled.
chasecam = true;
}
if (chasecam && (thiscam && !thiscam->chase))
{
@ -1292,7 +1297,7 @@ void R_SetupFrame(int s)
R_SetupCommonFrame(player, r_viewmobj->subsector);
}
else if (!player->spectator && chasecam)
else if (chasecam)
// use outside cam view
{
r_viewmobj = NULL;
@ -1430,10 +1435,8 @@ boolean R_ViewpointHasChasecam(player_t *player)
}
}
if (player->playerstate == PST_DEAD || gamestate == GS_TITLESCREEN)
if (player->playerstate == PST_DEAD || gamestate == GS_TITLESCREEN || player->spectator)
chasecam = true; // force chasecam on
else if (player->spectator) // no spectator chasecam
chasecam = false; // force chasecam off
return chasecam;
}
@ -1446,7 +1449,7 @@ boolean R_IsViewpointThirdPerson(player_t *player, boolean skybox)
if (player->awayview.tics || skybox)
return chasecam;
// use outside cam view
else if (!player->spectator && chasecam)
else if (chasecam)
return true;
// use the player's eyes view

View file

@ -39,6 +39,8 @@ extern size_t validcount, linecount, loopcount, framecount;
// The fraction of a tic being drawn (for interpolation between two tics)
extern fixed_t rendertimefrac;
// Same as rendertimefrac but not suspended when the game is paused
extern fixed_t rendertimefrac_unpaused;
// Evaluated delta tics for this frame (how many tics since the last frame)
extern fixed_t renderdeltatics;
// The current render is a new logical tic

View file

@ -22,7 +22,7 @@ INT32 R_ThingLightLevel(mobj_t* thing)
if (player)
{
if (player->instaShieldCooldown && (player->rings <= 0) && (leveltime & 1))
if (player->instaShieldCooldown && !player->whip && (player->rings <= 0) && (leveltime & 1))
{
// Darken on every other frame of instawhip cooldown
lightlevel -= 128;

View file

@ -540,11 +540,6 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume)
continue;
}
if (i == 0 && democam.soundmobj)
{
continue;
}
if (player->awayview.tics)
{
listenmobj[i] = player->awayview.mobj;
@ -554,7 +549,7 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume)
listenmobj[i] = player->mo;
}
if (origin && origin == listenmobj[i])
if (origin && origin == listenmobj[i] && !demo.freecam)
{
itsUs = true;
}
@ -827,11 +822,6 @@ void S_UpdateSounds(void)
continue;
}
if (i == 0 && democam.soundmobj)
{
continue;
}
if (player->awayview.tics)
{
listenmobj[i] = player->awayview.mobj;
@ -898,12 +888,15 @@ void S_UpdateSounds(void)
{
boolean itsUs = false;
for (i = r_splitscreen; i >= 0; i--)
if (!demo.freecam)
{
if (c->origin != listenmobj[i])
continue;
for (i = r_splitscreen; i >= 0; i--)
{
if (c->origin != listenmobj[i])
continue;
itsUs = true;
itsUs = true;
}
}
if (itsUs == false)

View file

@ -137,7 +137,7 @@ typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T);
#define UNIXBACKTRACE
#endif
// Locations for searching for main.kart
// Locations for searching for bios.pk3
#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
#define DEFAULTWADLOCATION1 "/usr/local/share/games/RingRacers"
#define DEFAULTWADLOCATION2 "/usr/local/games/RingRacers"
@ -150,7 +150,7 @@ typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T);
/** \brief WAD file to look for
*/
#define WADKEYWORD "main.kart"
#define WADKEYWORD "bios.pk3"
/** \brief holds wad path
*/
static char returnWadPath[256];
@ -2104,7 +2104,7 @@ static void pathonly(char *s)
}
}
/** \brief search for main.kart in the given path
/** \brief search for bios.pk3 in the given path
\param searchDir starting path
@ -2128,9 +2128,9 @@ static const char *searchWad(const char *searchDir)
return NULL;
}
/** \brief go through all possible paths and look for main.kart
/** \brief go through all possible paths and look for bios.pk3
\return path to main.kart if any
\return path to bios.pk3 if any
*/
static const char *locateWad(void)
@ -2260,7 +2260,7 @@ const char *I_LocateWad(void)
if (waddir)
{
// change to the directory where we found main.kart
// change to the directory where we found bios.pk3
#if defined (_WIN32)
SetCurrentDirectoryA(waddir);
#else

View file

@ -145,7 +145,7 @@ void __set_fpscr(long); // in libgcc / kernel's startup.s?
#define O_BINARY 0
#endif
// Locations for searching the main.kart
// Locations for searching the bios.pk3
#ifdef _arch_dreamcast
#define DEFAULTWADLOCATION1 "/cd"
#define DEFAULTWADLOCATION2 "/pc"
@ -217,7 +217,7 @@ void __set_fpscr(long); // in libgcc / kernel's startup.s?
/** \brief WAD file to look for
*/
#define WADKEYWORD "main.kart"
#define WADKEYWORD "bios.pk3"
/** \brief holds wad path
*/
static char returnWadPath[256];
@ -3436,7 +3436,7 @@ static void pathonly(char *s)
}
}
/** \brief search for main.kart in the given path
/** \brief search for bios.pk3 in the given path
\param searchDir starting path
@ -3460,9 +3460,9 @@ static const char *searchWad(const char *searchDir)
return NULL;
}
/** \brief go through all possible paths and look for main.kart
/** \brief go through all possible paths and look for bios.pk3
\return path to main.kart if any
\return path to bios.pk3 if any
*/
static const char *locateWad(void)
{
@ -3579,7 +3579,7 @@ const char *I_LocateWad(void)
if (waddir)
{
// change to the directory where we found main.kart
// change to the directory where we found bios.pk3
#if (defined (_WIN32) && !defined (_WIN32_WCE)) && !defined (_XBOX)
SetCurrentDirectoryA(waddir);
#elif !defined (_WIN32_WCE) && !defined (_PS3)

View file

@ -1171,7 +1171,7 @@ static void ST_overlayDrawer(void)
{
if (cv_showviewpointtext.value)
{
if (!demo.title && !P_IsLocalPlayer(stplyr))
if (!demo.title && !P_IsLocalPlayer(stplyr) && !demo.freecam)
{
if (!r_splitscreen)
{

View file

@ -134,6 +134,7 @@ TYPEDEF (unloaded_mapheader_t);
TYPEDEF (tolinfo_t);
TYPEDEF (cupheader_t);
TYPEDEF (unloaded_cupheader_t);
TYPEDEF (exitcondition_t);
// font.h
TYPEDEF (font_t);

View file

@ -71,6 +71,7 @@ static patch_t *widebgpatch = NULL;
static patch_t *bgtile = NULL; // SPECTILE/SRB2BACK
static patch_t *interpic = NULL; // custom picture defined in map header
#define INFINITE_TIMER (INT16_MAX) // just some arbitrarily large value that won't easily overflow
static INT32 timer;
static INT32 powertype = PWRLV_DISABLED;
@ -86,6 +87,16 @@ intertype_t intertype = int_none;
static huddrawlist_h luahuddrawlist_intermission;
static boolean Y_CanSkipIntermission(void)
{
if (!netgame)
{
return true;
}
return false;
}
static void Y_UnloadData(void);
//
@ -1533,6 +1544,17 @@ finalcounter:
}
}
if (Y_CanSkipIntermission())
{
K_drawButton(
2*FRACUNIT,
(BASEVIDHEIGHT - 16)*FRACUNIT,
0,
kp_button_a[1],
M_MenuConfirmHeld(0)
);
}
else
{
const INT32 tickDown = (timer + 1)/TICRATE;
@ -1578,6 +1600,46 @@ void Y_Ticker(void)
LUA_HOOK(IntermissionThinker);
if (Y_CanSkipIntermission())
{
if (M_MenuConfirmPressed(0))
{
// If there is a roundqueue, make time for it.
// Else, end instantly on button press.
// Actually, give it a slight delay, so the "kaching" sound isn't cut off.
const tic_t end = roundqueue.size != 0 ? 3*TICRATE : TICRATE;
if (intertic == -1) // card flip hasn't started
{
if (sorttic != -1)
{
intertic = sorttic;
}
else
{
intertic = 0;
timer = end;
}
}
else if (timer >= INFINITE_TIMER && intertic >= sorttic + 16) // card done flipping
{
const INT32 kaching = sorttic + 16 + (2*TICRATE);
if (intertic < kaching)
{
intertic = kaching; // kaching immediately
}
timer = end;
}
}
if (intertic == -1)
{
return;
}
}
intertic++;
// Team scramble code for team match and CTF.
@ -1590,7 +1652,7 @@ void Y_Ticker(void)
P_DoTeamscrambling();
}*/
if ((timer && !--timer)
if ((timer < INFINITE_TIMER && --timer <= 0)
|| (intertic == endtic))
{
Y_EndIntermission();
@ -1665,6 +1727,12 @@ void Y_Ticker(void)
r++;
data.jitter[data.num[q]] = 1;
// Player can skip the tally, kaching!
if (Y_CanSkipIntermission() && timer < INFINITE_TIMER)
{
data.increase[data.num[q]] = 0;
}
if (powertype != PWRLV_DISABLED)
{
// Power Levels
@ -1815,6 +1883,21 @@ void Y_StartIntermission(void)
sorttic = max((timer/2) - 2*TICRATE, 2*TICRATE);
}
// TODO: code's a mess, I'm just making it extra clear
// that this piece of code is supposed to take priority
// over the above. :)
if (Y_CanSkipIntermission())
{
timer = INFINITE_TIMER; // doesn't count down
if (sorttic != -1)
{
// Will start immediately, but must be triggered.
// Needs to be TICRATE to bypass a condition in Y_Ticker.
sorttic = TICRATE;
}
}
// We couldn't display the intermission even if we wanted to.
// But we still need to give the players their score bonuses, dummy.
//if (dedicated) return;