Merge branch 'fix-reset-interpolation' into 'master'
P_Ticker: update view interpolation at the start of a tic See merge request KartKrew/Kart!1192
This commit is contained in:
parent
43ecac1e8d
commit
f54ba922a9
6 changed files with 375 additions and 19 deletions
|
|
@ -1849,6 +1849,8 @@ void G_ResetView(UINT8 viewnum, INT32 playernum, boolean onlyactive)
|
|||
{
|
||||
camerap = &camera[viewnum-1];
|
||||
P_ResetCamera(&players[(*displayplayerp)], camerap);
|
||||
|
||||
R_ResetViewInterpolation(viewnum);
|
||||
}
|
||||
|
||||
if (viewnum > splits)
|
||||
|
|
|
|||
326
src/g_party.cpp
Normal file
326
src/g_party.cpp
Normal file
|
|
@ -0,0 +1,326 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2023 by James Robert Roman
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
|
||||
#include "core/static_vec.hpp"
|
||||
#include "cxxutil.hpp"
|
||||
|
||||
#include "d_clisrv.h" // playerconsole
|
||||
#include "doomdef.h" // MAXPLAYERS
|
||||
#include "doomstat.h" // consoleplayer
|
||||
#include "g_game.h" // localangle
|
||||
#include "g_party.h"
|
||||
#include "g_state.h"
|
||||
#include "p_local.h"
|
||||
#include "r_fps.h"
|
||||
#include "r_main.h" // R_ExecuteSetViewSize
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
using playernum_t = uint8_t;
|
||||
|
||||
class Party
|
||||
{
|
||||
public:
|
||||
class Console
|
||||
{
|
||||
public:
|
||||
// The Console class is basically analogous to
|
||||
// a playernum except local splitscreen players only
|
||||
// resolve to one playernum.
|
||||
//
|
||||
// Local splitscreen players are always joined with
|
||||
// each other, so this lets just one party to refer to
|
||||
// that group.
|
||||
|
||||
Console(playernum_t player)
|
||||
{
|
||||
SRB2_ASSERT(player >= 0 && player < MAXPLAYERS);
|
||||
|
||||
console_ = playerconsole[player];
|
||||
|
||||
SRB2_ASSERT(console_ >= 0 && console_ < MAXPLAYERS);
|
||||
}
|
||||
|
||||
operator playernum_t() const { return console_; }
|
||||
|
||||
private:
|
||||
playernum_t console_;
|
||||
};
|
||||
|
||||
//
|
||||
// Write Access Methods
|
||||
//
|
||||
|
||||
// Add a single player.
|
||||
void add(playernum_t player) { vec_.push_back(player); }
|
||||
|
||||
// Add every player from another party.
|
||||
void add(const Party& party) { std::copy(party.vec_.begin(), party.vec_.end(), std::back_inserter(vec_)); }
|
||||
|
||||
// Remove every player whose console is the same.
|
||||
void remove(Console console)
|
||||
{
|
||||
auto it = std::remove_if(vec_.begin(), vec_.end(), [console](Console other) { return other == console; });
|
||||
|
||||
while (it < vec_.end())
|
||||
{
|
||||
vec_.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Read Access Methods
|
||||
//
|
||||
|
||||
std::size_t size() const { return vec_.size(); }
|
||||
|
||||
// The player at this position in the party.
|
||||
playernum_t at(std::size_t i) const { return vec_[i]; }
|
||||
playernum_t operator[](std::size_t i) const { return at(i); }
|
||||
|
||||
// C array access to the raw player numbers.
|
||||
const playernum_t* data() const { return &vec_[0]; }
|
||||
|
||||
// True if the player is a member of this party.
|
||||
bool contains(playernum_t player) const { return std::find(vec_.begin(), vec_.end(), player) != vec_.end(); }
|
||||
|
||||
// True if the consoleplayer is a member of this party.
|
||||
bool local() const
|
||||
{
|
||||
// consoleplayer is not valid yet.
|
||||
if (!addedtogame)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return contains(consoleplayer);
|
||||
}
|
||||
|
||||
// Returns a party composed of only the unique consoles
|
||||
// from this party.
|
||||
Party consoles() const
|
||||
{
|
||||
Party party;
|
||||
|
||||
std::unique_copy(vec_.begin(), vec_.end(), std::back_inserter(party.vec_), std::equal_to<Console>());
|
||||
|
||||
return party;
|
||||
}
|
||||
|
||||
// If the party is local, set the correct viewports.
|
||||
void rebuild_displayplayers() const
|
||||
{
|
||||
if (!local())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Rendering stuff is not valid outside of levels.
|
||||
if (!G_GamestateUsesLevel())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < size(); ++i)
|
||||
{
|
||||
const playernum_t player = at(i);
|
||||
|
||||
displayplayers[i] = player;
|
||||
|
||||
// The order of displayplayers can change, which
|
||||
// would make localangle invalid now.
|
||||
localangle[i] = players[player].angleturn;
|
||||
|
||||
P_ResetCamera(&players[player], &camera[i]);
|
||||
|
||||
// Make sure the viewport doesn't interpolate at
|
||||
// all into its new position -- just snap
|
||||
// instantly into place.
|
||||
R_ResetViewInterpolation(1 + i);
|
||||
}
|
||||
|
||||
r_splitscreen = size() - 1;
|
||||
|
||||
R_ExecuteSetViewSize(); // present viewport
|
||||
}
|
||||
|
||||
//
|
||||
// Iterators
|
||||
//
|
||||
|
||||
// Returns an iterator to the player within this party if
|
||||
// they are a member. Else returns the end() iterator.
|
||||
auto find(playernum_t player) const
|
||||
{
|
||||
return std::find(vec_.begin(), vec_.end(), player);
|
||||
}
|
||||
|
||||
// Iterator to the beginning of the party.
|
||||
auto begin() const { return vec_.begin(); }
|
||||
|
||||
// Iterator to the end of the party.
|
||||
auto end() const { return vec_.end(); }
|
||||
|
||||
private:
|
||||
srb2::StaticVec<playernum_t, MAXSPLITSCREENPLAYERS> vec_;
|
||||
};
|
||||
|
||||
class PartyManager
|
||||
{
|
||||
public:
|
||||
// To avoid copying the same party to each local
|
||||
// splitscreen player, all lookups will use the
|
||||
// consoleplayer.
|
||||
Party& operator [](Party::Console console) { return pool_[console]; }
|
||||
|
||||
protected:
|
||||
std::array<Party, MAXPLAYERS> pool_;
|
||||
}
|
||||
local_party;
|
||||
|
||||
class FinalPartyManager : public PartyManager
|
||||
{
|
||||
public:
|
||||
// Adds guest's entire local splitscreen party to the
|
||||
// host's party. If the operation succeeds, host and guest
|
||||
// parties are guaranteed to be identical and the
|
||||
// viewports are updated for every player involved.
|
||||
bool join(Party::Console host, Party::Console guest)
|
||||
{
|
||||
Party &party = pool_[host];
|
||||
|
||||
// Already in the same party.
|
||||
if (party.contains(guest))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parties do not fit when merged.
|
||||
if (party.size() + local_party[guest].size() > MAXSPLITSCREENPLAYERS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the host party includes players from a local
|
||||
// party, iterating the unique consoles avoids
|
||||
// duplicate insertions of the guest.
|
||||
for (Party::Console other : party.consoles())
|
||||
{
|
||||
pool_[other].add(local_party[guest]);
|
||||
}
|
||||
|
||||
reset(guest, party); // assign new party to guest
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Removes a player from another party and assigns a new
|
||||
// party. Viewports are updated for all players involved.
|
||||
void reset(Party::Console player, const Party &party)
|
||||
{
|
||||
SRB2_ASSERT(party.size() > 0);
|
||||
|
||||
remove(player);
|
||||
|
||||
pool_[player] = party;
|
||||
|
||||
party.rebuild_displayplayers();
|
||||
}
|
||||
|
||||
private:
|
||||
// Removes a player from every party they're in. Updates
|
||||
// viewports for the players left behind.
|
||||
void remove(Party::Console player)
|
||||
{
|
||||
Party &party = pool_[player];
|
||||
|
||||
// Iterate a COPY of party because this very party
|
||||
// will be modified.
|
||||
for (Party::Console member : Party(party))
|
||||
{
|
||||
pool_[member].remove(player);
|
||||
}
|
||||
|
||||
party.rebuild_displayplayers(); // restore viewports for left behind party
|
||||
}
|
||||
}
|
||||
final_party;
|
||||
|
||||
}; // namespace
|
||||
|
||||
INT32 splitscreen_invitations[MAXPLAYERS];
|
||||
|
||||
void G_ObliterateParties(void)
|
||||
{
|
||||
final_party = {};
|
||||
local_party = {};
|
||||
}
|
||||
|
||||
void G_DestroyParty(UINT8 player)
|
||||
{
|
||||
local_party[player] = {};
|
||||
final_party[player] = {};
|
||||
}
|
||||
|
||||
void G_BuildLocalSplitscreenParty(UINT8 player)
|
||||
{
|
||||
local_party[player].add(player);
|
||||
final_party[player] = local_party[player];
|
||||
}
|
||||
|
||||
void G_JoinParty(UINT8 host, UINT8 guest)
|
||||
{
|
||||
final_party.join(host, guest);
|
||||
}
|
||||
|
||||
void G_LeaveParty(UINT8 player)
|
||||
{
|
||||
final_party.reset(player, local_party[player]);
|
||||
}
|
||||
|
||||
UINT8 G_LocalSplitscreenPartySize(UINT8 player)
|
||||
{
|
||||
return local_party[player].size();
|
||||
}
|
||||
|
||||
UINT8 G_PartySize(UINT8 player)
|
||||
{
|
||||
return final_party[player].size();
|
||||
}
|
||||
|
||||
boolean G_IsPartyLocal(UINT8 player)
|
||||
{
|
||||
return final_party[player].local();
|
||||
}
|
||||
|
||||
UINT8 G_PartyMember(UINT8 player, UINT8 index)
|
||||
{
|
||||
SRB2_ASSERT(index < final_party[player].size());
|
||||
|
||||
return final_party[player][index];
|
||||
}
|
||||
|
||||
const UINT8* G_PartyArray(UINT8 player)
|
||||
{
|
||||
return final_party[player].data();
|
||||
}
|
||||
|
||||
UINT8 G_PartyPosition(UINT8 player)
|
||||
{
|
||||
const Party& party = final_party[player];
|
||||
|
||||
return party.find(player) - party.begin();
|
||||
}
|
||||
|
|
@ -8097,28 +8097,29 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
|
|||
P_MapEnd(); // just in case MapLoad modifies tmthing
|
||||
}
|
||||
|
||||
// No render mode or reloading gamestate, stop here.
|
||||
if (rendermode == render_none || reloadinggamestate)
|
||||
return true;
|
||||
|
||||
R_ResetViewInterpolation(0);
|
||||
R_ResetViewInterpolation(0);
|
||||
R_UpdateMobjInterpolators();
|
||||
if (rendermode != render_none && reloadinggamestate == false)
|
||||
{
|
||||
|
||||
// Title card!
|
||||
G_StartTitleCard();
|
||||
R_ResetViewInterpolation(0);
|
||||
R_ResetViewInterpolation(0);
|
||||
R_UpdateMobjInterpolators();
|
||||
|
||||
// Can the title card actually run, though?
|
||||
if (!WipeStageTitle)
|
||||
return true;
|
||||
if (ranspecialwipe == 2)
|
||||
return true;
|
||||
// Title card!
|
||||
G_StartTitleCard();
|
||||
|
||||
// If so...
|
||||
// but not if joining because the fade may time us out
|
||||
if (!fromnetsave)
|
||||
G_PreLevelTitleCard();
|
||||
// Can the title card actually run, though?
|
||||
if (!WipeStageTitle)
|
||||
return true;
|
||||
if (ranspecialwipe == 2)
|
||||
return true;
|
||||
|
||||
// If so...
|
||||
// but not if joining because the fade may time us out
|
||||
if (!fromnetsave)
|
||||
G_PreLevelTitleCard();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include "r_state.h"
|
||||
#include "s_sound.h"
|
||||
#include "r_main.h"
|
||||
#include "r_fps.h"
|
||||
|
||||
/** \brief The P_MixUp function
|
||||
|
||||
|
|
|
|||
29
src/p_tick.c
29
src/p_tick.c
|
|
@ -24,7 +24,9 @@
|
|||
#include "lua_hook.h"
|
||||
#include "m_perfstats.h"
|
||||
#include "i_system.h" // I_GetPreciseTime
|
||||
#include "i_video.h"
|
||||
#include "r_fps.h"
|
||||
#include "r_main.h"
|
||||
|
||||
// Object place
|
||||
#include "m_cheat.h"
|
||||
|
|
@ -555,6 +557,13 @@ void P_Ticker(boolean run)
|
|||
players[i].jointime++;
|
||||
}
|
||||
|
||||
if (run)
|
||||
{
|
||||
// Update old view state BEFORE ticking so resetting
|
||||
// the old interpolation state from game logic works.
|
||||
R_UpdateViewInterpolation();
|
||||
}
|
||||
|
||||
if (objectplacing)
|
||||
{
|
||||
if (OP_FreezeObjectplace())
|
||||
|
|
@ -781,7 +790,25 @@ void P_Ticker(boolean run)
|
|||
if (run)
|
||||
{
|
||||
R_UpdateLevelInterpolators();
|
||||
R_UpdateViewInterpolation();
|
||||
|
||||
// Hack: ensure newview is assigned every tic.
|
||||
// Ensures view interpolation is T-1 to T in poor network conditions
|
||||
// We need a better way to assign view state decoupled from game logic
|
||||
if (rendermode != render_none)
|
||||
{
|
||||
for (i = 0; i <= r_splitscreen; i++)
|
||||
{
|
||||
player_t *player = &players[displayplayers[i]];
|
||||
if (!player->mo)
|
||||
continue;
|
||||
const boolean skybox = (player->skybox.viewpoint && cv_skybox.value); // True if there's a skybox object and skyboxes are on
|
||||
if (skybox)
|
||||
{
|
||||
R_SkyboxFrame(i);
|
||||
}
|
||||
R_SetupFrame(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
P_MapEnd();
|
||||
|
|
|
|||
|
|
@ -3628,7 +3628,6 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
|
|||
{
|
||||
P_MoveChaseCamera(player, thiscam, false);
|
||||
R_ResetViewInterpolation(num + 1);
|
||||
R_ResetViewInterpolation(num + 1);
|
||||
}
|
||||
|
||||
return (x == thiscam->x && y == thiscam->y && z == thiscam->z && angle == thiscam->aiming);
|
||||
|
|
|
|||
Loading…
Reference in a new issue