Add win/lose game-ending flow
Removing all 3 locked files while the timer is still running now marks
the session WON immediately (checked right after every successful rm)
and broadcasts a "game_finished" signal over Mercure so every
connected player gets redirected together, not just the one who
removed the last file. A new /won/{session} route + won.html.twig
mirrors the existing lost flow (victory narrative + the same feedback
form).
The existing timer-expiry path already set LOST but always redirected
to lostUrl regardless of actual status; it now picks won/lost based on
the status the server reports.
Also fixes a pre-existing bug on the lost page (and would-be bug on
the new won page): PlayerService::GetCurrentlyActiveAsPlayer() only
matches players in READY/PLAYING sessions, so by the time a session
has ended it always returned null there, silently breaking the
feedback form. Both pages now look the player up directly via
PlayerRepository instead.
Added a navigatingAway flag so the page's "confirm before leaving"
prompt doesn't block our own win/lose redirects.
This commit is contained in:
@@ -4,6 +4,7 @@ namespace App\Game\Service;
|
||||
|
||||
use App\Game\Enum\DecodeMessage;
|
||||
use App\Game\Enum\SessionSettingType;
|
||||
use App\Game\Enum\SessionStatus;
|
||||
use App\Game\Entity\Player;
|
||||
use App\Game\Entity\Session;
|
||||
use App\Game\Entity\SessionSetting;
|
||||
@@ -230,6 +231,18 @@ class GameResponseService
|
||||
return ['result' => ['You are not allowed to remove this file.']];
|
||||
|
||||
$this->playerService->addDeletedFileToSession($player, $fullPath);
|
||||
|
||||
if ($this->checkFilesRemovalWin($player)) {
|
||||
return [
|
||||
'result' => [
|
||||
'File removed: ' . $filename,
|
||||
'MAINFRAME: All protected files purged. The AI virus has been contained. Well done, agents.',
|
||||
],
|
||||
'messageType' => 'mainframe',
|
||||
'gameWon' => true,
|
||||
];
|
||||
}
|
||||
|
||||
$filesRemovalDeadline = $this->startLockedFilesRemovalDeadline($player->getSession(), $fullPath);
|
||||
$lock = $this->triggerLock($player);
|
||||
|
||||
@@ -1030,6 +1043,39 @@ class GameResponseService
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether all locked files are currently removed and, if so, marks the session
|
||||
* as won (once) and broadcasts a "game finished" signal to every connected player.
|
||||
*/
|
||||
private function checkFilesRemovalWin(Player $player): bool
|
||||
{
|
||||
$session = $player->getSession();
|
||||
|
||||
if ($session->getStatus() !== SessionStatus::PLAYING) {
|
||||
return $session->getStatus() === SessionStatus::WON;
|
||||
}
|
||||
|
||||
$deletedFiles = $this->playerService->getDeletedFilesOfSession($player);
|
||||
$lockedFiles = $this->getLockedFiles();
|
||||
|
||||
if (count(array_intersect($lockedFiles, $deletedFiles)) < count($lockedFiles)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$session->setStatus(SessionStatus::WON);
|
||||
$this->entityManager->persist($session);
|
||||
$this->entityManager->flush();
|
||||
|
||||
$topic = '/game/hub/' . $session->getId();
|
||||
try {
|
||||
$this->hub->publish(new Update($topic, json_encode(['type' => 'game_finished', 'status' => 'won'])));
|
||||
} catch (\Exception $e) {
|
||||
// Mercure might be down
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts (if not already running) the 60-second window within which all locked files
|
||||
* must be removed, or the ones already removed get restored by the virus. Returns the
|
||||
@@ -1084,6 +1130,10 @@ class GameResponseService
|
||||
*/
|
||||
private function enforceLockedFilesRemovalDeadline(Session $session): void
|
||||
{
|
||||
if ($session->getStatus() !== SessionStatus::PLAYING) {
|
||||
return;
|
||||
}
|
||||
|
||||
$setting = $this->sessionSettingRepository->getSetting($session, SessionSettingType::LOCKED_FILES_REMOVAL_DEADLINE);
|
||||
if (!$setting || !$setting->getValue()) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user