137 lines
3.1 KiB
PHP
137 lines
3.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Game;
|
|
|
|
final class Room
|
|
{
|
|
public const STATE_LOBBY = 'lobby';
|
|
|
|
public const STATE_PLAYING = 'playing';
|
|
|
|
public const STATE_OVER = 'over';
|
|
|
|
public float $width = 1920.0;
|
|
|
|
public float $height = 1080.0;
|
|
|
|
public EntityList $entities;
|
|
|
|
/** @var array<string, Player> playerId => Player */
|
|
public array $players = [];
|
|
|
|
public string $state = self::STATE_LOBBY;
|
|
|
|
/** Player id of the room's host (the first player to join). */
|
|
public string $hostId = '';
|
|
|
|
public float $elapsed = 0.0;
|
|
|
|
public float $nextBossScore = 50.0;
|
|
|
|
public int $bossCount = 0;
|
|
|
|
public float $bossCooldown = 0.0;
|
|
|
|
public float $spawnTimer = 0.0;
|
|
|
|
public float $pickupTimer = 8.0;
|
|
|
|
/** @var array<int, array<string, mixed>> Queue of one-shot event descriptors for clients. */
|
|
public array $events = [];
|
|
|
|
public int $seq = 0;
|
|
|
|
public function __construct(
|
|
public readonly string $id,
|
|
public readonly int $maxPlayers = 8,
|
|
) {
|
|
$this->entities = new EntityList;
|
|
}
|
|
|
|
public function addPlayer(Player $player): void
|
|
{
|
|
$this->players[$player->id] = $player;
|
|
$this->entities->append($player);
|
|
}
|
|
|
|
public function removePlayer(Player $player): void
|
|
{
|
|
unset($this->players[$player->id]);
|
|
$this->entities->remove($player);
|
|
}
|
|
|
|
/** Reset round state so the room can be played again (host-initiated). */
|
|
public function reset(): void
|
|
{
|
|
$this->state = self::STATE_LOBBY;
|
|
$this->elapsed = 0.0;
|
|
$this->nextBossScore = 50.0;
|
|
$this->bossCount = 0;
|
|
$this->bossCooldown = 0.0;
|
|
$this->spawnTimer = 0.0;
|
|
$this->pickupTimer = 8.0;
|
|
$this->events = [];
|
|
$this->seq++;
|
|
|
|
foreach ($this->entities->copy() as $entity) {
|
|
if ($entity instanceof Player) {
|
|
continue;
|
|
}
|
|
$this->entities->remove($entity);
|
|
}
|
|
|
|
foreach ($this->players as $player) {
|
|
$player->resetForRound();
|
|
}
|
|
}
|
|
|
|
/** @return array<int, Player> */
|
|
public function alivePlayers(): array
|
|
{
|
|
$alive = [];
|
|
foreach ($this->players as $player) {
|
|
if (! $player->dead) {
|
|
$alive[] = $player;
|
|
}
|
|
}
|
|
|
|
return $alive;
|
|
}
|
|
|
|
public function highestScore(): int
|
|
{
|
|
$best = 0;
|
|
foreach ($this->players as $player) {
|
|
$best = max($best, $player->score);
|
|
}
|
|
|
|
return $best;
|
|
}
|
|
|
|
public function hasBoss(): bool
|
|
{
|
|
foreach ($this->entities as $entity) {
|
|
if ($entity instanceof Entities\Boss) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function pushEvent(string $kind, array $data = []): void
|
|
{
|
|
$this->events[] = ['k' => $kind, 'x' => $data['x'] ?? 0.0, 'y' => $data['y'] ?? 0.0, 'd' => $data];
|
|
}
|
|
|
|
public function flushEvents(): array
|
|
{
|
|
$events = $this->events;
|
|
$this->events = [];
|
|
|
|
return $events;
|
|
}
|
|
}
|