feat: resolve objective control and objective victory
This commit is contained in:
+21
-16
@@ -180,12 +180,7 @@ final class CombatEngine
|
|||||||
actionLog: [...$match->actionLog, "{$endingTeamId} ended turn"],
|
actionLog: [...$match->actionLog, "{$endingTeamId} ended turn"],
|
||||||
);
|
);
|
||||||
|
|
||||||
$winner = $this->resolveObjectiveVictory($next);
|
return $this->resolveObjectiveVictory($next, $endingTeamId === $teamIds[0]);
|
||||||
if ($winner !== null) {
|
|
||||||
$next = $next->withWinner($winner);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -291,29 +286,39 @@ final class CombatEngine
|
|||||||
return $match->withWinner($actingTeamId);
|
return $match->withWinner($actingTeamId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function resolveObjectiveVictory(MatchState $match): ?string
|
/**
|
||||||
|
* @param bool $endingTeamIsFirstInOrder true when the team that just ended is the first team in
|
||||||
|
* sorted team order; this identifies the first half of a round.
|
||||||
|
*/
|
||||||
|
private function resolveObjectiveVictory(MatchState $match, bool $endingTeamIsFirstInOrder): MatchState
|
||||||
{
|
{
|
||||||
if ($match->victoryCondition !== VictoryCondition::HoldObjective || $match->objectives === []) {
|
if ($match->victoryCondition !== VictoryCondition::HoldObjective || $match->objectives === []) {
|
||||||
return null;
|
return $match;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$next = $match;
|
||||||
|
|
||||||
|
if ($endingTeamIsFirstInOrder) {
|
||||||
/** @var list<ObjectiveMarker> $objectiveList */
|
/** @var list<ObjectiveMarker> $objectiveList */
|
||||||
$objectiveList = array_values($match->objectives);
|
$objectiveList = array_values($match->objectives);
|
||||||
$objective = $objectiveList[0];
|
$objective = $objectiveList[0];
|
||||||
$controller = $this->objectiveController($match, $objective->position);
|
$controller = $this->objectiveController($match, $objective->position);
|
||||||
|
|
||||||
if ($controller === null) {
|
if ($controller !== null) {
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$control = (new ObjectiveControl($match->objectiveControl))->recordRound($controller);
|
$control = (new ObjectiveControl($match->objectiveControl))->recordRound($controller);
|
||||||
$next = $match->withObjectiveControl($control->roundsByTeam);
|
$next = $match->withObjectiveControl($control->roundsByTeam);
|
||||||
|
|
||||||
if (($control->roundsByTeam[$controller] ?? 0) >= $next->holdRoundsRequired) {
|
|
||||||
return $controller;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return $next;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($match->objectiveControl as $teamId => $count) {
|
||||||
|
if ($count >= $match->holdRoundsRequired) {
|
||||||
|
return $next->withWinner($teamId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function objectiveController(MatchState $match, Position $tile): ?string
|
private function objectiveController(MatchState $match, Position $tile): ?string
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ use BattleForge\Domain\Battlefield;
|
|||||||
use BattleForge\Domain\CombatEngine;
|
use BattleForge\Domain\CombatEngine;
|
||||||
use BattleForge\Domain\CombatException;
|
use BattleForge\Domain\CombatException;
|
||||||
use BattleForge\Domain\MatchState;
|
use BattleForge\Domain\MatchState;
|
||||||
|
use BattleForge\Domain\ObjectiveMarker;
|
||||||
use BattleForge\Domain\Position;
|
use BattleForge\Domain\Position;
|
||||||
use BattleForge\Domain\Terrain;
|
use BattleForge\Domain\Terrain;
|
||||||
use BattleForge\Domain\UnitState;
|
use BattleForge\Domain\UnitState;
|
||||||
|
use BattleForge\Domain\VictoryCondition;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use PHPUnit\Framework\Attributes\DataProvider;
|
use PHPUnit\Framework\Attributes\DataProvider;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
@@ -973,4 +975,65 @@ final class CombatEngineTest extends TestCase
|
|||||||
|
|
||||||
(new CombatEngine())->useAbility($match, 'scout-1', 'heal', new Position(0, 1));
|
(new CombatEngine())->useAbility($match, 'scout-1', 'heal', new Position(0, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testEndTurnTalliesObjectiveControlAfterAFullRound(): void
|
||||||
|
{
|
||||||
|
$alpha = $this->unit('alpha-1', 'alpha', new Position(4, 4));
|
||||||
|
$bravo = $this->unit('bravo-1', 'bravo', new Position(7, 7));
|
||||||
|
$match = new MatchState(
|
||||||
|
new Battlefield(8, 8),
|
||||||
|
[$alpha, $bravo],
|
||||||
|
'alpha',
|
||||||
|
objectives: ['objective-1' => new ObjectiveMarker('objective-1', new Position(4, 4))],
|
||||||
|
victoryCondition: VictoryCondition::HoldObjective,
|
||||||
|
holdRoundsRequired: 2,
|
||||||
|
);
|
||||||
|
|
||||||
|
$afterAlphaEnd = (new CombatEngine())->endTurn($match);
|
||||||
|
$afterBravoEnd = (new CombatEngine())->endTurn($afterAlphaEnd);
|
||||||
|
|
||||||
|
self::assertSame(['alpha' => 1], $afterAlphaEnd->objectiveControl);
|
||||||
|
self::assertSame(['alpha' => 1], $afterBravoEnd->objectiveControl);
|
||||||
|
self::assertNull($afterBravoEnd->winnerTeamId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEndTurnDeclaresWinnerWhenHoldRoundsReached(): void
|
||||||
|
{
|
||||||
|
$alpha = $this->unit('alpha-1', 'alpha', new Position(4, 4));
|
||||||
|
$bravo = $this->unit('bravo-1', 'bravo', new Position(7, 7));
|
||||||
|
$match = new MatchState(
|
||||||
|
new Battlefield(8, 8),
|
||||||
|
[$alpha, $bravo],
|
||||||
|
'alpha',
|
||||||
|
objectives: ['objective-1' => new ObjectiveMarker('objective-1', new Position(4, 4))],
|
||||||
|
victoryCondition: VictoryCondition::HoldObjective,
|
||||||
|
holdRoundsRequired: 1,
|
||||||
|
objectiveControl: ['alpha' => 0],
|
||||||
|
);
|
||||||
|
|
||||||
|
$afterAlphaEnd = (new CombatEngine())->endTurn($match);
|
||||||
|
$afterBravoEnd = (new CombatEngine())->endTurn($afterAlphaEnd);
|
||||||
|
|
||||||
|
self::assertSame('alpha', $afterBravoEnd->winnerTeamId);
|
||||||
|
self::assertSame(['alpha' => 1], $afterBravoEnd->objectiveControl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEndTurnDoesNotAwardControlWhenNoTeamStandsOnTheObjective(): void
|
||||||
|
{
|
||||||
|
$alpha = $this->unit('alpha-1', 'alpha', new Position(0, 0));
|
||||||
|
$bravo = $this->unit('bravo-1', 'bravo', new Position(7, 7));
|
||||||
|
$match = new MatchState(
|
||||||
|
new Battlefield(8, 8),
|
||||||
|
[$alpha, $bravo],
|
||||||
|
'alpha',
|
||||||
|
objectives: ['objective-1' => new ObjectiveMarker('objective-1', new Position(4, 4))],
|
||||||
|
victoryCondition: VictoryCondition::HoldObjective,
|
||||||
|
holdRoundsRequired: 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
$next = (new CombatEngine())->endTurn($match);
|
||||||
|
|
||||||
|
self::assertSame([], $next->objectiveControl);
|
||||||
|
self::assertNull($next->winnerTeamId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user