zturtleman/spearmint

"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;
                        }
                }