"team follow1" can follow local players and doesn't work correctly
zturtleman opened this issue · 0 comments
Premise
Following other local players (split-screen players) is not allowed by follow
command. However it is allowed by team follow1
and team follow2
which can be used for displays at game tournaments.
For tournaments I would expect a separate PC to be used for the players and for showing team follow1/2
to the audience so following local players would not occur.
The issue found by @77ZaRR77.
Hard to fix
Following local players is buggy. Player is added twice to all viewports, appears to lag behind the real local player, messes with the sound listener position (higher localPlayerNum sets to either first or third person), and may cause duplicate sounds.
Copying real localPlayer_t
's predictedPlayerState
and predictedPlayerEntity
kind of works but there other data in localPlayer_t
that is per-player, like chat lines and showing scoreboard, and lots that is derived from the predictedPlayerState which should be copied.
It's kind of a mess to fix it but I'm not sure what the alternative is for team follow1
. It would be odd/confusing to show intermission point if it's a local player. As there would be no indication it's due to being a splitscreen player.
Try anyway
Below is my work-in-progress solution. Though view-height for real player gets messed up on stairs (huh?) and possibly other issues. I'm not planning to continue working on it right now.
Patch
diff --git a/code/cgame/cg_ents.c b/code/cgame/cg_ents.c
index 5a02e58e..9cdad9bb 100644
@@ -1302,6 +1304,9 @@ void CG_AddPacketEntities( void ) {
if ( cg.localPlayers[num].playerNum == -1 ) {
continue;
}
+ if ( cg.localPlayers[num].realLocalPlayer != num ) {
+ continue;
+ }
ps = &cg.localPlayers[num].predictedPlayerState;
BG_PlayerStateToEntityState( ps, &cg.localPlayers[num].predictedPlayerEntity.currentState, qfalse );
CG_AddCEntity( &cg.localPlayers[num].predictedPlayerEntity );
diff --git a/code/cgame/cg_local.h b/code/cgame/cg_local.h
index f5a04937..87fce6bf 100644
--- a/code/cgame/cg_local.h
+++ b/code/cgame/cg_local.h
@@ -543,6 +543,8 @@ typedef struct {
int playerNum;
+ int realLocalPlayer; // for player following another local player
+
// prediction state
qboolean hyperspace; // true if prediction has hit a trigger_teleport
playerState_t predictedPlayerState;
diff --git a/code/cgame/cg_snapshot.c b/code/cgame/cg_snapshot.c
index d8daf4b2..3ffc6f65 100644
--- a/code/cgame/cg_snapshot.c
+++ b/code/cgame/cg_snapshot.c
@@ -139,7 +139,7 @@ The transition point from snap to nextSnap has passed
static void CG_TransitionSnapshot( void ) {
centity_t *cent;
snapshot_t *oldFrame;
- int i;
+ int i, j;
if ( !cg.snap ) {
CG_Error( "CG_TransitionSnapshot: NULL cg.snap" );
@@ -178,6 +178,18 @@ static void CG_TransitionSnapshot( void ) {
if ( cg.snap->playerNums[i] != -1 ) {
BG_PlayerStateToEntityState( &cg.snap->pss[i], &cg_entities[ cg.snap->pss[i].playerNum ].currentState, qfalse );
cg_entities[ cg.snap->pss[i].playerNum ].interpolate = qfalse;
+
+ cg.localPlayers[i].realLocalPlayer = i;
+ if ( cg.snap->pss[i].pm_flags & PMF_FOLLOW ) {
+ for ( j = 0 ; j < CG_MaxSplitView() ; j++ ) {
+ playerState_t *otherPS = &cg.snap->pss[j];
+ if ( cg.snap->pss[i].playerNum == otherPS->playerNum && !( otherPS->pm_flags & PMF_FOLLOW ) ) {
+ // this player is following another local player...
+ cg.localPlayers[i].realLocalPlayer = j;
+ break;
+ }
+ }
+ }
}
}
diff --git a/code/cgame/cg_view.c b/code/cgame/cg_view.c
index 5cfec5b0..4426f7de 100644
--- a/code/cgame/cg_view.c
+++ b/code/cgame/cg_view.c
@@ -1133,6 +1140,12 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demo
cg.cur_lc = &cg.localPlayers[i];
cg.cur_ps = &cg.snap->pss[i];
+ if ( cg.cur_lc->realLocalPlayer != cg.cur_localPlayerNum ) {
+ memcpy( &cg.cur_lc->predictedPlayerState, &cg.localPlayers[cg.cur_lc->realLocalPlayer].predictedPlayerState, sizeof( cg.cur_lc->predictedPlayerState ) );
+
+ memcpy( &cg.cur_lc->predictedPlayerEntity, &cg.localPlayers[cg.cur_lc->realLocalPlayer].predictedPlayerEntity, sizeof( cg.cur_lc->predictedPlayerEntity ) );
+ }
+
// decide on third person view
cg.cur_lc->renderingThirdPerson = cg.cur_ps->persistant[PERS_TEAM] != TEAM_SPECTATOR
&& (cg_thirdPerson[cg.cur_localPlayerNum].integer || (cg.cur_ps->stats[STAT_HEALTH] <= 0) || cg.cur_lc->cameraOrbit);
@@ -1169,7 +1182,9 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demo
CG_PowerupTimerSounds();
// update audio positions
- trap_S_Respatialize( cg.cur_ps->playerNum, cg.refdef.vieworg, cg.refdef.viewaxis, inwater, !cg.cur_lc->renderingThirdPerson );
+ if ( cg.cur_lc->realLocalPlayer == cg.cur_localPlayerNum ) {
+ trap_S_Respatialize( cg.cur_ps->playerNum, cg.refdef.vieworg, cg.refdef.viewaxis, inwater, !cg.cur_lc->renderingThirdPerson );
+ }
// make sure the lagometerSample and frame timing isn't done twice when in stereo
if ( stereoView != STEREO_RIGHT && cg.viewport == 0 ) {
diff --git a/code/game/g_active.c b/code/game/g_active.c
index e2c32917..c405fe14 100644
--- a/code/game/g_active.c
+++ b/code/game/g_active.c
@@ -1064,6 +1064,11 @@ void SpectatorPlayerEndFrame( gentity_t *ent ) {
ent->player->ps = cl->ps;
ent->player->ps.pm_flags |= PMF_FOLLOW;
ent->player->ps.eFlags = flags;
+
+ // ugh, player is following one of their local players
+ if ( cl->pers.connectionNum == ent->player->pers.connectionNum ) {
+ //G_Printf( "GAME FIXME: don't follow one of their local players\n" );
+ }
return;
}
}