Port the trainer card and the play time

The player's name in the start menu opens TrainerCard: page 1 with the
name, ID, money, Pokédex count and play time, page 2 with the Johto
leaders and the badges owned, turned and closed with the original
buttons. The card picture, leader faces and animated badges come from the
ROM with _CGB_TrainerCard's palettes. GameTimer now counts the play time
from the end of the introduction, capped at 999:59:59, and saves keep it.

Closes #23

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Sébastien QUEROLandClaude Opus 5.5 committed 2026-10-01 13:32:14 +02:00
1 parent 38b9562bd3
commit 47698565ab
24 files changed
+954 -30

No files matched your search

+1
View File
@@ -33,6 +33,7 @@ export function renderShell(root: HTMLElement) {
</div>
<div id="stats-screen" class="stats-screen" hidden aria-label="Pokémon stats"><div class="stats-top"><canvas id="stats-pic" width="56" height="56" role="img"></canvas><div id="stats-header" class="stats-header"></div></div><div id="stats-page" class="stats-page" aria-live="polite"></div><div class="stats-nav"><button id="stats-prev" aria-label="Previous page">◀</button><span id="stats-page-number"></span><button id="stats-next" aria-label="Next page">▶</button><button id="stats-close">Close</button></div></div>
<div id="pokegear" class="pokegear" hidden aria-label="Pokégear"><div id="pokegear-cards" class="pokegear-cards" aria-label="Cards"></div><div id="pokegear-card" class="pokegear-card" aria-live="polite"></div></div>
<div id="trainer-card" class="trainer-card" hidden aria-label="Trainer card"><div class="trainer-card-top"><dl id="trainer-card-info"></dl><canvas id="trainer-card-pic" width="40" height="56" role="img"></canvas></div><div id="trainer-card-page" class="trainer-card-page"></div></div>
<div id="town-map-view" class="town-map-view" hidden aria-label="Town Map"><canvas id="town-map-canvas" width="160" height="144" role="img" aria-label="Town Map"></canvas><button id="town-map-close">Close map</button></div>
<div id="mart" class="mart" hidden aria-label="Poké Mart">
<p id="mart-money" class="mart-money"></p>
+66 -1
View File
@@ -539,7 +539,8 @@ dialog h2 {
display: none;
}
.intro-screen:not([hidden]) ~ .minimap-overlay,
.pokegear:not([hidden]) ~ .minimap-overlay {
.pokegear:not([hidden]) ~ .minimap-overlay,
.trainer-card:not([hidden]) ~ .minimap-overlay {
display: none;
}
.pokepic {
@@ -1261,3 +1262,67 @@ dialog dd {
padding: 4px 8px;
border: 3px double var(--ink);
}
/* TrainerCard: the card's frame takes the other gender's palette. */
.trainer-card {
position: absolute;
inset: 0;
z-index: 2;
display: grid;
grid-template-rows: auto 1fr;
gap: 8px;
padding: 10px;
overflow-y: auto;
color: var(--ink);
background: var(--trainer-card-frame, var(--blue-dark));
}
.trainer-card-top,
.trainer-card-page {
padding: 8px 12px;
background: #fff;
border: 5px double var(--ink);
font-variant-numeric: tabular-nums;
}
.trainer-card-top {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
gap: 12px;
}
.trainer-card dl {
display: grid;
grid-template-columns: auto 1fr;
gap: 6px 12px;
margin: 0;
}
.trainer-card dt,
.trainer-card dd {
margin: 0;
white-space: pre;
}
.trainer-card-top canvas {
width: min(22vw, 100px);
aspect-ratio: 40 / 56;
image-rendering: pixelated;
}
.trainer-card-page {
display: grid;
align-content: start;
gap: 8px;
}
.trainer-card-page h3 {
margin: 0;
font-size: 0.85rem;
letter-spacing: 0.08em;
}
.trainer-card-page p {
margin: 0;
justify-self: end;
}
.trainer-card-page canvas {
width: 100%;
max-width: 512px;
justify-self: center;
aspect-ratio: 128 / 48;
image-rendering: pixelated;
}