Skip to content

Commit

Permalink
Co-op: fix the 'No Friendly Fire' option so that players cannot shoot…
Browse files Browse the repository at this point in the history
… each other (#116).
  • Loading branch information
BodbDearg committed Sep 5, 2024
1 parent 1228b97 commit 7678053
Show file tree
Hide file tree
Showing 13 changed files with 128 additions and 11 deletions.
4 changes: 0 additions & 4 deletions game/Doom/Base/i_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,6 @@ texture_t gTex_CONNECT;
// Max tolerated packet delay in milliseconds: after which we start adjusting clocks
static constexpr int32_t MAX_PACKET_DELAY_MS = 15;

// The current network protocol version.
// Should be incremented whenever the data format being transmitted changes, or when updates might cause differences in game behavior.
static constexpr int32_t NET_PROTOCOL_VERSION = 33;

// Previous game error checking value when we last sent to the other player.
// Have to store this because we always send 1 packet ahead for the next frame.
static uint32_t gNetPrevErrorCheck;
Expand Down
4 changes: 4 additions & 0 deletions game/Doom/Base/i_main.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ struct texture_t;
#endif
#endif

// The current network protocol version.
// Should be incremented whenever the data format being transmitted changes, or when updates might cause differences in game behavior.
static constexpr int32_t NET_PROTOCOL_VERSION = 34;

// Game control binding index: these are the actions which are configurable to different buttons.
// These also must be synchronized in a network game.
enum controlbinding_t : int32_t {
Expand Down
7 changes: 7 additions & 0 deletions game/Doom/Game/p_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "doomdata.h"
#include "info.h"
#include "p_enemy.h"
#include "p_inter.h"
#include "p_local.h"
#include "p_maputl.h"
#include "p_mobj.h"
Expand Down Expand Up @@ -690,6 +691,12 @@ static bool PB_CheckThing(mobj_t& mobj) noexcept {
#endif

if (bFirerExists) {
#if PSYDOOM_MODS
// Disable player missile to player collisions if the 'no friendly fire' option is enabled for a coop game
if (P_IgnoreFriendlyAttackTarget(*pFirer, mobj))
return true;
#endif

const mobjtype_t sourceObjType = pFirer->type;

if (sourceObjType == mobj.type) {
Expand Down
18 changes: 17 additions & 1 deletion game/Doom/Game/p_inter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,6 @@ void P_TouchSpecialThing(mobj_t& special, mobj_t& toucher) noexcept {
}

#if PSYDOOM_MODS

//------------------------------------------------------------------------------------------------------------------------------------------
// PsyDoom addition: tells if the given key can be picked up the specified player
//------------------------------------------------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -814,6 +813,23 @@ bool P_CanTouchSpecialThing(const mobj_t& special, const mobj_t& toucher) noexce
return true;
}

//------------------------------------------------------------------------------------------------------------------------------------------
// Tells if the specified attacker should NOT aim at or hit the given target due to 'no friendly fire' rules for a coop game
//------------------------------------------------------------------------------------------------------------------------------------------
bool P_IgnoreFriendlyAttackTarget(const mobj_t& attacker, const mobj_t& target) noexcept
{
return (
// The 'No friendly fire' rule is only used for player to player attacks and only for coop
attacker.player &&
target.player &&
(gNetGame == gt_coop) &&
// The 'No friendly fire' rule must be enabled, otherwise players can attack each other
Game::gSettings.bCoopNoFriendlyFire &&
// Older versions of PsyDoom let players hit each other, even when the 'no friendly fire' option was turned on for coop.
// We keep this behavior for older demos for compatibility purposes.
Game::gSettings.bFixCoopNoFriendlyFireTargeting
);
}
#endif // #if PSYDOOM_MODS

//------------------------------------------------------------------------------------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions game/Doom/Game/p_inter.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ void P_TouchSpecialThing(mobj_t& special, mobj_t& toucher) noexcept;

#if PSYDOOM_MODS
bool P_CanTouchSpecialThing(const mobj_t& special, const mobj_t& toucher) noexcept;
bool P_IgnoreFriendlyAttackTarget(const mobj_t& attacker, const mobj_t& target) noexcept;
#endif

void P_KillMobj(mobj_t* const pKiller, mobj_t& target) noexcept;
Expand Down
6 changes: 6 additions & 0 deletions game/Doom/Game/p_move.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,12 @@ static bool PIT_CheckThing(mobj_t& mobj) noexcept {
#endif

if (bFirerExists) {
#if PSYDOOM_MODS
// Disable player missile to player collisions if the 'no friendly fire' option is enabled for a coop game
if (P_IgnoreFriendlyAttackTarget(*pFiringThing, mobj))
return true;
#endif

if (mobj.type == pFiringThing->type) {
// Missiles don't collide with the things which fired them
if (&mobj == pFiringThing)
Expand Down
7 changes: 7 additions & 0 deletions game/Doom/Game/p_shoot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "Doom/Renderer/r_local.h"
#include "Doom/Renderer/r_main.h"
#include "doomdata.h"
#include "p_inter.h"
#include "p_map.h"
#include "p_setup.h"

Expand Down Expand Up @@ -272,6 +273,12 @@ bool PA_ShootThing(mobj_t& thing, const fixed_t hitFrac) noexcept {
if (&thing == gpShooter)
return true;

#if PSYDOOM_MODS
// Disable player to player targeting if the 'no friendly fire' option is enabled for a coop game
if (P_IgnoreFriendlyAttackTarget(*gpShooter, thing))
return true;
#endif

// Can't shoot the thing if it's not shootable (corpse etc.)
if ((thing.flags & MF_SHOOTABLE) == 0)
return true;
Expand Down
2 changes: 1 addition & 1 deletion game/PsyDoom/Config/ConfigSerialization_Multiplayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ void initCfgSerialization_Multiplayer() noexcept {

cfg.coopNoFriendlyFire = makeConfigField(
"CoopNoFriendlyFire",
"Cooperative: disable taking damage from other players?\n"
"Cooperative: disable taking damage from and shooting at other players?\n"
"\n"
"Notes:\n"
"(1) This setting does not affect barrel or telefrag damage.\n"
Expand Down
9 changes: 9 additions & 0 deletions game/PsyDoom/DemoCommon.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
#include "DemoCommon.h"

#include "Doom/Base/i_main.h"
#include "Doom/doomdef.h"
#include "Endian.h"

#include <cstring>

// Helper/reminder for when one of these constants is changed.
// These numbers should normally be incremented at the same time for a public facing release of PsyDoom.
// A change in network game behavior normally implies a change in demo behavior, and new game settings to enable/disable the modified behavior.
static_assert(
(NET_PROTOCOL_VERSION == 34) &&
(DemoCommon::DEMO_FILE_VERSION == 16)
);

BEGIN_NAMESPACE(DemoCommon)

//--------------------------------------------------------------------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion game/PsyDoom/DemoCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ BEGIN_NAMESPACE(DemoCommon)
// The current demo file format version.
// This should be incremented whenever the contents of or expected behavior of the demo file changes.
//------------------------------------------------------------------------------------------------------------------------------------------
static constexpr uint32_t DEMO_FILE_VERSION = 15;
static constexpr uint32_t DEMO_FILE_VERSION = 16;

//------------------------------------------------------------------------------------------------------------------------------------------
// Helper: calls 'byteSwap()' on the specified type if the host architecture is big endian.
Expand Down
4 changes: 4 additions & 0 deletions game/PsyDoom/Game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,9 @@ void getUserGameSettings(GameSettings& settings) noexcept {
settings.bAllowMultiMapPickup = Config::gbAllowMultiMapPickup;
settings.bEnableMapPatches_GamePlay = Config::gbEnableMapPatches_GamePlay;
settings.bCoopNoFriendlyFire = Config::gbCoopNoFriendlyFire;
// We always apply this fix to 'no friendly fire' for current versions of PsyDoom.
// It's only ever disabled when playing older multiplayer demos, for compatibility reasons.
settings.bFixCoopNoFriendlyFireTargeting = true;
settings.bCoopForceSpawnDeathmatchThings = Config::gbCoopForceSpawnDeathmatchThings;
settings.bDmExitDisabled = Config::gbDmExitDisabled;
settings.bCoopPreserveKeys = Config::gbCoopPreserveKeys;
Expand Down Expand Up @@ -260,6 +263,7 @@ void getClassicDemoGameSettings(GameSettings& settings) noexcept {
settings.bAllowMultiMapPickup = false;
settings.bEnableMapPatches_GamePlay = false;
settings.bCoopNoFriendlyFire = false;
settings.bFixCoopNoFriendlyFireTargeting = false;
settings.bCoopForceSpawnDeathmatchThings = false;
settings.bDmExitDisabled = false;
settings.bCoopPreserveKeys = false;
Expand Down
72 changes: 69 additions & 3 deletions game/PsyDoom/GameSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "GameSettings.h"

#include "Asserts.h"
#include "DemoCommon.h"
#include "Doom/Base/i_main.h"
#include "Endian.h"
#include "Game.h"

Expand All @@ -13,11 +15,62 @@
#include <type_traits>

//------------------------------------------------------------------------------------------------------------------------------------------
// Game version: 1.1.0+
// Game version: 1.2.0+
// Demo file version: 16
// Net protocol version: 34
//------------------------------------------------------------------------------------------------------------------------------------------
static_assert(DemoCommon::DEMO_FILE_VERSION == 16, "Update the comment here after bumping the current demo file version!");
static_assert(NET_PROTOCOL_VERSION == 34, "Update the comment here after bumping the net protocol version!");

typedef GameSettings GameSettingsV3;

//------------------------------------------------------------------------------------------------------------------------------------------
// Game version: 1.1.x
// Demo file version: 14
// Net protocol version: 31
// Net protocol version: 31/32
//------------------------------------------------------------------------------------------------------------------------------------------
typedef GameSettings GameSettingsV2;
struct GameSettingsV2 {
static constexpr uint32_t VERSION = 2;

uint8_t bUsePalTimings;
uint8_t bUseDemoTimings;
uint8_t bFixKillCount;
uint8_t bFixLineActivation;
uint8_t bUseExtendedPlayerShootRange;
uint8_t bFixMultiLineSpecialCrossing;
uint8_t bUsePlayerRocketBlastFix;
uint8_t bUseSuperShotgunDelayTweak;
uint8_t bUseMoveInputLatencyTweak;
uint8_t bUseItemPickupFix;
uint8_t bUseFinalDoomPlayerMovement;
uint8_t bAllowMovementCancellation;
uint8_t bAllowTurningCancellation;
uint8_t bFixViewBobStrength;
uint8_t bFixGravityStrength;
uint8_t bNoMonsters;
uint8_t bNoMonstersBossFixup;
uint8_t bPistolStart;
uint8_t bTurboMode;
uint8_t bUseLostSoulSpawnFix;
uint8_t bUseLineOfSightOverflowFix;
uint8_t bRemoveMaxCrossLinesLimit;
uint8_t bFixOutdoorBulletPuffs;
uint8_t bFixBlockingGibsBug;
uint8_t bFixSoundPropagation;
uint8_t bFixSpriteVerticalWarp;
uint8_t bAllowMultiMapPickup;
uint8_t bEnableMapPatches_GamePlay;
uint8_t bCoopNoFriendlyFire;
uint8_t bCoopForceSpawnDeathmatchThings;
uint8_t bDmExitDisabled;
uint8_t bCoopPreserveKeys;
uint8_t bDmActivateBossSpecialSectors;
int32_t lostSoulSpawnLimit;
int32_t viewBobbingStrengthFixed;
int32_t dmFragLimit;
int32_t coopPreserveAmmoFactor;
uint8_t bSinglePlayerForceSpawnDmThings;
};

//------------------------------------------------------------------------------------------------------------------------------------------
// Game version: 1.0.x
Expand Down Expand Up @@ -104,6 +157,10 @@ static void byteSwapGameSettings(GameSettingsT& settings) noexcept {
Endian::byteSwapInPlace(settings.coopPreserveAmmoFactor);
Endian::byteSwapInPlace(settings.bSinglePlayerForceSpawnDmThings);
}

if constexpr (GameSettingsT::VERSION >= 3) {
Endian::byteSwapInPlace(settings.bFixCoopNoFriendlyFireTargeting);
}
}

//------------------------------------------------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -161,6 +218,10 @@ static void migrateGameSettings(OldGameSettingsT& oldSettings, GameSettings& new
COPY_GAME_SETTINGS_FIELD(bSinglePlayerForceSpawnDmThings);
}

if constexpr (OldGameSettingsT::VERSION >= 3) {
COPY_GAME_SETTINGS_FIELD(bFixCoopNoFriendlyFireTargeting);
}

#undef COPY_GAME_SETTINGS_FIELD
}

Expand Down Expand Up @@ -215,8 +276,11 @@ int32_t getGameSettingsVersionForDemoFileVersion(const int32_t demoFileVersion)
switch (demoFileVersion) {
case 11: return 1;
case 14: return 2;
case 16: return 3;
}

static_assert(DemoCommon::DEMO_FILE_VERSION == 16, "Update the code here after bumping the current demo file version!");

ASSERT_FAIL_F("No 'GameSettings' mapping for demo file version '%d'! Support might need to be added here...", demoFileVersion);
return -1;
}
Expand All @@ -229,6 +293,7 @@ int32_t getGameSettingsSize(const int32_t gameSettingsVersion) noexcept {
switch (gameSettingsVersion) {
case 1: return sizeof(GameSettingsV1);
case 2: return sizeof(GameSettingsV2);
case 3: return sizeof(GameSettingsV3);
}

ASSERT_FAIL_F("Invalid 'GameSettings' version '%d'!", gameSettingsVersion);
Expand All @@ -247,6 +312,7 @@ bool readAndMigrateGameSettings(const int32_t gameSettingsVersion, const void* c
switch (gameSettingsVersion) {
case 1: readAndMigrateGameSettingsImpl<GameSettingsV1>(pSrcBuffer, dstSettings); break;
case 2: readAndMigrateGameSettingsImpl<GameSettingsV2>(pSrcBuffer, dstSettings); break;
case 3: readAndMigrateGameSettingsImpl<GameSettingsV3>(pSrcBuffer, dstSettings); break;

default:
ASSERT_FAIL_F("Invalid 'GameSettings' version '%d'!", gameSettingsVersion);
Expand Down
3 changes: 2 additions & 1 deletion game/PsyDoom/GameSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
//------------------------------------------------------------------------------------------------------------------------------------------
struct GameSettings {
// The current version of this struct: this can be incremented for future PsyDoom releases if the format changes
static constexpr uint32_t VERSION = 2;
static constexpr uint32_t VERSION = 3;

uint8_t bUsePalTimings; // Use 50 Hz vblanks and other various timing adjustments for the PAL version of the game?
uint8_t bUseDemoTimings; // Force player logic to run at a consistent, but slower rate used by demos? (15 Hz for NTSC)
Expand Down Expand Up @@ -41,6 +41,7 @@ struct GameSettings {
uint8_t bAllowMultiMapPickup; // If true then allow players to pickup more than one 'Computer Area Map'
uint8_t bEnableMapPatches_GamePlay; // Enable patches for original game maps that affect gameplay?
uint8_t bCoopNoFriendlyFire; // If true then players are not able to damage each other in co-op.
uint8_t bFixCoopNoFriendlyFireTargeting; // Fix a bug with 'bCoopNoFriendlyFire' where friendly players are targeted?
uint8_t bCoopForceSpawnDeathmatchThings; // Enable multiplayer-only things in co-op?
uint8_t bDmExitDisabled; // If true while playing deathmatch exits will display a 'disabled' message and do nothing.
uint8_t bCoopPreserveKeys; // If true while playing co-op a player will spawn with previously collected keys.
Expand Down

0 comments on commit 7678053

Please sign in to comment.