continue-puzzle-progress #6

Merged
Frank merged 3 commits from continue-puzzle-progress into main 2026-01-07 16:50:37 +00:00
5 changed files with 145 additions and 12 deletions
Showing only changes of commit 78b570bd75 - Show all commits

View File

@@ -1,6 +1,9 @@
/* Game1 entry point built with Webpack Encore */ /* Game1 entry point built with Webpack Encore */
import './styles/game1.css'; import './styles/game1.css';
let sequenceFinished = false;
let stillPlayingSound = true;
function subscribeToMercure(mercurePublicUrl, topic) { function subscribeToMercure(mercurePublicUrl, topic) {
try { try {
const url = mercurePublicUrl + '?topic=' + encodeURIComponent(topic); const url = mercurePublicUrl + '?topic=' + encodeURIComponent(topic);
@@ -10,6 +13,25 @@ function subscribeToMercure(mercurePublicUrl, topic) {
try { try {
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
console.log('[Mercure][game1] Update:', data); console.log('[Mercure][game1] Update:', data);
// data is [sendTo, message]
if (Array.isArray(data) && data.length >= 2) {
const messageContainer = document.getElementById('message-container');
if (messageContainer) {
const msgEl = document.createElement('div');
msgEl.className = 'message';
msgEl.textContent = data[1];
msgEl.style.color = '#0F0'; // Green for incoming messages
msgEl.style.marginBottom = '10px';
messageContainer.appendChild(msgEl);
if(stillPlayingSound)
playSound();
console.log('[Mercure][game1] sequenceFinished status:', sequenceFinished);
if (sequenceFinished) {
flashRed();
}
}
}
} catch (e) { } catch (e) {
console.log('[Mercure][game1] Raw event:', event.data); console.log('[Mercure][game1] Raw event:', event.data);
} }
@@ -25,6 +47,28 @@ function subscribeToMercure(mercurePublicUrl, topic) {
} }
} }
function playSound() {
const sound = document.getElementById('message-sound');
if (sound) {
sound.currentTime = 0;
sound.play().catch(e => console.warn('[Audio] Playback failed:', e));
}
}
function flashRed() {
console.log('[Game1] Triggering flashRed');
const body = document.body;
body.classList.remove('flash-red');
void body.offsetWidth; // Trigger reflow to restart animation
body.classList.add('flash-red');
// Also remove it after animation finishes so it's clean for inspection
setTimeout(() => {
body.classList.remove('flash-red');
console.log('[Game1] Removed flash-red class');
}, 150);
}
async function fetchJson(url, options = {}) { async function fetchJson(url, options = {}) {
const opts = { ...options }; const opts = { ...options };
const headers = new Headers(opts.headers || {}); const headers = new Headers(opts.headers || {});
@@ -125,16 +169,26 @@ document.addEventListener('DOMContentLoaded', async () => {
const printNextMessage = () => { const printNextMessage = () => {
if (currentMessageIndex < messages.length) { if (currentMessageIndex < messages.length) {
const msg = messages[currentMessageIndex]; const msg = messages[currentMessageIndex];
const msgEl = document.createElement('div'); const msgEl = document.createElement('div');
msgEl.className = 'message';
let extraClass = '';
if(msg[2])
extraClass = msg[2];
msgEl.className = 'message ' + extraClass;
msgEl.textContent = msg[0]; msgEl.textContent = msg[0];
msgEl.style.color = '#F00';
msgEl.style.marginBottom = '10px'; msgEl.style.marginBottom = '10px';
messageContainer.appendChild(msgEl); messageContainer.appendChild(msgEl);
playSound();
currentMessageIndex++; currentMessageIndex++;
setTimeout(printNextMessage, msg[1]); setTimeout(printNextMessage, msg[1]);
if (sequenceFinished) {
flashRed();
}
} else { } else {
// After it has printed a set of messages, it has to start a timer of 2 seconds // After it has printed a set of messages, it has to start a timer of 2 seconds
console.log('[Game1] All messages printed. Starting 2s timer to expand message-container height...'); console.log('[Game1] All messages printed. Starting 2s timer to expand message-container height...');
@@ -146,6 +200,8 @@ document.addEventListener('DOMContentLoaded', async () => {
// Add event listener for Enter key // Add event listener for Enter key
inputField.addEventListener('keypress', async (e) => { inputField.addEventListener('keypress', async (e) => {
if (e.key === 'Enter') { if (e.key === 'Enter') {
stillPlayingSound = false;
sequenceFinished = false;
const message = inputField.value.trim(); const message = inputField.value.trim();
if (message && apiEchoUrl) { if (message && apiEchoUrl) {
inputField.value = ''; inputField.value = '';
@@ -163,6 +219,8 @@ document.addEventListener('DOMContentLoaded', async () => {
}); });
console.log('[Game1] message-container height changed to 400vh and input enabled'); console.log('[Game1] message-container height changed to 400vh and input enabled');
sequenceFinished = true;
console.log('[Game1] sequenceFinished is now TRUE');
}, 2000); }, 2000);
} }
}; };

View File

@@ -47,6 +47,10 @@ div#message-container {
font-size: 20px; font-size: 20px;
} }
div.message {
color: #C0C0C0;
}
div#input { div#input {
padding: 20px; padding: 20px;
} }
@@ -54,10 +58,32 @@ div#input {
input#input-message { input#input-message {
width: 100%; width: 100%;
padding: 10px; padding: 10px;
background: #000; background: #111;
border: 1px solid #F00; border: 1px solid #A00000;
color: #F00; color: #C0C0C0;
font-size: 18px; font-size: 18px;
box-sizing: border-box; box-sizing: border-box;
font-family: monospace; font-family: monospace;
} }
.flash-red::after {
content: '';
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 100px;
pointer-events: none;
background: linear-gradient(to top, rgba(255, 0, 0, 0.8), transparent);
animation: flash-red-anim 0.1s linear forwards;
z-index: 9999;
}
@keyframes flash-red-anim {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}

BIN
public/audio/message.mp3 Normal file

Binary file not shown.

View File

@@ -8,6 +8,8 @@ use App\Game\Entity\Player;
use App\Game\Repository\SessionSettingRepository; use App\Game\Repository\SessionSettingRepository;
use App\Tech\Entity\User; use App\Tech\Entity\User;
use Symfony\Bundle\SecurityBundle\Security; use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
class GameResponseService class GameResponseService
{ {
@@ -15,10 +17,11 @@ class GameResponseService
private Security $security, private Security $security,
private PlayerService $playerService, private PlayerService $playerService,
private SessionSettingRepository $sessionSettingRepository, private SessionSettingRepository $sessionSettingRepository,
private HubInterface $hub,
) { ) {
} }
public function getGameResponse(string $raw) public function getGameResponse(string $raw) : array
{ {
$info = json_decode($raw, true); $info = json_decode($raw, true);
@@ -83,8 +86,10 @@ class GameResponseService
if(!in_array('chat', $rechten)) if(!in_array('chat', $rechten))
return ['result' => ['Unknown command']]; return ['result' => ['Unknown command']];
$this->handleChatMessage($message); if($this->handleChatMessage($message, $player))
break; return ['result' => ['succesfully send']];
else
return ['result' => ['Error sending']];
case '/help': case '/help':
return ['result' => $this->getHelpCommand($rechten)]; return ['result' => $this->getHelpCommand($rechten)];
case '/decode': case '/decode':
@@ -101,7 +106,6 @@ class GameResponseService
default: default:
return ['result' => ['Unknown command']]; return ['result' => ['Unknown command']];
} }
return [];
} }
private function checkConsoleCommando(string $message, Player $player, bool $sudo = false) : array private function checkConsoleCommando(string $message, Player $player, bool $sudo = false) : array
@@ -124,6 +128,9 @@ class GameResponseService
$this->playerService->saveCurrentPwdOfPlayer($player, $newLocation); $this->playerService->saveCurrentPwdOfPlayer($player, $newLocation);
return ['result' => ['Path: ' . $newLocation]]; return ['result' => ['Path: ' . $newLocation]];
case 'pwd':
$pwd = $this->playerService->getCurrentPwdOfPlayer($player);
return ['result' => ['Path: ' . $pwd]];
case 'rm': case 'rm':
break; break;
case 'sudo': case 'sudo':
@@ -161,6 +168,13 @@ class GameResponseService
$messages[] = ' USAGE: /decode {message}'; $messages[] = ' USAGE: /decode {message}';
$messages[] = ''; $messages[] = '';
break; break;
case 'pwd':
$messages[] = 'pwd';
$messages[] = ' This message will let you know what your current location is.';
$messages[] = ' It will show you the folder you are in so you can continue navigating the server.';
$messages[] = ' USAGE: pwd';
$messages[] = '';
break;
case 'cat': case 'cat':
$messages[] = 'cat'; $messages[] = 'cat';
$messages[] = ' To read a file, use cat {filename}.'; $messages[] = ' To read a file, use cat {filename}.';
@@ -210,9 +224,42 @@ class GameResponseService
return $messages; return $messages;
} }
private function handleChatMessage(string $message) private function handleChatMessage(string $message, Player $player) : bool
{ {
$messageParts = explode(' ', $message);
$toSingle = false;
if (isset($messageParts[1]) &&
is_numeric($messageParts[1]) &&
$messageParts[1] >= 1 &&
$messageParts[1] <= 3) {
$toSingle = true;
}
$chatMessage = array_shift($messageParts);
$sendTo = 0;
if ($toSingle) {
$sendTo = array_shift($messageParts);
}
$message = $player->getUser()->getUsername() . ': ' . $chatMessage;
foreach($messageParts as $messagePart) {
$message .= $messagePart . ' ';
}
$message = trim($message);
$activeGame = $player->getSession()?->getId();
if(is_null($activeGame))
return false;
$topic = $_ENV['MERCURE_TOPIC_BASE'] . '/game/hub-' . $activeGame;
$this->hub->publish(new Update($topic, json_encode([$sendTo, $message])));
return true;
} }
private function handleDecodeMessage(string $message, Player $player) private function handleDecodeMessage(string $message, Player $player)

View File

@@ -15,10 +15,9 @@
<div id="game-container"> <div id="game-container">
<div id="mercure-config" <div id="mercure-config"
data-mercure-public-url="{{ mercure_public_url|e('html_attr') }}" data-mercure-public-url="{{ mercure_public_url|e('html_attr') }}"
data-topic="{{ (mercure_topic_base ~ '/game/hub')|e('html_attr') }}" data-topic="{{ (mercure_topic_base ~ '/game/hub-' ~ session.id)|e('html_attr') }}"
data-api-ping-url="{{ path('game_api_ping')|e('html_attr') }}" data-api-ping-url="{{ path('game_api_ping')|e('html_attr') }}"
data-api-echo-url="{{ path('game_api_message')|e('html_attr') }}" data-api-echo-url="{{ path('game_api_message')|e('html_attr') }}"
data-user-id="{{ user_id|e('html_attr') }}"
style="display:none"> style="display:none">
</div> </div>
@@ -28,6 +27,9 @@
<div id="message-container"> <div id="message-container">
</div> </div>
<audio id="message-sound" style="display:none">
<source src="{{ asset('audio/message.mp3') }}" type="audio/mpeg">
</audio>
<div id="input"> <div id="input">
<input type="text" disabled id="input-message"> <input type="text" disabled id="input-message">
</div> </div>