39305-vm/score.php
Flatlogic Bot 9191afc91a 0
2026-03-25 12:13:13 +00:00

171 lines
7.8 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
require_once __DIR__ . '/lib/tetris_store.php';
$projectName = trim((string) ($_SERVER['PROJECT_NAME'] ?? 'RetroStack'));
$projectDescription = trim((string) ($_SERVER['PROJECT_DESCRIPTION'] ?? 'Classic Tetris-style puzzle gameplay with an online leaderboard.'));
$projectImageUrl = trim((string) ($_SERVER['PROJECT_IMAGE_URL'] ?? ''));
$scoreId = isset($_GET['id']) ? (int) $_GET['id'] : 0;
$score = null;
$rank = null;
$errorMessage = null;
$topScores = [];
try {
if ($scoreId > 0) {
$score = tetrisFetchScore($scoreId);
$rank = $score ? tetrisFetchScoreRank($scoreId) : null;
}
$topScores = tetrisFetchTopScores(10);
} catch (Throwable $exception) {
$errorMessage = 'Leaderboard data is unavailable right now.';
error_log('Tetris score page error: ' . $exception->getMessage());
}
if (!$score) {
http_response_code(404);
}
$pageTitle = $score ? sprintf('%s — %s score %d', $projectName !== '' ? $projectName : 'RetroStack', $score['player_name'], (int) $score['score']) : (($projectName !== '' ? $projectName : 'RetroStack') . ' — Score not found');
$pageDescription = $score
? sprintf('%s reached %d points, %d lines, and level %d in this RetroStack run.', $score['player_name'], (int) $score['score'], (int) $score['lines_cleared'], (int) $score['level_reached'])
: $projectDescription;
function esc(?string $value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= esc($pageTitle) ?></title>
<meta name="description" content="<?= esc($pageDescription) ?>">
<?php if ($projectDescription !== ''): ?>
<meta property="og:description" content="<?= esc($projectDescription) ?>">
<meta property="twitter:description" content="<?= esc($projectDescription) ?>">
<?php endif; ?>
<?php if ($projectImageUrl !== ''): ?>
<meta property="og:image" content="<?= esc($projectImageUrl) ?>">
<meta property="twitter:image" content="<?= esc($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((string) filemtime(__DIR__ . '/assets/css/custom.css')) ?>">
</head>
<body>
<header class="border-bottom border-secondary-subtle sticky-top app-header">
<nav class="navbar navbar-expand-lg navbar-dark py-3">
<div class="container">
<a class="navbar-brand d-flex align-items-center gap-2" href="index.php#top">
<span class="brand-mark">RS</span>
<span>
<span class="d-block brand-title">RetroStack</span>
<span class="brand-subtitle">Online score detail</span>
</span>
</a>
<div class="ms-auto d-flex gap-2">
<a class="btn btn-outline-light btn-sm" href="index.php#leaderboard">Leaderboard</a>
<a class="btn btn-light btn-sm" href="index.php#play">Play again</a>
</div>
</div>
</nav>
</header>
<main class="py-5">
<div class="container">
<?php if ($errorMessage): ?>
<div class="alert alert-warning border-0 surface-panel mb-4"><?= esc($errorMessage) ?></div>
<?php endif; ?>
<?php if (!$score): ?>
<section class="surface-panel p-4 p-lg-5 text-center mx-auto" style="max-width: 720px;">
<span class="section-label">Score detail</span>
<h1 class="h2 mt-3">That run could not be found.</h1>
<p class="text-secondary mb-4">Try a fresh round and submit a new score to populate the online leaderboard.</p>
<a class="btn btn-light" href="index.php#play">Return to the game</a>
</section>
<?php else: ?>
<div class="row g-4 align-items-start">
<div class="col-lg-8">
<section class="surface-panel p-4 p-lg-5">
<div class="d-flex flex-wrap gap-3 justify-content-between align-items-start mb-4">
<div>
<span class="section-label">Verified leaderboard run</span>
<h1 class="display-title mt-3 mb-2"><?= esc($score['player_name']) ?></h1>
<p class="text-secondary mb-0">Submitted <?= esc(date('M j, Y  H:i', strtotime((string) $score['created_at']))) ?> UTC</p>
</div>
<div class="score-rank-pill">Rank #<?= (int) $rank ?></div>
</div>
<div class="row g-3 mb-4">
<div class="col-sm-6 col-xl-3">
<div class="metric-card h-100">
<div class="metric-label">Score</div>
<div class="metric-value"><?= number_format((int) $score['score']) ?></div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="metric-card h-100">
<div class="metric-label">Lines</div>
<div class="metric-value"><?= number_format((int) $score['lines_cleared']) ?></div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="metric-card h-100">
<div class="metric-label">Level</div>
<div class="metric-value"><?= number_format((int) $score['level_reached']) ?></div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="metric-card h-100">
<div class="metric-label">Duration</div>
<div class="metric-value"><?= number_format((int) $score['duration_seconds']) ?>s</div>
</div>
</div>
</div>
<div class="surface-subpanel p-4">
<h2 class="h5 mb-3">Run notes</h2>
<ul class="detail-list mb-0">
<li>Online leaderboard entries are ordered by score, then lines, then level, then shorter survival time.</li>
<li>This detail page gives each score a shareable destination for friendly competition.</li>
<li>To improve your rank, return to the main board and chase a higher line clear count.</li>
</ul>
</div>
</section>
</div>
<div class="col-lg-4">
<aside class="surface-panel p-4">
<div class="d-flex align-items-center justify-content-between mb-3">
<div>
<span class="section-label">Top runs</span>
<h2 class="h5 mt-2 mb-0">Leaderboard snapshot</h2>
</div>
<a class="text-decoration-none small-link" href="index.php#leaderboard">View live board</a>
</div>
<?php if (!$topScores): ?>
<p class="text-secondary mb-0">No scores yet. Be the first to submit a run.</p>
<?php else: ?>
<div class="leaderboard-list compact-list">
<?php foreach ($topScores as $index => $entry): ?>
<a class="leaderboard-item <?= (int) $entry['id'] === (int) $score['id'] ? 'is-active' : '' ?>" href="score.php?id=<?= (int) $entry['id'] ?>">
<span class="leaderboard-rank">#<?= $index + 1 ?></span>
<span class="leaderboard-player"><?= esc($entry['player_name']) ?></span>
<span class="leaderboard-meta"><?= number_format((int) $entry['score']) ?> pts</span>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
</aside>
</div>
</div>
<?php endif; ?>
</div>
</main>
</body>
</html>