feat: add four per-verb match action handlers

This commit is contained in:
Keith Solomon
2026-07-26 01:05:14 -05:00
parent 47dc95fa56
commit df8d4023ba
5 changed files with 567 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace BattleForge\Http\Handlers;
use BattleForge\Domain\CombatEngine;
use BattleForge\Domain\CombatException;
use BattleForge\Domain\Position;
use BattleForge\Http\MatchActionSupport;
use BattleForge\Http\Request;
use BattleForge\Http\Response;
final class PostMatchAbility
{
public function __construct(private readonly string $secret)
{
}
/** @param array<string, string> $params */
public function handle(Request $request, array $params): Response
{
$support = new MatchActionSupport($this->secret);
$result = $support->verify($request);
if ($result->response !== null) {
return $result->response;
}
$matchId = $result->matchIdAndMatch['matchId'];
$match = $result->matchIdAndMatch['match'];
$payload = json_decode($request->rawBody, true);
$unitId = (string) ($payload['unitId'] ?? '');
$abilityId = (string) ($payload['abilityId'] ?? '');
$x = (int) ($payload['x'] ?? -1);
$y = (int) ($payload['y'] ?? -1);
try {
$next = (new CombatEngine())->useAbility($match, $unitId, $abilityId, new Position($x, $y));
} catch (CombatException $exception) {
return PostMatchMove::rejectionResponse($this->secret, $matchId, $match, $exception->getMessage());
}
return Response::json(200, PostMatchMove::successResponse($this->secret, $matchId, $next));
}
}
+42
View File
@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace BattleForge\Http\Handlers;
use BattleForge\Domain\CombatEngine;
use BattleForge\Domain\CombatException;
use BattleForge\Http\MatchActionSupport;
use BattleForge\Http\Request;
use BattleForge\Http\Response;
final class PostMatchAttack
{
public function __construct(private readonly string $secret)
{
}
/** @param array<string, string> $params */
public function handle(Request $request, array $params): Response
{
$support = new MatchActionSupport($this->secret);
$result = $support->verify($request);
if ($result->response !== null) {
return $result->response;
}
$matchId = $result->matchIdAndMatch['matchId'];
$match = $result->matchIdAndMatch['match'];
$payload = json_decode($request->rawBody, true);
$attackerId = (string) ($payload['attackerId'] ?? '');
$targetId = (string) ($payload['targetId'] ?? '');
try {
$next = (new CombatEngine())->attack($match, $attackerId, $targetId);
} catch (CombatException $exception) {
return PostMatchMove::rejectionResponse($this->secret, $matchId, $match, $exception->getMessage());
}
return Response::json(200, PostMatchMove::successResponse($this->secret, $matchId, $next));
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace BattleForge\Http\Handlers;
use BattleForge\Domain\CombatEngine;
use BattleForge\Domain\CombatException;
use BattleForge\Http\MatchActionSupport;
use BattleForge\Http\Request;
use BattleForge\Http\Response;
final class PostMatchEndTurn
{
public function __construct(private readonly string $secret)
{
}
/** @param array<string, string> $params */
public function handle(Request $request, array $params): Response
{
$support = new MatchActionSupport($this->secret);
$result = $support->verify($request);
if ($result->response !== null) {
return $result->response;
}
$matchId = $result->matchIdAndMatch['matchId'];
$match = $result->matchIdAndMatch['match'];
try {
$next = (new CombatEngine())->endTurn($match);
} catch (CombatException $exception) {
return PostMatchMove::rejectionResponse($this->secret, $matchId, $match, $exception->getMessage());
}
return Response::json(200, PostMatchMove::successResponse($this->secret, $matchId, $next));
}
}
+76
View File
@@ -0,0 +1,76 @@
<?php
declare(strict_types=1);
namespace BattleForge\Http\Handlers;
use BattleForge\Application\ScenarioSerializer;
use BattleForge\Application\TurnToken;
use BattleForge\Domain\CombatEngine;
use BattleForge\Domain\CombatException;
use BattleForge\Domain\Position;
use BattleForge\Http\MatchActionSupport;
use BattleForge\Http\Request;
use BattleForge\Http\Response;
final class PostMatchMove
{
public function __construct(private readonly string $secret)
{
}
/** @param array<string, string> $params */
public function handle(Request $request, array $params): Response
{
$support = new MatchActionSupport($this->secret);
$result = $support->verify($request);
if ($result->response !== null) {
return $result->response;
}
$matchId = $result->matchIdAndMatch['matchId'];
$match = $result->matchIdAndMatch['match'];
$payload = json_decode($request->rawBody, true);
$unitId = (string) ($payload['unitId'] ?? '');
$x = (int) ($payload['x'] ?? -1);
$y = (int) ($payload['y'] ?? -1);
try {
$next = (new CombatEngine())->move($match, $unitId, new Position($x, $y));
} catch (CombatException $exception) {
return self::rejectionResponse($this->secret, $matchId, $match, $exception->getMessage());
}
return Response::json(200, self::successResponse($this->secret, $matchId, $next));
}
/** @return array<string, mixed> */
public static function successResponse(
string $secret,
string $matchId,
\BattleForge\Domain\MatchState $next
): array {
$turnToken = TurnToken::issue($secret, $matchId, $next->activeTeamId, $next->round, count($next->actionLog));
return [
'match' => ScenarioSerializer::matchToArray($next),
'turnToken' => $turnToken,
'winnerTeamId' => $next->winnerTeamId,
];
}
public static function rejectionResponse(
string $secret,
string $matchId,
\BattleForge\Domain\MatchState $match,
string $reason
): Response {
$turnToken = TurnToken::issue($secret, $matchId, $match->activeTeamId, $match->round, count($match->actionLog));
return Response::json(409, [
'error' => $reason,
'match' => ScenarioSerializer::matchToArray($match),
'turnToken' => $turnToken,
]);
}
}