diff --git a/composer.json b/composer.json index 2458124..114a577 100644 --- a/composer.json +++ b/composer.json @@ -6,18 +6,15 @@ "dev2": "php cli/server.php 2 8080 --debug & php cli/udp-ws-bridge.php & php cli/udp-ws-bridge.php 8082", "dev2c": "php cli/server.php 2 8080 --debug & php cli/udp-ws-bridge.php & sleep 2 && php cli/client.php acode 8080", "dev3c": "php cli/server.php 3 8080 --debug & php cli/udp-ws-bridge.php & sleep 1 ; php cli/client.php acode 8080 & php cli/client.php acode 8080", - "test": [ - "@putenv TEST_ENVIRONMENT=true", - "@unit" - ], "coverage": [ "@putenv XDEBUG_MODE=coverage", "@unit --coverage-html www/coverage" ], "check": [ + "@putenv PROJECT_CHECK=true", "@composer dumpautoload --optimize --classmap-authoritative", "@stan", - "@test" + "@unit" ] }, "require": { diff --git a/server/src/Core/Game.php b/server/src/Core/Game.php index 9ac60a2..62c4257 100644 --- a/server/src/Core/Game.php +++ b/server/src/Core/Game.php @@ -20,6 +20,7 @@ use cs\Event\RoundStartEvent; use cs\Event\SoundEvent; use cs\Event\ThrowEvent; +use cs\Interface\ForOneRoundMax; use cs\Map\Map; class Game @@ -409,6 +410,11 @@ private function halfTimeSwapTeams(): void private function roundReset(bool $firstRound, RoundEndEvent $roundEndEvent): void { $this->world->roundReset(); + foreach ($this->events as $event) { + if ($event instanceof ForOneRoundMax) { + $this->removeEvent($event->customId); + } + } $randomizeSpawn = $this->properties->randomize_spawn_position; foreach ($this->players as $player) { if (!$firstRound) { diff --git a/server/src/Core/Util.php b/server/src/Core/Util.php index 5d8fa00..d29f39c 100644 --- a/server/src/Core/Util.php +++ b/server/src/Core/Util.php @@ -52,10 +52,15 @@ public static function smallestDeltaAngle(int $start, int $target): int } /** - * @return null[]|float[] world angles [horizontal, vertical] in degree + * @return array{?float,float} world angles [horizontal, vertical] in degree */ public static function worldAngle(Point $point, Point $origin = new Point()): array { + $d = Util::distanceSquared($origin, $point); + if ($d === 0) { + return [null, 0.0]; + } + $cx = $point->x - $origin->x; $cy = $point->y - $origin->y; $cz = $point->z - $origin->z; @@ -65,10 +70,6 @@ public static function worldAngle(Point $point, Point $origin = new Point()): ar $h = fmod(450 - rad2deg(atan2($cz, $cx)), 360.0); } - $d = Util::distanceSquared($origin, $point); - if ($d === 0) { - return [$h, null]; - } $v = rad2deg(asin(abs($cy) / sqrt($d))); return [$h, (($cy) >= 0 ? $v : -$v)]; } diff --git a/server/src/Core/World.php b/server/src/Core/World.php index 8da738e..3335d96 100644 --- a/server/src/Core/World.php +++ b/server/src/Core/World.php @@ -508,7 +508,9 @@ public function bulletHit(Hittable $hit, Bullet $bullet, bool $wasHeadshot): voi $soundEvent->addExtra('shooter', $attacker); $this->makeSound($soundEvent); - $this->game->getScore()->getPlayerStat($attacker)->addDamage($damage); + if ($hit->getPlayer() && $hit->getPlayer()->isPlayingOnAttackerSide() !== $bullet->isOriginPlayerAttackerSide()) { + $this->game->getScore()->getPlayerStat($attacker)->addDamage($damage); + } } public function tryPlantBomb(Player $player): void diff --git a/server/src/Event/DropEvent.php b/server/src/Event/DropEvent.php index 639a46d..0c2afcd 100644 --- a/server/src/Event/DropEvent.php +++ b/server/src/Event/DropEvent.php @@ -7,12 +7,12 @@ use cs\Core\Item; use cs\Core\Player; use cs\Core\Point; -use cs\Core\Setting; use cs\Core\Util; use cs\Core\World; use cs\Enum\SoundType; +use cs\Interface\ForOneRoundMax; -class DropEvent extends Event +class DropEvent extends Event implements ForOneRoundMax { private string $id; diff --git a/server/src/HitGeometry/BallCollider.php b/server/src/HitGeometry/BallCollider.php index 8ed9bb3..46abec8 100644 --- a/server/src/HitGeometry/BallCollider.php +++ b/server/src/HitGeometry/BallCollider.php @@ -74,8 +74,7 @@ public function hasCollision(Point $point, float $angleHorizontal, float $angleV $candidate = new Point(); if ($this->lastExtremePosition && Util::distanceSquared($this->lastExtremePosition, $point) > 9) { [$angleH, $angleV] = Util::worldAngle($point, $this->lastExtremePosition); - $angleH = $angleH ?? $angleHorizontal; - $angleV = $angleV ?? $angleVertical; + $angleH = $angleH ?? 0; } else { $angleH = $angleHorizontal; $angleV = $angleVertical; diff --git a/server/src/Interface/ForOneRoundMax.php b/server/src/Interface/ForOneRoundMax.php new file mode 100644 index 0000000..7290acb --- /dev/null +++ b/server/src/Interface/ForOneRoundMax.php @@ -0,0 +1,8 @@ +assertNotNull($attackResult, 'No attack result'); + $this->assertTrue($attackResult->somePlayersWasHit(), 'No players were hit'); + + return $attackResult; + } + + public function assertPlayerNotHit(?AttackResult $attackResult): AttackResult + { + $this->assertNotNull($attackResult, 'No attack result'); + $this->assertFalse($attackResult->somePlayersWasHit(), 'Some players were hit'); + + return $attackResult; + } + public function assertPositionSame(Point $expected, Point $actual, string $extraMsg = ''): void { $this->assertTrue($expected->equals($actual), "Expected: {$expected} <> {$actual} actual." . $extraMsg); diff --git a/test/og/BaseTestCase.php b/test/og/BaseTestCase.php index f595436..0c07ff0 100644 --- a/test/og/BaseTestCase.php +++ b/test/og/BaseTestCase.php @@ -154,11 +154,14 @@ protected function playPlayer(TestGame $game, array $commands, int $playerId = 1 return; } if (!isset($commands[$i])) { - throw new InvalidArgumentException("No command defined for tick '{$i}' or too many tickMax in Game"); + throw new InvalidArgumentException("No command defined for tick '{$i}' or too many tickMax (no endGame?)"); } if (false === $commands[$i]) { - $game->setTickMax(0); $i++; + if (count($commands) > $i) { + throw new InvalidArgumentException("Early exit from iteration '{$i}'"); + } + $game->setTickMax(0); return; } if (is_int($commands[$i])) { diff --git a/test/og/Inventory/SimpleInventoryTest.php b/test/og/Inventory/SimpleInventoryTest.php index e40e51d..b47df57 100644 --- a/test/og/Inventory/SimpleInventoryTest.php +++ b/test/og/Inventory/SimpleInventoryTest.php @@ -242,7 +242,7 @@ public function testDropBoxCollisionNotPickUp(): void $box = new Box(new Point(500, 0, 500), 800, 600, 200); $game->getWorld()->addBox($box); - $this->playPlayerDebug($game, [ + $this->playPlayer($game, [ fn(Player $p) => $p->setPosition($box->getBase()->clone()->addZ(-($player->getBoundingRadius() + 10))->addX(200)), fn(Player $p) => $p->moveForward(), fn(Player $p) => $p->equipSecondaryWeapon(), diff --git a/test/og/Shooting/BacktrackTest.php b/test/og/Shooting/BacktrackTest.php index dade084..a0f6c62 100644 --- a/test/og/Shooting/BacktrackTest.php +++ b/test/og/Shooting/BacktrackTest.php @@ -54,10 +54,7 @@ public function testNoBacktrackKill(): void } if ($i === 0) { - $result = $state->getPlayer(1)->attack(); - $this->assertNotNull($result); - - $this->assertTrue($result->somePlayersWasHit()); + $result = $this->assertPlayerHit($state->getPlayer(1)->attack()); $this->assertFalse($state->getPlayer(2)->isAlive()); $game->quit(GameOverReason::TIE); } @@ -78,10 +75,7 @@ public function test1BacktrackDisable(): void $state->getPlayer(2)->moveRight(); if ($i === 1) { - $result = $state->getPlayer(1)->attack(); - $this->assertNotNull($result); - - $this->assertFalse($result->somePlayersWasHit()); + $result = $this->assertPlayerNotHit($state->getPlayer(1)->attack()); $this->assertTrue($state->getPlayer(2)->isAlive()); $game->quit(GameOverReason::TIE); } @@ -103,10 +97,7 @@ public function test1BacktrackEnableOneTick(): void $state->getPlayer(2)->moveRight(); if ($i === 1) { - $result = $state->getPlayer(1)->attack(); - $this->assertNotNull($result); - - $this->assertTrue($result->somePlayersWasHit()); + $result = $this->assertPlayerHit($state->getPlayer(1)->attack()); $this->assertFalse($state->getPlayer(2)->isAlive()); $game->quit(GameOverReason::TIE); } @@ -128,10 +119,7 @@ public function test1BacktrackEnableTwoTick(): void $state->getPlayer(2)->moveRight(); if ($i === 2) { - $result = $state->getPlayer(1)->attack(); - $this->assertNotNull($result); - - $this->assertTrue($result->somePlayersWasHit()); + $result = $this->assertPlayerHit($state->getPlayer(1)->attack()); $this->assertFalse($state->getPlayer(2)->isAlive()); $game->quit(GameOverReason::TIE); } @@ -153,10 +141,7 @@ public function test1BacktrackEnableTwoTickNoHitWhenPlayerThreeTickAwayFromHit() $state->getPlayer(2)->moveRight(); if ($i === 3) { - $result = $state->getPlayer(1)->attack(); - $this->assertNotNull($result); - - $this->assertFalse($result->somePlayersWasHit()); + $result = $this->assertPlayerNotHit($state->getPlayer(1)->attack()); $this->assertTrue($state->getPlayer(2)->isAlive()); $game->quit(GameOverReason::TIE); } @@ -178,10 +163,7 @@ public function test1BacktrackEnableFourTick(): void $state->getPlayer(2)->moveRight(); if ($i === 3) { - $result = $state->getPlayer(1)->attack(); - $this->assertNotNull($result); - - $this->assertTrue($result->somePlayersWasHit()); + $result = $this->assertPlayerHit($state->getPlayer(1)->attack()); $this->assertFalse($state->getPlayer(2)->isAlive()); $game->quit(GameOverReason::TIE); } diff --git a/test/og/TestGame.php b/test/og/TestGame.php index 72e09b2..b89d086 100644 --- a/test/og/TestGame.php +++ b/test/og/TestGame.php @@ -4,6 +4,7 @@ use Closure; use cs\Core\Game; +use cs\Core\GameException; use cs\Core\Setting; use cs\Net\Protocol\TextProtocol; @@ -27,6 +28,9 @@ public function setTickMax(int $tickMax): void public function start(bool $debug = false): void { if ($debug) { + if (getenv('PROJECT_CHECK') === 'true') { + throw new GameException('Debug flag detected, see oldest item of stacktrace'); + } $protocol = new TextProtocol(); $this->gameStates[0] = json_decode($protocol->serializeGameState($this)); } diff --git a/test/og/Unit/UtilTest.php b/test/og/Unit/UtilTest.php index 8891157..17bafcc 100644 --- a/test/og/Unit/UtilTest.php +++ b/test/og/Unit/UtilTest.php @@ -61,6 +61,14 @@ public function testCubeDiagonalMovement(): void $this->assertSame([10, -47, 14], Util::movementXYZ(35, -70, 50)); } + public function testMovementXYZWithFullVertical(): void + { + foreach (range(0, 36) as $i) { + $this->assertSame([0, 100, 0], Util::movementXYZ($i * 10, 90, 100), "{$i}"); + $this->assertSame([0, -100, 0], Util::movementXYZ($i * 10, -90, 100), "{$i}"); + } + } + public function testRotatePointY(): void { $data = [ @@ -189,6 +197,11 @@ protected function _testWorldAngleUsingMovement(Point $start, float $h, float $v public function testWorldAngle(): void { + $this->assertSame([0.0, 0.0], Util::worldAngle(new Point(10, 10, 20), new Point(10, 10, 10))); + $this->assertSame([180.0, 0.0], Util::worldAngle(new Point(10, 10, 20), new Point(10, 10, 30))); + $this->assertSame([90.0, 0.0], Util::worldAngle(new Point(11, 10, 20), new Point(10, 10, 20))); + $this->assertSame([270.0, 0.0], Util::worldAngle(new Point(9, 10, 20), new Point(10, 10, 20))); + $this->assertSame([null, -90.0], Util::worldAngle(new Point(), new Point(0, 10, 0))); $this->assertSame([null, 90.0], Util::worldAngle(new Point(), new Point(0, -10, 0))); $this->assertSame([Util::normalizeAngle(-90.0), 0.0], Util::worldAngle(new Point(), new Point(10, 0, 0))); @@ -196,14 +209,12 @@ public function testWorldAngle(): void $this->assertSame([0.0, 0.0], Util::worldAngle(new Point(), new Point(0, 0, -10))); $this->assertNotSame([180.0, -90.0], Util::worldAngle(new Point(829, 773, 10), new Point(829, 940, 145))); $this->assertSame([90.0, 0.0], Util::worldAngle(new Point(10, 0, 0))); - $this->assertSame([null, null], Util::worldAngle(new Point(10, 2, 6), new Point(10, 2, 6))); $this->assertSame([null, 90.0], Util::worldAngle(new Point(10, 4, 6), new Point(10, 2, 6))); $this->assertSame([90.0, 0.0], Util::worldAngle(new Point(10, 0, 0))); $this->assertSame([0.0, 0.0], Util::worldAngle(new Point(0, 0, 10))); $this->assertSame([45.0, 0.0], Util::worldAngle(new Point(5, 0, 5))); - $this->assertSame([null, null], Util::worldAngle(new Point(10, 2, 6), new Point(10, 2, 6))); - $this->assertSame([null, 90.0], Util::worldAngle(new Point(10, 4, 6), new Point(10, 2, 6))); + $this->assertSame([null, 0.0], Util::worldAngle(new Point(10, 2, 6), new Point(10, 2, 6))); } public function testLerp(): void diff --git a/www/assets/js/Game.js b/www/assets/js/Game.js index 1ddfef0..0a19390 100644 --- a/www/assets/js/Game.js +++ b/www/assets/js/Game.js @@ -207,6 +207,7 @@ export class Game { } const item = this.#dropItems[data.extra.id]; item.rotation.set(0, 0, 0) + item.rotateOnWorldAxis(new THREE.Vector3(0, 1, 0), Math.random() * 6.28) item.position.set(data.position.x, data.position.y, -data.position.z) } if (data.type === SoundType.BOMB_DEFUSED || data.type === SoundType.BOMB_EXPLODED) { @@ -220,7 +221,6 @@ export class Game { grenade.position.set(data.position.x, data.position.y, -data.position.z) if (data.type === SoundType.GRENADE_LAND) { - grenade.rotation.set(0, 0, 0) this.#grenadeLand(data.extra.id, data.item, data.player, data.position) } }