140 lines
6.6 KiB
PHP
140 lines
6.6 KiB
PHP
<?php
|
||
declare(strict_types=1);
|
||
@date_default_timezone_set('UTC');
|
||
|
||
require_once __DIR__ . '/tetris_data.php';
|
||
|
||
$projectName = $_SERVER['PROJECT_NAME'] ?? 'Midnight Blocks';
|
||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Classic in-browser Tetris with a dark interface, leaderboard, and recent runs.';
|
||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||
$assetVersion = (string) @filemtime(__DIR__ . '/assets/css/custom.css');
|
||
|
||
$scoreId = isset($_GET['id']) ? (int) $_GET['id'] : 0;
|
||
$score = null;
|
||
$dbError = null;
|
||
|
||
try {
|
||
if ($scoreId > 0) {
|
||
$score = tetrisFetchScoreById($scoreId);
|
||
}
|
||
} catch (Throwable $e) {
|
||
$dbError = 'Leaderboard data is temporarily unavailable.';
|
||
}
|
||
|
||
$title = $score ? $score['player_name'] . ' run · ' . $projectName : 'Run not found · ' . $projectName;
|
||
$metaDescription = $score
|
||
? sprintf('%s scored %d points with %d cleared lines in Midnight Blocks.', $score['player_name'], $score['score'], $score['lines_cleared'])
|
||
: $projectDescription;
|
||
?>
|
||
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title><?= htmlspecialchars($title) ?></title>
|
||
<meta name="description" content="<?= htmlspecialchars($metaDescription) ?>">
|
||
<meta property="og:title" content="<?= htmlspecialchars($title) ?>">
|
||
<meta property="og:description" content="<?= htmlspecialchars($metaDescription) ?>">
|
||
<meta property="twitter:title" content="<?= htmlspecialchars($title) ?>">
|
||
<meta property="twitter:description" content="<?= htmlspecialchars($metaDescription) ?>">
|
||
<?php if ($projectImageUrl): ?>
|
||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>">
|
||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>">
|
||
<?php endif; ?>
|
||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||
<link rel="stylesheet" href="/assets/css/custom.css?v=<?= urlencode($assetVersion ?: (string) time()) ?>">
|
||
</head>
|
||
<body class="tetris-app">
|
||
<header class="border-bottom border-secondary-subtle bg-body-tertiary bg-opacity-10">
|
||
<nav class="navbar navbar-expand-lg navbar-dark py-3">
|
||
<div class="container">
|
||
<a class="navbar-brand fw-semibold letter-spacing-1" href="/">Midnight Blocks</a>
|
||
<div class="d-flex align-items-center gap-2">
|
||
<a class="btn btn-outline-light btn-sm" href="/#game">Play again</a>
|
||
<a class="btn btn-light btn-sm" href="/#leaderboard">Leaderboard</a>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
</header>
|
||
|
||
<main class="py-5">
|
||
<div class="container">
|
||
<div class="row justify-content-center">
|
||
<div class="col-xl-8">
|
||
<?php if ($dbError): ?>
|
||
<div class="alert alert-warning border-0 soft-panel mb-4"><?= htmlspecialchars($dbError) ?></div>
|
||
<?php endif; ?>
|
||
|
||
<?php if (!$score): ?>
|
||
<section class="soft-panel p-4 p-lg-5 text-center">
|
||
<span class="eyebrow">Run detail</span>
|
||
<h1 class="display-6 fw-semibold mt-3 mb-2">That score could not be found.</h1>
|
||
<p class="text-secondary mb-4">Try another run from the leaderboard or start a fresh game.</p>
|
||
<div class="d-flex justify-content-center gap-2 flex-wrap">
|
||
<a class="btn btn-light" href="/">Back to game</a>
|
||
<a class="btn btn-outline-light" href="/#leaderboard">Browse leaderboard</a>
|
||
</div>
|
||
</section>
|
||
<?php else: ?>
|
||
<section class="soft-panel p-4 p-lg-5 mb-4">
|
||
<div class="d-flex flex-wrap justify-content-between align-items-start gap-3 mb-4">
|
||
<div>
|
||
<span class="eyebrow">Saved run</span>
|
||
<h1 class="display-6 fw-semibold mt-3 mb-2"><?= htmlspecialchars($score['player_name']) ?>’s session</h1>
|
||
<p class="text-secondary mb-0">Recorded <?= htmlspecialchars(date('M j, Y H:i', strtotime((string) $score['created_at']))) ?> UTC.</p>
|
||
</div>
|
||
<div class="score-chip score-chip-lg text-end">
|
||
<span class="score-chip-label">Score</span>
|
||
<strong><?= number_format((int) $score['score']) ?></strong>
|
||
</div>
|
||
</div>
|
||
<div class="row g-3">
|
||
<div class="col-md-4">
|
||
<article class="metric-card h-100">
|
||
<span class="metric-label">Lines cleared</span>
|
||
<strong class="metric-value"><?= number_format((int) $score['lines_cleared']) ?></strong>
|
||
</article>
|
||
</div>
|
||
<div class="col-md-4">
|
||
<article class="metric-card h-100">
|
||
<span class="metric-label">Level reached</span>
|
||
<strong class="metric-value"><?= number_format((int) $score['level_reached']) ?></strong>
|
||
</article>
|
||
</div>
|
||
<div class="col-md-4">
|
||
<article class="metric-card h-100">
|
||
<span class="metric-label">Session length</span>
|
||
<strong class="metric-value"><?= number_format((int) $score['duration_seconds']) ?>s</strong>
|
||
</article>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="soft-panel p-4 p-lg-5">
|
||
<span class="eyebrow">Breakdown</span>
|
||
<div class="row g-4 mt-1 align-items-start">
|
||
<div class="col-md-7">
|
||
<h2 class="h4 fw-semibold mb-3">What this run means</h2>
|
||
<ul class="detail-list mb-0">
|
||
<li>High score value reflects line clears plus soft and hard drop bonuses.</li>
|
||
<li>Levels increase every 10 cleared lines, which accelerates the drop speed.</li>
|
||
<li>Each saved run can be revisited from the leaderboard for quick comparison.</li>
|
||
</ul>
|
||
</div>
|
||
<div class="col-md-5">
|
||
<div class="metric-card h-100">
|
||
<span class="metric-label">Next challenge</span>
|
||
<p class="text-secondary mb-3">Beat this run by improving stacking efficiency and chaining doubles or tetrises.</p>
|
||
<a class="btn btn-light w-100" href="/">Start a new run</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
</body>
|
||
</html>
|