This commit is contained in:
Daniel Maixner 2022-10-02 14:31:55 +02:00
parent 1c8b72c7b5
commit 580bdc18c2
10 changed files with 76 additions and 43 deletions

View File

@ -47,6 +47,8 @@ class Game
$this->world = new World($this);
$this->score = new Score();
$this->properties = $properties;
$this->initialize();
}
private function initialize(): void
@ -71,9 +73,6 @@ class Game
$this->tickEvents = [$this->gameOver];
return $this->gameOver;
}
if ($tickId === 0) {
$this->initialize();
}
$alivePlayers = [0, 0];
foreach ($this->players as $player) {

View File

@ -9,9 +9,9 @@ use cs\Weapon\Knife;
class HitBox implements Hittable
{
private int $moneyAward = 0;
private bool $playerWasKilled = false;
private bool $wasHeadShot = false;
private int $moneyAward;
private bool $playerWasKilled;
private bool $wasHeadShot;
public function __construct(
private Player $player,
@ -19,6 +19,7 @@ class HitBox implements Hittable
private HitIntersect $geometry
)
{
$this->reset();
}
public function reset(): void
@ -51,7 +52,7 @@ class HitBox implements Hittable
return false;
}
// TODO do back hit box based on player and bullet angle
// TODO do back hit box based on player and bullet angle, or add HitBoxBack geometry
return false;
}

View File

@ -16,24 +16,29 @@ final class Player
use PlayerTrait\MovementTrait;
use PlayerTrait\InventoryTrait;
// Better to use even numbers
// NOTE: Better to use even numbers for all constants
// TODO: migrate to time based values, so we can play on different tick settings, and expand simulation tests, maybe some player setting object
public const speedFall = 60;
public const speedMove = 50; // TODO: linear movement or ease-in-out?
public const speedMove = 50;
public const speedJump = 30;
public const speedMoveWalk = 40;
public const speedMoveCrouch = 30;
public const speedJump = 30;
public const tickCountJump = 5;
public const tickCountCrouch = 10;
public const headRadius = 30;
public const bodyRadius = 44;
public const jumpHeight = 150;
public const headHeightStand = 190;
public const gunHeightStand = self::headHeightStand - self::headRadius;
public const headHeightCrouch = 140;
public const boxHeightCrouchCover = self::headHeightCrouch + 2;
public const obstacleOvercomeHeight = 20;
public const playerBoundingRadius = self::bodyRadius;
public const fallDamageThreshold = 3 * self::headHeightStand;
public const jumpHeight = self::speedJump * self::tickCountJump;
public const headRadius = 30;
public const bodyRadius = 44;
public const boxHeightCrouchCover = self::headHeightCrouch + 2;
public const gunHeightStand = self::headHeightStand - self::headRadius;
/** @deprecated make it private eventually */
public int $playerBoundingRadius = self::playerBoundingRadius;
public const jumpMovementSlowDown = 1;
public const flyingMovementSlowDown = 0.8;
@ -47,11 +52,10 @@ final class Player
/** @var Event[] */
private array $events = [];
private bool $isAttacking = false;
private int $health = 100;
private int $health;
private int $armor = 0;
private int $headHeight = self::headHeightStand; // highest player point
public int $playerBoundingRadius = self::playerBoundingRadius;
private int $headHeight; // highest player point
private bool $isAttacking = false;
// Event IDs, sequence, ascending order priority
private int $eventIdPrimary = 0;
@ -77,6 +81,11 @@ final class Player
private function initialize(): void
{
$this->health = 100;
$this->isWalking = false;
$this->headHeight = self::headHeightStand;
$this->events = [];
$this->addEvent($this->createMovementEvent(), $this->eventIdMovement);
$this->addEvent($this->createGravityEvent(), $this->eventIdGravity);
}
@ -99,6 +108,11 @@ final class Player
$this->isAttacking = false;
}
private function onPlayerDied(): void
{
$this->armor = 0;
}
private function addEvent(Event $event, int $eventId): void
{
$this->events[$eventId] = $event;
@ -118,7 +132,7 @@ final class Player
public function suicide(): void
{
$this->health = 0;
$this->lowerHealth($this->health);
$this->world->playerDiedToFallDamage($this);
}
@ -175,6 +189,9 @@ final class Player
public function lowerHealth(int $healthDamage): void
{
$this->health -= abs($healthDamage);
if ($this->health <= 0) {
$this->onPlayerDied();
}
}
public function getHealth(): int
@ -192,11 +209,6 @@ final class Player
$this->resetTickStates();
$this->getSight()->reset();
$this->inventory->reset($this->isPlayingOnAttackerSide, !$this->isAlive());
$this->events = [];
$this->health = 100;
$this->isWalking = false;
$this->headHeight = self::headHeightStand;
$this->initialize();
}
@ -239,7 +251,7 @@ final class Player
"heightSight" => $this->getSightHeight(),
"heightBody" => $this->getBodyHeight(),
"height" => $this->getHeadHeight(),
"armor" => 0, //TODO
"armor" => $this->armor, //TODO
];
}

View File

@ -7,8 +7,6 @@ use cs\Map\Map;
class World
{
private const ATTACKER = 0;
private const DEFENDER = 1;
private const WALL_X = 0;
private const WALL_Z = 1;
@ -146,7 +144,7 @@ class World
throw new GameException("No map is loaded! Cannot spawn players.");
}
$key = ($isAttacker ? self::ATTACKER : self::DEFENDER);
$key = (int)$isAttacker;
if (isset($this->spawnCandidates[$key])) {
$source = $this->spawnCandidates[$key];
} else {
@ -181,24 +179,26 @@ class World
public function calculateHits(Bullet $bullet): array
{
$hits = [];
$alreadyHitPlayerIds = $bullet->getPlayerHitIds();
$alreadyHitPlayerIds[$bullet->getOriginPlayerId()] = true; // cannot shoot self
foreach ($this->playersColliders as $playerCollider) {
if (isset($alreadyHitPlayerIds[$playerCollider->getPlayerId()])) {
continue; // player already hit
continue; // player already hit or self
}
$hitBox = $playerCollider->tryHitPlayer($bullet);
if ($hitBox) {
$player = $hitBox->getPlayer();
if ($player) {
$bullet->addPlayerIdHit($player->getId());
}
if ($hitBox->playerWasKilled() && $player) {
if (!$hitBox) {
continue;
}
$hits[] = $hitBox;
$player = $hitBox->getPlayer();
if ($player) {
$bullet->addPlayerIdHit($player->getId());
if ($hitBox->playerWasKilled()) {
$this->game->playerAttackKilledEvent($player, $bullet, $hitBox->wasHeadShot());
}
$hits[] = $hitBox;
}
}
@ -233,11 +233,12 @@ class World
}
$candidatePlane = $center->to2D('zy')->addX(-$radius);
$width = 2 * $radius;
foreach (($this->walls[self::WALL_X][$center->x] ?? []) as $wall) {
if ($wall->getCeiling() === $center->y) {
continue;
}
if (Collision::planeWithPlane($wall->getPoint2DStart(), $wall->width, $wall->height, $candidatePlane, 2 * $radius, $height)) {
if (Collision::planeWithPlane($wall->getPoint2DStart(), $wall->width, $wall->height, $candidatePlane, $width, $height)) {
return $wall;
}
}
@ -252,11 +253,12 @@ class World
}
$candidatePlane = $center->to2D('xy')->addX(-$radius);
$width = 2 * $radius;
foreach (($this->walls[self::WALL_Z][$center->z] ?? []) as $wall) {
if ($wall->getCeiling() === $center->y) {
continue;
}
if (Collision::planeWithPlane($wall->getPoint2DStart(), $wall->width, $wall->height, $candidatePlane, 2 * $radius, $height)) {
if (Collision::planeWithPlane($wall->getPoint2DStart(), $wall->width, $wall->height, $candidatePlane, $width, $height)) {
return $wall;
}
}

View File

@ -43,7 +43,7 @@ final class AttackEvent
foreach ($hits as $hit) {
$bullet->lowerDamage($hit->getHitAntiForce());
$result->addHit($hit);
if ($bullet->isActive()) {
if (!$bullet->isActive()) {
break;
}
}

View File

@ -278,6 +278,9 @@ class Server
$this->logger->log($level, $msg);
}
/**
* @codeCoverageIgnore
*/
private function saveRequestMetaData(): void
{
$players = [];
@ -293,6 +296,9 @@ class Server
], JSON_THROW_ON_ERROR));
}
/**
* @codeCoverageIgnore
*/
public function storeRequests(string $path = '/tmp/cs.server.req'): void
{
$this->saveRequestsPath = $path;

View File

@ -43,6 +43,7 @@ trait AttackTrait
$this->isAttacking = true;
$origin = $this->getPositionImmutable();
$origin->addY($this->getSightHeight());
return new AttackEvent(
$this->world,
$origin,

View File

@ -31,7 +31,7 @@ class RoundTest extends BaseTestCase
$game = $this->createGame();
$break = false;
$game->onEvents(function (array $e) use (&$events, &$break): void {
if ($e === [] || $break) {
if ($break) {
return;
}
if ($e[0] instanceof GameOverEvent) {

View File

@ -75,4 +75,14 @@ class HitBoxTest extends BaseTest
$this->assertPositionSame(new Point(-10, -8, 67), $sphere->calculateWorldCoordinate($player, new Point(15, -20, 8)));
}
public function testSphereHitBoxIntersect(): void
{
$sphere = new SphereHitBox(new Point(-45, 12, 32), 38);
$player = new Player(1, Color::GREEN, true);
$this->assertFalse($sphere->intersect($player, new Point(-10, -8, 67)));
$player->getSight()->lookHorizontal(20);
$this->assertTrue($sphere->intersect($player, new Point(-10, -8, 67)));
}
}

View File

@ -63,13 +63,15 @@ class PlayerKillTest extends BaseTestCase
$player1->getInventory()->earnMoney(1000);
$player1->buyItem(BuyMenuItem::KEVLAR_BODY_AND_HEAD);
$this->assertSame(ArmorType::BODY_AND_HEAD, $player1->getArmorType());
$player2->setPosition($player1->getPositionImmutable()->addY($player1->getHeadHeight() + 10));
$player2Position = $player1->getPositionImmutable()->addY($player1->getHeadHeight() + 10);
$player2->setPosition($player2Position);
$player2->getSight()->lookAt(0, -90);
$result = $player2->attack();
$this->assertNotNull($result);
$gun = $player2->getEquippedItem();
$this->assertInstanceOf(PistolUsp::class, $gun);
$this->assertSame($player2Position->y + $player2->getSightHeight(), $result->getBullet()->getDistanceTraveled());
$hits = $result->getHits();
$this->assertCount(2, $hits);