diff --git a/dGame/dComponents/DestroyableComponent.cpp b/dGame/dComponents/DestroyableComponent.cpp index 4b007954d..9c4f0a93f 100644 --- a/dGame/dComponents/DestroyableComponent.cpp +++ b/dGame/dComponents/DestroyableComponent.cpp @@ -532,6 +532,8 @@ void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignore bool DestroyableComponent::IsEnemy(const Entity* other) const { if (IsEnemyImplentation.ExecuteWithDefault(other, false)) return true; if (m_Parent->IsPlayer() && other->IsPlayer()) { + // A player is never its own PVP target + if (other == m_Parent) return false; auto* thisCharacterComponent = m_Parent->GetComponent(); if (!thisCharacterComponent) return false; auto* otherCharacterComponent = other->GetComponent(); diff --git a/tests/dGameTests/dComponentsTests/DestroyableComponentTests.cpp b/tests/dGameTests/dComponentsTests/DestroyableComponentTests.cpp index b3600d867..70678c55b 100644 --- a/tests/dGameTests/dComponentsTests/DestroyableComponentTests.cpp +++ b/tests/dGameTests/dComponentsTests/DestroyableComponentTests.cpp @@ -622,6 +622,41 @@ TEST_F(DestroyableTest, PlayerDieDoesNotClaimAClientDeath) { player.SetCharacter(nullptr); } +// LWOCharacterComponent::SendMessage, IsValidPVPTarget: two players with PVP on are enemies, a player is never its own +TEST_F(DestroyableTest, APlayerWithPvpIsNotItsOwnEnemy) { + auto playerInfo = info; + playerInfo.lot = 1; + SystemAddress address; + address.binaryAddress = 0x0100007f; + address.port = 2003; + + Entity player(0x1000000000000001LL, playerInfo); + Character character(1, nullptr); + player.SetCharacter(&character); + character.SetEntity(&player); + auto* playerCharacter = player.AddComponent(-1, &character, address); + auto* destroyable = player.AddComponent(-1); + + Entity other(0x1000000000000002LL, playerInfo); + Character otherCharacter(2, nullptr); + other.SetCharacter(&otherCharacter); + otherCharacter.SetEntity(&other); + auto* otherCharacterComponent = other.AddComponent(-1, &otherCharacter, address); + other.AddComponent(-1); + ASSERT_TRUE(player.IsPlayer()); + ASSERT_TRUE(other.IsPlayer()); + + EXPECT_FALSE(destroyable->IsEnemy(&other)); + playerCharacter->SetPvpEnabled(true); + EXPECT_FALSE(destroyable->IsEnemy(&other)); + otherCharacterComponent->SetPvpEnabled(true); + EXPECT_TRUE(destroyable->IsEnemy(&other)); + EXPECT_FALSE(destroyable->IsEnemy(&player)); + + player.SetCharacter(nullptr); + other.SetCharacter(nullptr); +} + // BasicAttack's dir_angle_* are degrees; live's Die carries them as these exact floats. TEST_F(DestroyableTest, DeathDirectionDegreesMatchLiveBits) { const auto bits = [](float value) { uint32_t out; std::memcpy(&out, &value, sizeof(out)); return out; };