Files
2026-09-07 23:02:47 +02:00

176 lines
6.0 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Unit\Game;
use App\Game\Entities\Zombie;
use App\Game\InputState;
use App\Game\Player;
use App\Game\Room;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
final class PlayerTest extends TestCase
{
private function makePlayer(?Room $room = null): Player
{
$room = $room ?? new Room('test');
$input = new InputState;
return new Player('p1', 'Tester', $input, $room->entities, $room, [
'x' => 100.0,
'y' => 100.0,
'health' => 3,
]);
}
#[Test]
public function movement_keys_affect_velocity(): void
{
$room = new Room('test');
$input = new InputState(['d']);
$player = new Player('p1', 'Tester', $input, $room->entities, $room, ['x' => 100.0, 'y' => 100.0]);
$player->update(1.0 / 60.0);
$this->assertGreaterThan(100.0, $player->pos->x);
}
#[Test]
public function fire_spawns_bullets(): void
{
$room = new Room('test');
$input = new InputState(fire: true);
$player = new Player('p1', 'Tester', $input, $room->entities, $room, ['x' => 100.0, 'y' => 100.0]);
$room->addPlayer($player);
$player->update(1.0 / 60.0);
$bullets = $room->entities->findByKind('bullet');
$this->assertNotEmpty($bullets);
}
#[Test]
public function auto_aim_fires_at_nearest_zombie_without_mouse(): void
{
$room = new Room('test');
$room->state = Room::STATE_PLAYING;
$input = new InputState(fire: false);
$player = new Player('p1', 'Tester', $input, $room->entities, $room, ['x' => 100.0, 'y' => 100.0]);
$room->addPlayer($player);
$zombie = new Zombie(300.0, 100.0, $room->entities, $room, ['emergeTime' => 0.0]);
$zombie->target = $player;
$room->entities->append($zombie);
$player->update(1.0 / 60.0);
$bullets = $room->entities->findByKind('bullet');
$this->assertNotEmpty($bullets, 'auto-aim should fire at a zombie without the mouse held');
$direction = $bullets[0]->velocity->normalize();
$this->assertGreaterThan(0.9, $direction->x, 'bullet should fly toward the zombie to the right');
}
#[Test]
public function auto_aim_does_not_fire_without_target(): void
{
$room = new Room('test');
$input = new InputState(fire: false);
$player = new Player('p1', 'Tester', $input, $room->entities, $room, ['x' => 100.0, 'y' => 100.0]);
$room->addPlayer($player);
$player->update(1.0 / 60.0);
$this->assertEmpty($room->entities->findByKind('bullet'), 'no auto-fire when no zombie is in range');
}
#[Test]
public function take_damage_decreases_health_and_cooldown_prevents_double_hit(): void
{
$room = new Room('test');
$player = $this->makePlayer($room);
$player->takeDamage();
$this->assertSame(2, $player->health);
$this->assertFalse($player->dead);
$player->takeDamage();
$this->assertSame(2, $player->health, 'damage cooldown prevented second hit');
}
#[Test]
public function take_damage_kills_at_zero_health(): void
{
$room = new Room('test');
$player = $this->makePlayer($room);
$player->takeDamage();
$player->update(0.6);
$player->takeDamage();
$player->update(0.6);
$player->takeDamage();
$this->assertSame(0, $player->health);
$this->assertTrue($player->dead);
}
#[Test]
public function apply_upgrade_increases_level_and_effects(): void
{
$room = new Room('test');
$player = $this->makePlayer($room);
$originalFireRate = $player->fireRate;
$this->assertTrue($player->applyUpgrade('rapid'));
$this->assertSame(1, $player->upgrades['rapid']);
$this->assertLessThan($originalFireRate, $player->fireRate);
}
#[Test]
public function apply_upgrade_returns_false_when_maxed(): void
{
$room = new Room('test');
$player = $this->makePlayer($room);
for ($i = 0; $i < $player->maxUpgradeLevel; $i++) {
$this->assertTrue($player->applyUpgrade('damage'));
}
$this->assertFalse($player->applyUpgrade('damage'));
$this->assertSame($player->maxUpgradeLevel, $player->upgrades['damage']);
}
#[Test]
public function apply_upgrade_unknown_type_returns_false(): void
{
$player = $this->makePlayer();
$this->assertFalse($player->applyUpgrade('nonexistent'));
}
#[Test]
public function reset_for_round_restores_derived_stats(): void
{
$room = new Room('test');
$input = new InputState;
$player = new Player('p1', 'Tester', $input, $room->entities, $room, [
'x' => 100.0,
'y' => 100.0,
'fireRate' => 0.5,
'damage' => 2,
'bulletCount' => 3,
'bulletSpeed' => 900.0,
]);
$player->applyUpgrade('rapid');
$player->applyUpgrade('damage');
$player->applyUpgrade('multishot');
$player->applyUpgrade('speed');
$player->resetForRound();
$this->assertSame(['rapid' => 0, 'damage' => 0, 'multishot' => 0, 'speed' => 0], $player->upgrades);
$this->assertSame(0.5, $player->fireRate);
$this->assertSame(2, $player->damage);
$this->assertSame(3, $player->bulletCount);
$this->assertSame(900.0, $player->bulletSpeed);
}
#[Test]
public function auto_aim_tracks_nearest_zombie(): void
{
$room = new Room('test');
$input = new InputState(fire: true);
$player = new Player('p1', 'Tester', $input, $room->entities, $room, ['x' => 100.0, 'y' => 100.0]);
$room->addPlayer($player);
$zombie = new Zombie(200.0, 100.0, $room->entities, $room, ['emergeTime' => 0.0]);
$room->entities->append($zombie);
$player->update(1.0 / 60.0);
$bullets = $room->entities->findByKind('bullet');
$this->assertNotEmpty($bullets);
}
}