diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 3b914bfc3..2cd39ead3 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2269,13 +2269,6 @@ static void Command_SetViews_f(void) UINT8 splits; UINT8 newsplits; - if (!( demo.playback && multiplayer )) - { - CONS_Alert(CONS_NOTICE, - "You must be viewing a multiplayer replay to use this.\n"); - return; - } - if (COM_Argc() != 2) { CONS_Printf("setviews : set the number of split screens\n"); @@ -2286,12 +2279,33 @@ static void Command_SetViews_f(void) newsplits = atoi(COM_Argv(1)); newsplits = min(max(newsplits, 1), 4); - if (newsplits > splits) + + if (newsplits > splits && demo.playback && multiplayer) + { G_AdjustView(newsplits, 0, true); + } else { - r_splitscreen = newsplits-1; - R_ExecuteSetViewSize(); + // Even if the splits go beyond the real number of + // splitscreen players, displayplayers was filled + // with duplicates of P1 (see Got_AddPlayer). + if (demo.playback) + { + G_SyncDemoParty(consoleplayer, newsplits-1); + } + else + { + r_splitscreen = newsplits-1; + R_ExecuteSetViewSize(); + } + + // If promoting (outside of replays), make sure the + // camera is in the correct position. + UINT8 i; + for (i = splits + 1; i <= newsplits; ++i) + { + G_FixCamera(i); + } } } @@ -2308,7 +2322,7 @@ Command_Invite_f (void) return; } - if (r_splitscreen >= MAXSPLITSCREENPLAYERS) + if (G_PartySize(consoleplayer) >= MAXSPLITSCREENPLAYERS) { CONS_Alert(CONS_WARNING, "Your party is full!\n"); return; @@ -2327,9 +2341,9 @@ Command_Invite_f (void) return; } - if (invitee == consoleplayer) + if (G_IsPartyLocal(invitee)) { - CONS_Alert(CONS_WARNING, "You cannot invite yourself! Bruh!\n"); + CONS_Alert(CONS_WARNING, "That player is already a member of your party.\n"); return; } @@ -2337,6 +2351,7 @@ Command_Invite_f (void) { CONS_Alert(CONS_WARNING, "That player has already been invited to join another party.\n"); + return; } if ((G_PartySize(consoleplayer) + G_LocalSplitscreenPartySize(invitee)) > MAXSPLITSCREENPLAYERS) @@ -2344,6 +2359,7 @@ Command_Invite_f (void) CONS_Alert(CONS_WARNING, "That player joined with too many " "splitscreen players for your party.\n"); + return; } CONS_Printf( @@ -2387,6 +2403,7 @@ Command_CancelInvite_f (void) { CONS_Alert(CONS_WARNING, "You have not invited this player!\n"); + return; } CONS_Printf( @@ -2432,7 +2449,7 @@ Command_RejectInvite_f (void) static void Command_LeaveParty_f (void) { - if (r_splitscreen > splitscreen) + if (G_PartySize(consoleplayer) > G_LocalSplitscreenPartySize(consoleplayer)) { CONS_Printf("\x85Leaving party...\n"); @@ -3647,24 +3664,6 @@ void P_SetPlayerSpectator(INT32 playernum) players[playernum].pflags &= ~PF_WANTSTOJOIN; players[playernum].playerstate = PST_REBORN; - - /*if (cv_spectatormusic.value && (players[displayplayers[0]].spectator == true) && !r_splitscreen) - { - if (P_UseContinuousLevelMusic()) - { - if (!stricmp(Music_Song("level_nosync"), cv_spectatormusiclump.string)) - { - // Do not reset music if it is the same - Music_BatchExempt("level_nosync"); - } - Music_Remap("level_nosync", cv_spectatormusiclump.string); - } - else - { - Music_Remap("level", cv_spectatormusiclump.string); - } - }*/ - } //todo: This and the other teamchange functions are getting too long and messy. Needs cleaning. @@ -3742,24 +3741,19 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) //Safety first! // (not respawning spectators here...) - if (!players[playernum].spectator) + wasspectator = (players[playernum].spectator == true); + + if (!wasspectator) { - if (players[playernum].mo) + if (gamestate == GS_LEVEL && players[playernum].mo) { - P_DamageMobj(players[playernum].mo, NULL, NULL, 1, - (NetPacket.packet.newteam ? DMG_INSTAKILL : DMG_SPECTATOR)); - } - //else - if (!NetPacket.packet.newteam) - { - players[playernum].playerstate = PST_REBORN; + // The following will call P_SetPlayerSpectator if successful + P_DamageMobj(players[playernum].mo, NULL, NULL, 1, DMG_SPECTATOR); } + //...but because the above could return early under some contexts, we try again here + P_SetPlayerSpectator(playernum); } - else - wasspectator = true; - - players[playernum].pflags &= ~PF_WANTSTOJOIN; //Now that we've done our error checking and killed the player //if necessary, put the player on the correct team/status. @@ -3809,22 +3803,6 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) else if (NetPacket.packet.newteam == 0 && !wasspectator) HU_AddChatText(va("\x82*%s became a spectator.", player_names[playernum]), false); // "entered the game" text was moved to P_SpectatorJoinGame - // Reset away view (some code referenced from P_SpectatorJoinGame) - { - UINT8 i = 0; - INT32 *localplayertable = (splitscreen_partied[consoleplayer] ? splitscreen_party[consoleplayer] : g_localplayers); - - for (i = 0; i < r_splitscreen; i++) - { - if (localplayertable[i] == playernum) - { - LUA_HookViewpointSwitch(players+playernum, players+playernum, true); - displayplayers[i] = playernum; - break; - } - } - } - /*if (G_GametypeHasTeams()) { if (NetPacket.packet.newteam) diff --git a/src/g_demo.c b/src/g_demo.c index 3646bde41..3dfcb51e8 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -53,6 +53,7 @@ #include "k_color.h" #include "k_follower.h" #include "k_grandprix.h" +#include "g_party.h" static CV_PossibleValue_t recordmultiplayerdemos_cons_t[] = {{0, "Disabled"}, {1, "Manual Save"}, {2, "Auto Save"}, {0, NULL}}; consvar_t cv_recordmultiplayerdemos = CVAR_INIT ("netdemo_record", "Manual Save", CV_SAVE, recordmultiplayerdemos_cons_t, NULL); @@ -4186,3 +4187,33 @@ boolean G_DemoTitleResponder(event_t *ev) return true; } + +void G_SyncDemoParty(INT32 rem, INT32 newsplitscreen) +{ + int r_splitscreen_copy = r_splitscreen; + INT32 displayplayers_copy[MAXSPLITSCREENPLAYERS]; + memcpy(displayplayers_copy, displayplayers, sizeof displayplayers); + + // If we switch away from someone's view, that player + // should be removed from the party. + // However, it is valid to have the player on multiple + // viewports. + + // Remove this player + G_LeaveParty(rem); + + // And reset the rest of the party + for (int i = 0; i <= r_splitscreen_copy; ++i) + G_LeaveParty(displayplayers_copy[i]); + + // Restore the party, without the removed player, and + // with the order matching displayplayers + for (int i = 0; i <= newsplitscreen; ++i) + G_JoinParty(consoleplayer, displayplayers_copy[i]); + + // memcpy displayplayers back to preserve duplicates + // (G_JoinParty will not create duplicates itself) + r_splitscreen = newsplitscreen; + memcpy(displayplayers, displayplayers_copy, sizeof displayplayers); + R_ExecuteSetViewSize(); +} diff --git a/src/g_demo.h b/src/g_demo.h index 8a73145ad..78dd0d217 100644 --- a/src/g_demo.h +++ b/src/g_demo.h @@ -123,6 +123,9 @@ extern UINT8 demo_writerng; #define DXD_COLOR 0x10 // color changed #define DXD_FOLLOWER 0x20 // follower was changed #define DXD_RESPAWN 0x40 // "respawn" command in console + +#define DXD_ADDPLAYER (DXD_JOINDATA|DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN|DXD_FOLLOWER) + #define DXD_WEAPONPREF 0x80 // netsynced playsim settings were changed #define DXD_PST_PLAYING 0x01 @@ -197,6 +200,8 @@ void G_SaveDemo(void); boolean G_DemoTitleResponder(event_t *ev); +void G_SyncDemoParty(INT32 rem, INT32 newsplitscreen); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/g_game.c b/src/g_game.c index 19117bbb2..4b9ba9424 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1805,19 +1805,12 @@ INT32 G_CountPlayersPotentiallyViewable(boolean active) return total; } -// -// G_ResetView -// Correct a viewpoint to playernum or the next available, wraps forward. -// Also promotes splitscreen up to available viewable players. -// An out of range playernum is corrected. -// void G_ResetView(UINT8 viewnum, INT32 playernum, boolean onlyactive) { UINT8 splits; UINT8 viewd; INT32 *displayplayerp; - camera_t *camerap; INT32 olddisplayplayer; INT32 playersviewable; @@ -1843,33 +1836,70 @@ void G_ResetView(UINT8 viewnum, INT32 playernum, boolean onlyactive) /* Check if anyone is available to view. */ if (( playernum = G_FindView(playernum, viewnum, onlyactive, playernum < olddisplayplayer) ) == -1) - return; + { + if (G_PartySize(consoleplayer) < viewnum) + { + return; + } + + /* Fall back on true self */ + playernum = G_PartyMember(consoleplayer, viewnum - 1); + } + + // Call ViewpointSwitch hooks here. + // The viewpoint was forcibly changed. + LUA_HookViewpointSwitch(&players[g_localplayers[viewnum - 1]], &players[playernum], true); /* Focus our target view first so that we don't take its player. */ (*displayplayerp) = playernum; - if ((*displayplayerp) != olddisplayplayer) - { - camerap = &camera[viewnum-1]; - P_ResetCamera(&players[(*displayplayerp)], camerap); - - R_ResetViewInterpolation(viewnum); - } + /* If a viewpoint changes, reset the camera to clear uninitialized memory. */ if (viewnum > splits) { - for (viewd = splits+1; viewd < viewnum; ++viewd) + for (viewd = splits+1; viewd <= viewnum; ++viewd) { - displayplayerp = (&displayplayers[viewd-1]); - camerap = &camera[viewd]; - - (*displayplayerp) = G_FindView(0, viewd, onlyactive, false); - - P_ResetCamera(&players[(*displayplayerp)], camerap); + G_FixCamera(viewd); + } + } + else + { + if ((*displayplayerp) != olddisplayplayer) + { + G_FixCamera(viewnum); } } - if (viewnum == 1 && demo.playback) - consoleplayer = displayplayers[0]; + if (demo.playback) + { + if (viewnum == 1) + consoleplayer = displayplayers[0]; + + G_SyncDemoParty(olddisplayplayer, r_splitscreen); + } + + // change statusbar also if playing back demo + if (demo.quitafterplaying) + ST_changeDemoView(); +} + +// +// G_FixCamera +// Reset camera position, angle and interpolation on a view +// after changing state. +// +void G_FixCamera(UINT8 view) +{ + player_t *player = &players[displayplayers[view - 1]]; + + // The order of displayplayers can change, which would + // invalidate localangle. + localangle[view - 1] = player->angleturn; + + P_ResetCamera(player, &camera[view - 1]); + + // Make sure the viewport doesn't interpolate at all into + // its new position -- just snap instantly into place. + R_ResetViewInterpolation(view); } // @@ -2949,7 +2979,7 @@ void G_AddPlayer(INT32 playernum, INT32 console) newplayer->playerstate = PST_REBORN; newplayer->jointime = 0; - demo_extradata[playernum] |= DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN|DXD_FOLLOWER; // Set everything + demo_extradata[playernum] |= DXD_ADDPLAYER; // Set everything } void G_BeginLevelExit(void) diff --git a/src/g_game.h b/src/g_game.h index 8bcab9b66..65abb025d 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -220,6 +220,7 @@ boolean G_CanView(INT32 playernum, UINT8 viewnum, boolean onlyactive); INT32 G_FindView(INT32 startview, UINT8 viewnum, boolean onlyactive, boolean reverse); INT32 G_CountPlayersPotentiallyViewable(boolean active); +void G_FixCamera(UINT8 view); void G_ResetViews(void); void G_ResetView(UINT8 viewnum, INT32 playernum, boolean onlyactive); void G_AdjustView(UINT8 viewnum, INT32 offset, boolean onlyactive); diff --git a/src/p_inter.c b/src/p_inter.c index e208e4d7f..48bc0812d 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1981,6 +1981,11 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, (void)source; (void)inflictor; + if (type == DMG_SPECTATOR && (G_GametypeHasTeams() || G_GametypeHasSpectators())) + { + P_SetPlayerSpectator(player-players); + } + if (player->exiting) { player->mo->destscale = 1;