mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Let Director operate on separate splitscreens
This commit is contained in:
parent
412b37a8de
commit
49898abfeb
5 changed files with 281 additions and 240 deletions
|
|
@ -253,13 +253,13 @@ class TiccmdBuilder
|
||||||
if (M_MenuButtonPressed(pid, MBT_A))
|
if (M_MenuButtonPressed(pid, MBT_A))
|
||||||
{
|
{
|
||||||
G_AdjustView(ssplayer, 1, true);
|
G_AdjustView(ssplayer, 1, true);
|
||||||
K_ToggleDirector(false);
|
K_ToggleDirector(forplayer(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (M_MenuButtonPressed(pid, MBT_X))
|
if (M_MenuButtonPressed(pid, MBT_X))
|
||||||
{
|
{
|
||||||
G_AdjustView(ssplayer, -1, true);
|
G_AdjustView(ssplayer, -1, true);
|
||||||
K_ToggleDirector(false);
|
K_ToggleDirector(forplayer(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player()->spectator == true)
|
if (player()->spectator == true)
|
||||||
|
|
@ -272,7 +272,7 @@ class TiccmdBuilder
|
||||||
|
|
||||||
if (M_MenuButtonPressed(pid, MBT_R))
|
if (M_MenuButtonPressed(pid, MBT_R))
|
||||||
{
|
{
|
||||||
K_ToggleDirector(true);
|
K_ToggleDirector(forplayer(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,54 +4,30 @@
|
||||||
/// \brief SRB2kart automatic spectator camera.
|
/// \brief SRB2kart automatic spectator camera.
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include "cxxutil.hpp"
|
||||||
|
|
||||||
#include "k_kart.h"
|
#include "k_kart.h"
|
||||||
#include "k_respawn.h"
|
#include "k_respawn.h"
|
||||||
#include "doomdef.h"
|
#include "doomdef.h"
|
||||||
|
#include "doomstat.h"
|
||||||
#include "g_game.h"
|
#include "g_game.h"
|
||||||
#include "v_video.h"
|
#include "v_video.h"
|
||||||
#include "k_director.h"
|
#include "k_director.h"
|
||||||
#include "d_netcmd.h"
|
#include "d_netcmd.h"
|
||||||
#include "p_local.h"
|
#include "p_local.h"
|
||||||
#include "g_party.h"
|
#include "g_party.h"
|
||||||
|
#include "command.h"
|
||||||
|
|
||||||
#define SWITCHTIME TICRATE * 5 // cooldown between unforced switches
|
extern "C" consvar_t cv_devmode_screen;
|
||||||
#define BOREDOMTIME 3 * TICRATE / 2 // how long until players considered far apart?
|
|
||||||
#define TRANSFERTIME TICRATE // how long to delay reaction shots?
|
|
||||||
#define BREAKAWAYDIST 4000 // how *far* until players considered far apart?
|
|
||||||
#define WALKBACKDIST 600 // how close should a trailing player be before we switch?
|
|
||||||
#define PINCHDIST 30000 // how close should the leader be to be considered "end of race"?
|
|
||||||
|
|
||||||
struct directorinfo
|
#define SWITCHTIME TICRATE * 5 // cooldown between unforced switches
|
||||||
{
|
#define BOREDOMTIME 3 * TICRATE / 2 // how long until players considered far apart?
|
||||||
boolean active; // is view point switching enabled?
|
#define TRANSFERTIME TICRATE // how long to delay reaction shots?
|
||||||
tic_t cooldown; // how long has it been since we last switched?
|
#define BREAKAWAYDIST 4000 // how *far* until players considered far apart?
|
||||||
tic_t freeze; // when nonzero, fixed switch pending, freeze logic!
|
#define WALKBACKDIST 600 // how close should a trailing player be before we switch?
|
||||||
INT32 attacker; // who to switch to when freeze delay elapses
|
#define PINCHDIST 30000 // how close should the leader be to be considered "end of race"?
|
||||||
INT32 maxdist; // how far is the closest player from finishing?
|
|
||||||
|
|
||||||
INT32 sortedplayers[MAXPLAYERS]; // position-1 goes in, player index comes out.
|
|
||||||
INT32 gap[MAXPLAYERS]; // gap between a given position and their closest pursuer
|
|
||||||
INT32 boredom[MAXPLAYERS]; // how long has a given position had no credible attackers?
|
|
||||||
} directorinfo;
|
|
||||||
|
|
||||||
void K_InitDirector(void)
|
|
||||||
{
|
|
||||||
INT32 playernum;
|
|
||||||
|
|
||||||
directorinfo.active = false;
|
|
||||||
directorinfo.cooldown = SWITCHTIME;
|
|
||||||
directorinfo.freeze = 0;
|
|
||||||
directorinfo.attacker = 0;
|
|
||||||
directorinfo.maxdist = 0;
|
|
||||||
|
|
||||||
for (playernum = 0; playernum < MAXPLAYERS; playernum++)
|
|
||||||
{
|
|
||||||
directorinfo.sortedplayers[playernum] = -1;
|
|
||||||
directorinfo.gap[playernum] = INT32_MAX;
|
|
||||||
directorinfo.boredom[playernum] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static fixed_t K_GetFinishGap(INT32 leader, INT32 follower)
|
static fixed_t K_GetFinishGap(INT32 leader, INT32 follower)
|
||||||
{
|
{
|
||||||
|
|
@ -68,116 +44,257 @@ static fixed_t K_GetFinishGap(INT32 leader, INT32 follower)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void K_UpdateDirectorPositions(void)
|
struct DirectorInfo
|
||||||
{
|
{
|
||||||
INT32 playernum;
|
UINT8 viewnum; // which screen does this director apply to?
|
||||||
INT32 position;
|
boolean active = false; // is view point switching enabled?
|
||||||
player_t* target;
|
tic_t cooldown = SWITCHTIME; // how long has it been since we last switched?
|
||||||
|
tic_t freeze = 0; // when nonzero, fixed switch pending, freeze logic!
|
||||||
|
INT32 attacker = 0; // who to switch to when freeze delay elapses
|
||||||
|
INT32 maxdist = 0; // how far is the closest player from finishing?
|
||||||
|
|
||||||
memset(directorinfo.sortedplayers, -1, sizeof(directorinfo.sortedplayers));
|
struct PlayerStat
|
||||||
|
|
||||||
for (playernum = 0; playernum < MAXPLAYERS; playernum++)
|
|
||||||
{
|
{
|
||||||
target = &players[playernum];
|
INT32 sorted = -1; // position-1 goes in, player index comes out.
|
||||||
|
INT32 gap = INT32_MAX; // gap between a given position and their closest pursuer
|
||||||
|
INT32 boredom = 0; // how long has a given position had no credible attackers?
|
||||||
|
}
|
||||||
|
playerstat[MAXPLAYERS];
|
||||||
|
|
||||||
if (playeringame[playernum] && !target->spectator && target->position > 0)
|
DirectorInfo(UINT8 viewnum_) : viewnum(viewnum_) {}
|
||||||
|
|
||||||
|
INT32 viewplayernum() const { return displayplayers[viewnum]; }
|
||||||
|
player_t* viewplayer() const { return &players[viewplayernum()]; }
|
||||||
|
|
||||||
|
void update()
|
||||||
|
{
|
||||||
|
INT32 targetposition;
|
||||||
|
|
||||||
|
update_positions();
|
||||||
|
|
||||||
|
if (cooldown > 0) {
|
||||||
|
cooldown--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle pending forced switches
|
||||||
|
if (freeze > 0)
|
||||||
{
|
{
|
||||||
directorinfo.sortedplayers[target->position - 1] = playernum;
|
if (!(--freeze))
|
||||||
|
change(attacker, true);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there's only one player left in the list, just switch to that player
|
||||||
|
if (playerstat[0].sorted != -1 && playerstat[1].sorted == -1)
|
||||||
|
{
|
||||||
|
change(playerstat[0].sorted, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// aaight, time to walk through the standings to find the first interesting pair
|
||||||
|
// NB: targetposition/PlayerStat::sorted is 0-indexed, aiming at the "back half" of a given pair by default.
|
||||||
|
// we adjust for this when comparing to player->position or when looking at the leading player, Don't Freak Out
|
||||||
|
for (targetposition = 1; targetposition < MAXPLAYERS; targetposition++)
|
||||||
|
{
|
||||||
|
INT32 target;
|
||||||
|
|
||||||
|
// you are out of players, try again
|
||||||
|
if (playerstat[targetposition].sorted == -1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pair too far apart? try the next one
|
||||||
|
if (playerstat[targetposition - 1].boredom >= BOREDOMTIME)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pair finished? try the next one
|
||||||
|
if (players[playerstat[targetposition].sorted].exiting)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't risk switching away from forward pairs at race end, might miss something!
|
||||||
|
if (maxdist > PINCHDIST)
|
||||||
|
{
|
||||||
|
// if the "next" player is close enough, they should be able to see everyone fine!
|
||||||
|
// walk back through the standings to find a vantage that gets everyone in frame.
|
||||||
|
// (also creates a pretty cool effect w/ overtakes at speed)
|
||||||
|
while (targetposition < MAXPLAYERS && playerstat[targetposition].gap < WALKBACKDIST)
|
||||||
|
{
|
||||||
|
targetposition++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
target = playerstat[targetposition].sorted;
|
||||||
|
|
||||||
|
// stop here since we're already viewing this player
|
||||||
|
if (viewplayernum() == target)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this is a splitscreen player, try next pair
|
||||||
|
if (P_IsDisplayPlayer(&players[target]))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we're certain the back half of the pair is actually in this position, try to switch
|
||||||
|
if (!players[target].positiondelay)
|
||||||
|
{
|
||||||
|
change(target, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// even if we're not certain, if we're certain we're watching the WRONG player, try to switch
|
||||||
|
if (viewplayer()->position != targetposition+1 && !viewplayer()->positiondelay)
|
||||||
|
{
|
||||||
|
change(target, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (position = 0; position < MAXPLAYERS - 1; position++)
|
void force_switch(INT32 player, INT32 time)
|
||||||
{
|
{
|
||||||
directorinfo.gap[position] = INT32_MAX;
|
if (players[player].exiting)
|
||||||
|
|
||||||
if (directorinfo.sortedplayers[position] == -1 || directorinfo.sortedplayers[position + 1] == -1)
|
|
||||||
{
|
{
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
directorinfo.gap[position] = P_ScaleFromMap(K_GetFinishGap(directorinfo.sortedplayers[position], directorinfo.sortedplayers[position + 1]), FRACUNIT);
|
attacker = player;
|
||||||
|
freeze = time;
|
||||||
if (directorinfo.gap[position] >= BREAKAWAYDIST)
|
|
||||||
{
|
|
||||||
directorinfo.boredom[position] = std::min(BOREDOMTIME * 2, directorinfo.boredom[position] + 1);
|
|
||||||
}
|
|
||||||
else if (directorinfo.boredom[position] > 0)
|
|
||||||
{
|
|
||||||
directorinfo.boredom[position]--;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
directorinfo.maxdist = P_ScaleFromMap(players[directorinfo.sortedplayers[0]].distancetofinish, FRACUNIT);
|
private:
|
||||||
|
void update_positions()
|
||||||
|
{
|
||||||
|
INT32 playernum;
|
||||||
|
INT32 position;
|
||||||
|
player_t* target;
|
||||||
|
|
||||||
|
for (PlayerStat& stat : playerstat)
|
||||||
|
{
|
||||||
|
stat.sorted = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (playernum = 0; playernum < MAXPLAYERS; playernum++)
|
||||||
|
{
|
||||||
|
target = &players[playernum];
|
||||||
|
|
||||||
|
if (playeringame[playernum] && !target->spectator && target->position > 0)
|
||||||
|
{
|
||||||
|
playerstat[target->position - 1].sorted = playernum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (position = 0; position < MAXPLAYERS - 1; position++)
|
||||||
|
{
|
||||||
|
playerstat[position].gap = INT32_MAX;
|
||||||
|
|
||||||
|
if (playerstat[position].sorted == -1 || playerstat[position + 1].sorted == -1)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
playerstat[position].gap = P_ScaleFromMap(
|
||||||
|
K_GetFinishGap(playerstat[position].sorted, playerstat[position + 1].sorted),
|
||||||
|
FRACUNIT
|
||||||
|
);
|
||||||
|
|
||||||
|
if (playerstat[position].gap >= BREAKAWAYDIST)
|
||||||
|
{
|
||||||
|
playerstat[position].boredom = std::min(BOREDOMTIME * 2, playerstat[position].boredom + 1);
|
||||||
|
}
|
||||||
|
else if (playerstat[position].boredom > 0)
|
||||||
|
{
|
||||||
|
playerstat[position].boredom--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxdist = P_ScaleFromMap(players[playerstat[0].sorted].distancetofinish, FRACUNIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool can_change() const
|
||||||
|
{
|
||||||
|
if (viewplayer()->trickpanel > 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cooldown > 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void change(INT32 player, boolean force)
|
||||||
|
{
|
||||||
|
if (!active)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (players[player].exiting)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!force && !can_change())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_ResetView(1 + viewnum, player, true);
|
||||||
|
cooldown = SWITCHTIME;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DirectorInfoManager
|
||||||
|
{
|
||||||
|
DirectorInfo& operator [](UINT8 viewnum)
|
||||||
|
{
|
||||||
|
SRB2_ASSERT(viewnum < MAXSPLITSCREENPLAYERS);
|
||||||
|
return info_[viewnum];
|
||||||
|
}
|
||||||
|
|
||||||
|
auto begin() { return info_.begin(); }
|
||||||
|
auto end() { return std::next(begin(), r_splitscreen + 1); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static_assert(MAXSPLITSCREENPLAYERS == 4);
|
||||||
|
std::array<DirectorInfo, MAXSPLITSCREENPLAYERS> info_{0, 1, 2, 3};
|
||||||
}
|
}
|
||||||
|
g_directorinfo;
|
||||||
|
|
||||||
static boolean K_CanSwitchDirector(void)
|
void K_InitDirector(void)
|
||||||
{
|
{
|
||||||
INT32 *displayplayerp = &displayplayers[0];
|
g_directorinfo = {};
|
||||||
|
|
||||||
if (players[*displayplayerp].trickpanel > 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (directorinfo.cooldown > 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void K_DirectorSwitch(INT32 player, boolean force)
|
|
||||||
{
|
|
||||||
if (!directorinfo.active)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (P_IsDisplayPlayer(&players[player]))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (players[player].exiting)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!force && !K_CanSwitchDirector())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
G_ResetView(1, player, true);
|
|
||||||
directorinfo.cooldown = SWITCHTIME;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void K_DirectorForceSwitch(INT32 player, INT32 time)
|
|
||||||
{
|
|
||||||
if (players[player].exiting)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
directorinfo.attacker = player;
|
|
||||||
directorinfo.freeze = time;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source)
|
void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source)
|
||||||
{
|
{
|
||||||
if (!P_IsDisplayPlayer(player))
|
for (DirectorInfo& directorinfo : g_directorinfo)
|
||||||
{
|
{
|
||||||
return;
|
if (directorinfo.viewplayer() != player)
|
||||||
}
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (inflictor && inflictor->player)
|
if (inflictor && inflictor->player)
|
||||||
{
|
{
|
||||||
K_DirectorForceSwitch(inflictor->player - players, TRANSFERTIME);
|
directorinfo.force_switch(inflictor->player - players, TRANSFERTIME);
|
||||||
}
|
}
|
||||||
else if (source && source->player)
|
else if (source && source->player)
|
||||||
{
|
{
|
||||||
K_DirectorForceSwitch(source->player - players, TRANSFERTIME);
|
directorinfo.force_switch(source->player - players, TRANSFERTIME);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,6 +310,8 @@ void K_DrawDirectorDebugger(void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DirectorInfo& directorinfo = g_directorinfo[cv_devmode_screen.value];
|
||||||
|
|
||||||
V_DrawThinString(10, 0, V_70TRANS, va("PLACE"));
|
V_DrawThinString(10, 0, V_70TRANS, va("PLACE"));
|
||||||
V_DrawThinString(40, 0, V_70TRANS, va("CONF?"));
|
V_DrawThinString(40, 0, V_70TRANS, va("CONF?"));
|
||||||
V_DrawThinString(80, 0, V_70TRANS, va("GAP"));
|
V_DrawThinString(80, 0, V_70TRANS, va("GAP"));
|
||||||
|
|
@ -203,8 +322,8 @@ void K_DrawDirectorDebugger(void)
|
||||||
for (position = 0; position < MAXPLAYERS - 1; position++)
|
for (position = 0; position < MAXPLAYERS - 1; position++)
|
||||||
{
|
{
|
||||||
ytxt = 10 * (position + 1);
|
ytxt = 10 * (position + 1);
|
||||||
leader = directorinfo.sortedplayers[position];
|
leader = directorinfo.playerstat[position].sorted;
|
||||||
follower = directorinfo.sortedplayers[position + 1];
|
follower = directorinfo.playerstat[position + 1].sorted;
|
||||||
|
|
||||||
if (leader == -1 || follower == -1)
|
if (leader == -1 || follower == -1)
|
||||||
break;
|
break;
|
||||||
|
|
@ -217,15 +336,15 @@ void K_DrawDirectorDebugger(void)
|
||||||
V_DrawThinString(40, ytxt, V_70TRANS, va("NG"));
|
V_DrawThinString(40, ytxt, V_70TRANS, va("NG"));
|
||||||
}
|
}
|
||||||
|
|
||||||
V_DrawThinString(80, ytxt, V_70TRANS, va("%d", directorinfo.gap[position]));
|
V_DrawThinString(80, ytxt, V_70TRANS, va("%d", directorinfo.playerstat[position].gap));
|
||||||
|
|
||||||
if (directorinfo.boredom[position] >= BOREDOMTIME)
|
if (directorinfo.playerstat[position].boredom >= BOREDOMTIME)
|
||||||
{
|
{
|
||||||
V_DrawThinString(120, ytxt, V_70TRANS, va("BORED"));
|
V_DrawThinString(120, ytxt, V_70TRANS, va("BORED"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
V_DrawThinString(120, ytxt, V_70TRANS, va("%d", directorinfo.boredom[position]));
|
V_DrawThinString(120, ytxt, V_70TRANS, va("%d", directorinfo.playerstat[position].boredom));
|
||||||
}
|
}
|
||||||
|
|
||||||
V_DrawThinString(150, ytxt, V_70TRANS, va("%s", player_names[leader]));
|
V_DrawThinString(150, ytxt, V_70TRANS, va("%s", player_names[leader]));
|
||||||
|
|
@ -235,100 +354,16 @@ void K_DrawDirectorDebugger(void)
|
||||||
|
|
||||||
void K_UpdateDirector(void)
|
void K_UpdateDirector(void)
|
||||||
{
|
{
|
||||||
INT32 *displayplayerp = &displayplayers[0];
|
for (DirectorInfo& directorinfo : g_directorinfo)
|
||||||
INT32 targetposition;
|
|
||||||
|
|
||||||
K_UpdateDirectorPositions();
|
|
||||||
|
|
||||||
if (directorinfo.cooldown > 0) {
|
|
||||||
directorinfo.cooldown--;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle pending forced switches
|
|
||||||
if (directorinfo.freeze > 0)
|
|
||||||
{
|
{
|
||||||
if (!(--directorinfo.freeze))
|
directorinfo.update();
|
||||||
K_DirectorSwitch(directorinfo.attacker, true);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there's only one player left in the list, just switch to that player
|
|
||||||
if (directorinfo.sortedplayers[0] != -1 && directorinfo.sortedplayers[1] == -1)
|
|
||||||
{
|
|
||||||
K_DirectorSwitch(directorinfo.sortedplayers[0], false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// aaight, time to walk through the standings to find the first interesting pair
|
|
||||||
// NB: targetposition/sortedplayers is 0-indexed, aiming at the "back half" of a given pair by default.
|
|
||||||
// we adjust for this when comparing to player->position or when looking at the leading player, Don't Freak Out
|
|
||||||
for (targetposition = 1; targetposition < MAXPLAYERS; targetposition++)
|
|
||||||
{
|
|
||||||
INT32 target;
|
|
||||||
|
|
||||||
// you are out of players, try again
|
|
||||||
if (directorinfo.sortedplayers[targetposition] == -1)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// pair too far apart? try the next one
|
|
||||||
if (directorinfo.boredom[targetposition - 1] >= BOREDOMTIME)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// pair finished? try the next one
|
|
||||||
if (players[directorinfo.sortedplayers[targetposition]].exiting)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't risk switching away from forward pairs at race end, might miss something!
|
|
||||||
if (directorinfo.maxdist > PINCHDIST)
|
|
||||||
{
|
|
||||||
// if the "next" player is close enough, they should be able to see everyone fine!
|
|
||||||
// walk back through the standings to find a vantage that gets everyone in frame.
|
|
||||||
// (also creates a pretty cool effect w/ overtakes at speed)
|
|
||||||
while (targetposition < MAXPLAYERS && directorinfo.gap[targetposition] < WALKBACKDIST)
|
|
||||||
{
|
|
||||||
targetposition++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
target = directorinfo.sortedplayers[targetposition];
|
|
||||||
|
|
||||||
// stop here since we're already viewing this player
|
|
||||||
if (*displayplayerp == target)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if this is a splitscreen player, try next pair
|
|
||||||
if (P_IsDisplayPlayer(&players[target]))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we're certain the back half of the pair is actually in this position, try to switch
|
|
||||||
if (!players[target].positiondelay)
|
|
||||||
{
|
|
||||||
K_DirectorSwitch(target, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// even if we're not certain, if we're certain we're watching the WRONG player, try to switch
|
|
||||||
if (players[*displayplayerp].position != targetposition+1 && !players[*displayplayerp].positiondelay)
|
|
||||||
{
|
|
||||||
K_DirectorSwitch(target, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void K_ToggleDirector(boolean active)
|
void K_ToggleDirector(UINT8 viewnum, boolean active)
|
||||||
{
|
{
|
||||||
|
DirectorInfo& directorinfo = g_directorinfo[viewnum];
|
||||||
|
|
||||||
if (directorinfo.active != active)
|
if (directorinfo.active != active)
|
||||||
{
|
{
|
||||||
directorinfo.cooldown = 0; // switch immediately
|
directorinfo.cooldown = 0; // switch immediately
|
||||||
|
|
@ -339,8 +374,7 @@ void K_ToggleDirector(boolean active)
|
||||||
|
|
||||||
boolean K_DirectorIsEnabled(UINT8 viewnum)
|
boolean K_DirectorIsEnabled(UINT8 viewnum)
|
||||||
{
|
{
|
||||||
(void)viewnum;
|
return g_directorinfo[viewnum].active;
|
||||||
return directorinfo.active;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean K_DirectorIsAvailable(UINT8 viewnum)
|
boolean K_DirectorIsAvailable(UINT8 viewnum)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ void K_InitDirector(void);
|
||||||
void K_UpdateDirector(void);
|
void K_UpdateDirector(void);
|
||||||
void K_DrawDirectorDebugger(void);
|
void K_DrawDirectorDebugger(void);
|
||||||
void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source);
|
void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source);
|
||||||
void K_ToggleDirector(boolean active);
|
void K_ToggleDirector(UINT8 viewnum, boolean active);
|
||||||
boolean K_DirectorIsEnabled(UINT8 viewnum);
|
boolean K_DirectorIsEnabled(UINT8 viewnum);
|
||||||
boolean K_DirectorIsAvailable(UINT8 viewnum);
|
boolean K_DirectorIsAvailable(UINT8 viewnum);
|
||||||
|
|
||||||
|
|
|
||||||
36
src/p_mobj.c
36
src/p_mobj.c
|
|
@ -50,6 +50,7 @@
|
||||||
#include "k_director.h"
|
#include "k_director.h"
|
||||||
#include "m_easing.h"
|
#include "m_easing.h"
|
||||||
#include "k_podium.h"
|
#include "k_podium.h"
|
||||||
|
#include "g_party.h"
|
||||||
|
|
||||||
actioncache_t actioncachehead;
|
actioncache_t actioncachehead;
|
||||||
|
|
||||||
|
|
@ -12217,22 +12218,29 @@ void P_SpawnPlayer(INT32 playernum)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spectating when there is literally any other player in
|
boolean director = p->spectator && pcount > 0;
|
||||||
// the level enables director cam. Or if the first player
|
|
||||||
// enters the game, spectate them.
|
|
||||||
// TODO: how do we support splitscreen?
|
|
||||||
if (playernum == consoleplayer || pcount == 1)
|
|
||||||
{
|
|
||||||
K_ToggleDirector(players[consoleplayer].spectator && pcount > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: handle splitscreen
|
if (G_IsPartyLocal(playernum))
|
||||||
// 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;
|
// Spectating when there is literally any other
|
||||||
|
// player in the level enables director cam.
|
||||||
|
K_ToggleDirector(G_PartyPosition(playernum), director);
|
||||||
|
|
||||||
|
// Spectators can switch to freecam. This should be
|
||||||
|
// disabled when they enter the race, or when the level
|
||||||
|
// changes.
|
||||||
|
if (!demo.playback)
|
||||||
|
{
|
||||||
|
demo.freecam = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (pcount == 1)
|
||||||
|
{
|
||||||
|
// If the first player enters the game, view them.
|
||||||
|
for (i = 0; i <= r_splitscreen; ++i)
|
||||||
|
{
|
||||||
|
K_ToggleDirector(i, director);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2802,11 +2802,10 @@ static void P_DeathThink(player_t *player)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: support splitscreen
|
|
||||||
// Spectate another player after 2 seconds
|
// Spectate another player after 2 seconds
|
||||||
if (player == &players[consoleplayer] && playerGone == true && (gametyperules & GTR_BUMPERS) && player->deadtimer == 2*TICRATE)
|
if (G_IsPartyLocal(player - players) && playerGone == true && (gametyperules & GTR_BUMPERS) && player->deadtimer == 2*TICRATE)
|
||||||
{
|
{
|
||||||
K_ToggleDirector(true);
|
K_ToggleDirector(G_PartyPosition(player - players), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep time rolling
|
// Keep time rolling
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue