diff --git a/src/d_clisrv.c b/src/d_clisrv.c index eb493d55e..990f8401a 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -561,6 +561,11 @@ void D_ResetTiccmds(void) D_Clearticcmd(textcmds[i]->tic); } +ticcmd_t *D_LocalTiccmd(UINT8 ss) +{ + return &localcmds[ss][0]; +} + void SendKick(UINT8 playernum, UINT8 msg) { UINT8 buf[2]; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 1d8c339e3..79d5c16cf 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -615,7 +615,9 @@ extern UINT8 playerconsole[MAXPLAYERS]; INT32 D_NumPlayers(void); boolean D_IsPlayerHumanAndGaming(INT32 player_number); + void D_ResetTiccmds(void); +ticcmd_t *D_LocalTiccmd(UINT8 ss); tic_t GetLag(INT32 node); UINT8 GetFreeXCmdSize(UINT8 playerid); diff --git a/src/d_main.c b/src/d_main.c index cade52936..bc042c57c 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -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) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 68abb753f..f49647810 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -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); diff --git a/src/g_build_ticcmd.cpp b/src/g_build_ticcmd.cpp index 035bdc08a..1057595b9 100644 --- a/src/g_build_ticcmd.cpp +++ b/src/g_build_ticcmd.cpp @@ -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; diff --git a/src/g_demo.c b/src/g_demo.c index d7f240f38..7f58d289e 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -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; diff --git a/src/g_game.c b/src/g_game.c index 550324341..08fc60159 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -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; @@ -861,7 +865,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; @@ -871,6 +875,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; @@ -3289,6 +3318,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)); } diff --git a/src/g_game.h b/src/g_game.h index 71dd4ada8..72e9ce005 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -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 diff --git a/src/k_battle.c b/src/k_battle.c index 66e98561c..837f173b4 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -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; } } diff --git a/src/k_hud.c b/src/k_hud.c index 0d928db0c..569304485 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -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(); } diff --git a/src/menus/transient/pause-replay.c b/src/menus/transient/pause-replay.c index 52cc4b7e5..68b4859dc 100644 --- a/src/menus/transient/pause-replay.c +++ b/src/menus/transient/pause-replay.c @@ -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) diff --git a/src/p_inter.c b/src/p_inter.c index fb2a7a071..918c3faca 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1998,7 +1998,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) diff --git a/src/p_local.h b/src/p_local.h index 20ad1d5d2..a09caf896 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -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); diff --git a/src/p_mobj.c b/src/p_mobj.c index 47d20059b..ab0d25e5e 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11841,7 +11841,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 } } @@ -11969,6 +11972,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) @@ -12002,12 +12014,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]); + } } } diff --git a/src/p_setup.c b/src/p_setup.c index 3f9bfcd2e..48caf293a 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7735,6 +7735,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) diff --git a/src/p_tick.c b/src/p_tick.c index b6b470ce5..8e408e92b 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -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; } diff --git a/src/p_user.c b/src/p_user.c index d2f148283..df58dc55f 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -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]]) @@ -2350,45 +2359,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 +2385,6 @@ void P_MovePlayer(player_t *player) if (player->spectator) { player->mo->eflags &= ~MFE_VERTICALFLIP; // deflip... - P_SpectatorMovement(player); return; } @@ -2970,184 +2939,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 +3028,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 +3093,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 +3123,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 +3131,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; diff --git a/src/r_fps.c b/src/r_fps.c index 8f870abf2..f21d38943 100644 --- a/src/r_fps.c +++ b/src/r_fps.c @@ -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)<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 diff --git a/src/r_main.h b/src/r_main.h index dcd496cfb..0f5560308 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -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 diff --git a/src/s_sound.c b/src/s_sound.c index a9e5a4656..eb6c9a2e1 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -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) diff --git a/src/st_stuff.c b/src/st_stuff.c index dc4133089..c7166c414 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -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) {