diff --git a/src/Analysis/ProjectZomboid/PvpDamageInformation.php b/src/Analysis/ProjectZomboid/PvpDamageInformation.php new file mode 100644 index 0000000..5f1c52b --- /dev/null +++ b/src/Analysis/ProjectZomboid/PvpDamageInformation.php @@ -0,0 +1,26 @@ +setLabel('PvP combat'); + $this->setValue(sprintf( + '%s hit %s with %s', + $matches['attacker'], + $matches['victim'], + $matches['weapon'] + )); + } +} diff --git a/src/Pattern/ProjectZomboid/PvpPattern.php b/src/Pattern/ProjectZomboid/PvpPattern.php index a55bc77..7112e22 100644 --- a/src/Pattern/ProjectZomboid/PvpPattern.php +++ b/src/Pattern/ProjectZomboid/PvpPattern.php @@ -24,4 +24,12 @@ class PvpPattern public const string COMBAT = '/^Combat: "(?[^"]+)" \((?\d+),(?\d+),(?-?\d+)\) hit "(?[^"]+)" \((?\d+),(?\d+),(?-?\d+)\) weapon="(?[^"]+)" damage=(?-?\d+\.\d+)\.$/'; public const string SAFETY = '/^Safety: "(?[^"]+)" \((?\d+),(?\d+),(?-?\d+)\) (?\w+) (?true|false)\.$/'; + + /** + * Real-PvP combat: weapon!="zombie" AND damage>0. Filtering is in the + * regex itself so PatternAnalyser produces no insights for zombie/zero + * rows. Damage clause matches any positive non-zero float (rejects + * 0.000000 and any leading-minus value). + */ + public const string COMBAT_REAL = '/Combat: "(?[^"]+)" \([^)]+\) hit "(?[^"]+)" \([^)]+\) weapon="(?(?!zombie")[^"]+)" damage=(?0\.0*[1-9][0-9]*|[1-9][0-9]*\.[0-9]+)/'; } diff --git a/test/tests/Games/ProjectZomboid/Analysis/PvpDamageInformationTest.php b/test/tests/Games/ProjectZomboid/Analysis/PvpDamageInformationTest.php new file mode 100644 index 0000000..f2385f0 --- /dev/null +++ b/test/tests/Games/ProjectZomboid/Analysis/PvpDamageInformationTest.php @@ -0,0 +1,66 @@ +assertSame([PvpPattern::COMBAT_REAL], PvpDamageInformation::getPatterns()); + } + + public function testCombatRealMatchesPositiveDamageRealWeapon(): void + { + $line = 'Combat: "Player1" (1005,2005,0) hit "Player2" (1006,2005,0) weapon="Tire Iron (Worn)" damage=0.112317.'; + $this->assertSame(1, preg_match(PvpPattern::COMBAT_REAL, $line, $m)); + + $insight = new PvpDamageInformation(); + $insight->setMatches($m, 0); + + $this->assertSame('PvP combat', $insight->getLabel()); + $this->assertSame('Player1 hit Player2 with Tire Iron (Worn)', $insight->getValue()); + } + + public function testCombatRealRejectsZombieWeapon(): void + { + $line = 'Combat: "Player1" (1005,2005,0) hit "Player1" (1005,2005,0) weapon="zombie" damage=-1.000000.'; + $this->assertSame(0, preg_match(PvpPattern::COMBAT_REAL, $line)); + } + + public function testCombatRealRejectsZeroDamage(): void + { + $line = 'Combat: "Player1" (1100,2200,0) hit "Player2" (1100,2201,0) weapon="vehicle" damage=0.000000.'; + $this->assertSame(0, preg_match(PvpPattern::COMBAT_REAL, $line)); + } + + public function testCombatRealRejectsNegativeDamage(): void + { + $line = 'Combat: "Player1" (1005,2005,0) hit "Player2" (1005,2005,0) weapon="Bare Hands" damage=-0.500000.'; + $this->assertSame(0, preg_match(PvpPattern::COMBAT_REAL, $line)); + } + + public function testIsEqualCoalescesSameAttackerVictimWeapon(): void + { + $a = $this->insightFor('Player1', 'Player2', 'Bare Hands'); + $b = $this->insightFor('Player1', 'Player2', 'Bare Hands'); + $c = $this->insightFor('Player1', 'Player2', 'Tire Iron'); + + $this->assertTrue($a->isEqual($b)); + $this->assertFalse($a->isEqual($c)); + } + + private function insightFor(string $attacker, string $victim, string $weapon): PvpDamageInformation + { + $insight = new PvpDamageInformation(); + $insight->setMatches([ + 'attacker' => $attacker, + 'victim' => $victim, + 'weapon' => $weapon, + ], 0); + return $insight; + } +}