feat: track archetype, abilities, and buff on unit state
This commit is contained in:
@@ -132,10 +132,109 @@ final class UnitStateTest extends TestCase
|
||||
self::assertSame($unit, $unit->startTurn());
|
||||
}
|
||||
|
||||
public function testItCarriesArchetypeAbilitiesAndBonus(): void
|
||||
{
|
||||
$unit = new UnitState(
|
||||
id: 'unit-1',
|
||||
teamId: 'alpha',
|
||||
position: new Position(0, 0),
|
||||
maxHealth: 10,
|
||||
health: 10,
|
||||
attack: 4,
|
||||
defense: 2,
|
||||
speed: 4,
|
||||
actionsRemaining: 2,
|
||||
hasAttacked: false,
|
||||
archetype: \BattleForge\Domain\Archetype::Support,
|
||||
abilities: ['heal', 'buff'],
|
||||
attackBonus: 2,
|
||||
hasUsedAbility: false,
|
||||
);
|
||||
|
||||
self::assertSame(\BattleForge\Domain\Archetype::Support, $unit->archetype);
|
||||
self::assertSame(['heal', 'buff'], $unit->abilities);
|
||||
self::assertSame(2, $unit->attackBonus);
|
||||
self::assertFalse($unit->hasUsedAbility);
|
||||
}
|
||||
|
||||
public function testItRejectsEmptyStringAbilities(): void
|
||||
{
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Unit abilities must be non-empty strings.');
|
||||
|
||||
new UnitState(
|
||||
id: 'unit-1',
|
||||
teamId: 'alpha',
|
||||
position: new Position(0, 0),
|
||||
maxHealth: 10,
|
||||
health: 10,
|
||||
attack: 4,
|
||||
defense: 2,
|
||||
speed: 4,
|
||||
actionsRemaining: 2,
|
||||
hasAttacked: false,
|
||||
archetype: \BattleForge\Domain\Archetype::Defender,
|
||||
abilities: [''],
|
||||
);
|
||||
}
|
||||
|
||||
public function testItExposesAllowedAbilitiesFromTheArchetypeCatalog(): void
|
||||
{
|
||||
$unit = $this->unit();
|
||||
|
||||
self::assertSame(
|
||||
\BattleForge\Domain\ArchetypeCatalog::templates()[$unit->archetype->value]->allowedAbilities,
|
||||
$unit->allowedAbilities(),
|
||||
);
|
||||
}
|
||||
|
||||
public function testWithAttackBonusReturnsAChangedCopyAndRejectsNegativeBonus(): void
|
||||
{
|
||||
$unit = $this->unit();
|
||||
$buffed = $unit->withAttackBonus(3);
|
||||
|
||||
self::assertNotSame($unit, $buffed);
|
||||
self::assertSame(0, $unit->attackBonus);
|
||||
self::assertSame(3, $buffed->attackBonus);
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$unit->withAttackBonus(-1);
|
||||
}
|
||||
|
||||
public function testSpendAbilityMarksAndConsumesOneActionButNotTwo(): void
|
||||
{
|
||||
$unit = $this->unit();
|
||||
$spent = $unit->spendAbility();
|
||||
|
||||
self::assertSame(1, $spent->actionsRemaining);
|
||||
self::assertTrue($spent->hasUsedAbility);
|
||||
}
|
||||
|
||||
public function testSpendAbilityRejectsAUnitThatAlreadyUsedAnAbility(): void
|
||||
{
|
||||
$unit = $this->unit(hasUsedAbility: true);
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$unit->spendAbility();
|
||||
}
|
||||
|
||||
public function testStartTurnClearsAttackBonusAndAbilityUse(): void
|
||||
{
|
||||
$unit = $this->unit(actionsRemaining: 0, hasAttacked: true, attackBonus: 3, hasUsedAbility: true);
|
||||
$started = $unit->startTurn();
|
||||
|
||||
self::assertSame(2, $started->actionsRemaining);
|
||||
self::assertFalse($started->hasAttacked);
|
||||
self::assertSame(0, $started->attackBonus);
|
||||
self::assertFalse($started->hasUsedAbility);
|
||||
}
|
||||
|
||||
private function unit(
|
||||
int $health = 10,
|
||||
int $actionsRemaining = 2,
|
||||
bool $hasAttacked = false,
|
||||
int $attackBonus = 0,
|
||||
bool $hasUsedAbility = false,
|
||||
): UnitState {
|
||||
return new UnitState(
|
||||
'unit-1',
|
||||
@@ -148,6 +247,10 @@ final class UnitStateTest extends TestCase
|
||||
4,
|
||||
$actionsRemaining,
|
||||
$hasAttacked,
|
||||
archetype: \BattleForge\Domain\Archetype::Defender,
|
||||
abilities: [],
|
||||
attackBonus: $attackBonus,
|
||||
hasUsedAbility: $hasUsedAbility,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user