explodorama / commit
Demo: a "You" tag over the player; the finger starts beside them
CHANGELOG.md | 3 ++- PLAN.md | 4 +++- README.md | 4 +++- decisions.md | 19 +++++++++++++++++++ docs/screenshots/demo.png | Bin 147519 -> 151437 bytes index.html | 1 + src/main.ts | 34 ++++++++++++++++++++++++++++------ src/render/board.ts | 2 +- src/style.css | 43 +++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 100 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e6cab7..d99e422 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,7 +67,8 @@ saved before the change is dropped and the next visit starts a new run. ways to die (into a blast, not far enough, blocked in, a chain, an enemy, and a couple of funny ones), each with a caption and an icon, until you tap. Each scene gets to its point 3 to 6 turns in, and a finger plays every turn on the real action bar — taps with a ripple, a long - press for a bomb, a button then a tile, Go — after five opening scenes on the controls alone. + press for a bomb, a button then a tile, Go — after five opening scenes on the controls alone. The player wears a "You" tag in the demo, and + each scene's finger starts beside them. - **Installable** as an app (Chrome, Edge, Android, iPhone / iPad), and playable offline once loaded. - **Deployment** to GitLab Pages on every push to the default branch, after the tests and the diff --git a/PLAN.md b/PLAN.md index a8f1cff..fb76eef 100644 --- a/PLAN.md +++ b/PLAN.md @@ -695,7 +695,9 @@ gesture can play isn't used. The UI acts each one out with a finger (Kenney's po the real action bar, shown in the demo as a tray right under the board, apart from the caption panel at the bottom (grouped by interactivity: board and buttons, then the words): a ripple where it taps, a ring closing in on a long press, then the bomb's preview; a picked button lights up its tiles, a far tap shows -auto-move's way. Turns take about 1–2 s. **Five control scenes** come first (step, long-press +auto-move's way. Turns take about 1–2 s. *Since 2026-10-24 (user request)* the player wears a +green **"You"** tag just over their head, always (it wasn't obvious which moving figure is you), and each scene's finger +appears just below-right of the player, where the eye should start. **Five control scenes** come first (step, long-press bomb, Bomb button, Wait, auto-move with Go), and again each time the living scenes have all been shown. Captions of Run, Kick, Throw, Teleport, Diagonal and the Detonator say how to do them. 41 kinds, all found on 40 seeds each, the slowest under 0.4 s. Ideas: a gem's choice, and diff --git a/README.md b/README.md index c54f2d4..d5bbb58 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,9 @@ the controls** as it plays every turn, on the real action bar (it sits right und apart from the caption at the bottom: what you play on, then what explains it): a tap on a tile (with a ripple where it lands), a long press for a bomb (a ring closes in, the bomb's blast is previewed, the bomb goes down when the finger lifts), a button and then a tile (Bomb then a tile; Move then two -tiles away for a Run), Wait, and Go. It starts with five scenes about the controls alone: +tiles away for a Run), Wait, and Go. The player wears a green **"You"** tag over their head throughout, since it isn't otherwise obvious which of the +moving figures is you, and the finger appears beside the player at the start of each scene, so +your eye starts where the action is. It starts with five scenes about the controls alone: stepping, a bomb by long press, a bomb with the Bomb button, waiting, and auto-move (tap a far tile, then Go). The rest show every feature on the board: bombs and their countdown, watching the enemies' arrows to bomb where they're going, a diff --git a/decisions.md b/decisions.md index db41123..7375301 100644 --- a/decisions.md +++ b/decisions.md @@ -1195,3 +1195,22 @@ compatibility code, it keeps a move log from being replayed under rules it wasn' Keeping a run across a rules change (a state snapshot, the record noting the change) or playing older versions was discussed and shelved by the user: a lot of effort for a doubtful pay-off while nobody else plays (PLAN.md §11). + +### 2026-10-24 — The demo's finger starts at the player; a "You" tag + +User request: the eyes follow the hand, so it should start close to the player; and it isn't +immediately clear which of the moving figures is the player. + +- **The finger appears beside the player** at the start of every scene: its tip at the + bottom-right corner of the player's tile, faded in there rather than gliding in from wherever + the last scene ended (or, for the first scene, the middle of the screen). It is placed once the + camera shows the new scene, so the first scene gets it right too. +- **A "You" tag** over the player for the whole demo: a green pill (the accent colour, as on a + picked button) with a dark rim and a point down at them, following where the player is drawn, + so it walks, runs and teleports with them (`playerPos`, now exported from board.ts). It hugs + the top of the player's head, always over them (user's call, same day: for a while it flipped + under the player on the top row, where it blended into the stone wall; now it stays put, and a + white halo round its dark rim keeps it apart from the stone). It sits + above the finger, so a tap on the tile over the player doesn't hide it. Only in the demo; in + play, the action bar's Move button and the tapping make it clear soon enough, and the tag would + cover the tile above. Checked in a browser frame by frame. diff --git a/docs/screenshots/demo.png b/docs/screenshots/demo.png index 1adb52a..c468499 100644 Binary files a/docs/screenshots/demo.png and b/docs/screenshots/demo.png differ diff --git a/index.html b/index.html index 58c2675..9a9ea15 100644 --- a/index.html +++ b/index.html @@ -60,6 +60,7 @@ <p class="demo-hint">Tap anywhere to play</p> </div> <div id="demo-finger" hidden aria-hidden="true"></div> +<div id="demo-you" hidden aria-hidden="true"></div> <script type="module" src="/src/main.ts"></script> </body> </html> diff --git a/src/main.ts b/src/main.ts index f4b0bf5..5832ff3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -12,7 +12,7 @@ import { vec2, worldToScreen, } from 'littlejsengine' -import { BACKGROUND, drawBoard, FLAME_SECONDS, toTile, toWorld, type Flame } from './render/board' +import { BACKGROUND, drawBoard, FLAME_SECONDS, playerPos, toTile, toWorld, type Flame } from './render/board' import { clearEffects, newEffects, playerTransition, pruneEffects, startEffects, type PlayerMove, type PlayerPose } from './render/effects' import { updateCamera } from './render/camera' import { CHARGE_ICONS, MONO_ICONS, POWER_UP_ICONS, TEXTURES } from './render/sprites' @@ -587,7 +587,8 @@ interface Beat { run: () => void then: number } -let demo: { scene: Scene; next: number; at: number; real: GameState; beats: Beat[] } | null = null +/** `rest`: the finger is still to be put beside the player, once the camera shows the new scene. */ +let demo: { scene: Scene; next: number; at: number; real: GameState; beats: Beat[]; rest: boolean } | null = null // The scenes' seeds: from `?seed` for a repeatable demo, otherwise random. let demoSeed = Number(params.get('seed')) || randomSeed() // The scenes about the controls first, in their order; then two shuffled queues, taken in turn: @@ -600,6 +601,7 @@ const demoPanel = document.getElementById('demo')! const demoCaption = document.getElementById('demo-caption')! const actionBar = document.getElementById('actions')! const finger = document.getElementById('demo-finger')! +const youTag = document.getElementById('demo-you')! finger.innerHTML = `<img src="${handIcon}" alt="" draggable="false">` /** * In the demo the action bar sits right under the board, as wide as it, and the caption panel @@ -668,18 +670,19 @@ function nextScene(): void { `<img class="demo-icon${MONO_ICONS.has(icon) ? ' mono' : ''}" src="${spriteUrl(icon)}" alt="" draggable="false">` + `<span>${SCENES[scene.kind].caption}</span>` afterChange() - // The finger comes back, from where it was. - finger.classList.remove('away') + // The finger comes back beside the player, where the eye should be (user request, 2026-10-24). + demo.rest = true return } } function startDemo(): void { - demo = { scene: null!, next: 0, at: 0, real: state, beats: [] } + demo = { scene: null!, next: 0, at: 0, real: state, beats: [], rest: false } document.body.classList.add('demo') demoPanel.hidden = false finger.hidden = false - placeFinger(vec2(innerWidth / 2, innerHeight / 2), false) + finger.classList.add('away') + youTag.hidden = false nextScene() if (!demo.scene) endDemo() } @@ -758,6 +761,23 @@ function centreOf(el: Element) { return vec2(r.left + r.width / 2, r.top + r.height / 2) } +/** + * Each frame of the demo: the "You" tag over the player, wherever they are drawn (it's not + * obvious which of the moving figures is you; user request, 2026-10-24), and at a scene's start + * the finger, appearing just below and right of the player, so the eye starts where the action is. + */ +function placeDemoMarks(): void { + if (!demo) return + // Hugging the top of the player's head, always over them (user request, 2026-10-24). + const tip = worldToScreen(playerPos(state, playerMove).add(vec2(0, 0.4))) + const r = mainCanvas.getBoundingClientRect() + youTag.style.transform = `translate(${r.left + tip.x}px, ${r.top + tip.y}px)` + if (!demo.rest) return + demo.rest = false + placeFinger(worldToScreen(toWorld(state, state.player).add(vec2(0.55, -0.55))).add(vec2(r.left, r.top)), false) + finger.classList.remove('away') +} + /** The finger's tip to `at`: gliding there, or (`glide` false) at once. */ function placeFinger(at: { x: number; y: number }, glide = true): void { finger.classList.toggle('glide', glide) @@ -798,6 +818,7 @@ function endDemo(): void { document.body.classList.remove('demo') demoPanel.hidden = true finger.hidden = true + youTag.hidden = true for (const mark of document.querySelectorAll('.demo-ripple, .demo-ring')) mark.remove() for (const b of actionBar.querySelectorAll('.pressed')) b.classList.remove('pressed') actionBar.style.left = actionBar.style.width = actionBar.style.top = '' @@ -914,6 +935,7 @@ await engineInit( const panel = demoPanel.getBoundingClientRect().height updateCamera(state, demo ? { top: 0, bottom: panel + DEMO_PANEL_GAP + DEMO_GAP + controls.insets().bottom } : controls.insets(), snapCamera) if (demo) placeDemoBar() + placeDemoMarks() snapCamera = false flames = flames.filter((f) => time - f.born < FLAME_SECONDS) pruneEffects(fx, time) diff --git a/src/render/board.ts b/src/render/board.ts index 668e92e..3680806 100644 --- a/src/render/board.ts +++ b/src/render/board.ts @@ -232,7 +232,7 @@ export function drawBoard(view: View): void { } /** Where the player is on screen right now: sliding between tiles while walking, otherwise on their tile. */ -function playerPos(s: GameState, move: PlayerMove | null): Vector2 { +export function playerPos(s: GameState, move: PlayerMove | null): Vector2 { const to = toWorld(s, s.player) const t = move ? time - move.at : Infinity if (!move || t >= moveSeconds(move)) return to diff --git a/src/style.css b/src/style.css index c8d56ab..49d6757 100644 --- a/src/style.css +++ b/src/style.css @@ -891,6 +891,49 @@ body.demo #actions { filter: drop-shadow(0 2px 3px rgb(0 0 0 / 0.35)); } +/* The demo's "You" tag over the player (main.ts places it on the top of their tile, following + them as they move): a green pill pointing down at them. Above the finger, so a tap on the + tile over the player doesn't hide it. */ +#demo-you { + position: fixed; + left: 0; + top: 0; + z-index: 5; + pointer-events: none; +} + +#demo-you[hidden] { + display: none; +} + +#demo-you::before { + content: 'You'; + position: absolute; + bottom: 6px; + left: 0; + transform: translateX(-50%); + padding: 2px 9px 3px; + border: 2px solid var(--ink); + border-radius: 999px; + background: var(--accent); + /* A white halo round the dark rim, so it stands out on the grey stone of the top wall too. */ + box-shadow: 0 0 0 2px #fff; + color: #fff; + font: 800 13px/1.1 ui-rounded, 'SF Pro Rounded', system-ui, -apple-system, sans-serif; + white-space: nowrap; +} + +/* Its point, a small triangle under the pill. */ +#demo-you::after { + content: ''; + position: absolute; + bottom: 0; + left: -6px; + border: 6px solid transparent; + border-bottom: 0; + border-top-color: var(--ink); +} + /* Pressing: the hand sinks toward the screen. */ #demo-finger.down img { transform: scale(0.86);