136 lines
7.2 KiB
Markdown
136 lines
7.2 KiB
Markdown
# Player browser application
|
|
|
|
Milestone 008 provides the responsive player-facing browser application. The
|
|
browser remains a renderer and input device: it sends movement and key-drop
|
|
intentions, and it never calculates a legal move, inventory change, timer,
|
|
visibility region, compass result, or match result.
|
|
|
|
## Local assets
|
|
|
|
The application consists of `index.html`, `player.css`, `player.js`, and the
|
|
locally authored `icons.svg` sprite. The deterministic asset build compresses
|
|
each file with gzip for LittleFS. There are no remote fonts, frameworks,
|
|
analytics, icon services, or runtime network dependencies beyond the local ESP.
|
|
|
|
## Gameplay icon mapping
|
|
|
|
`icons.svg` contains editable, local SVG symbols with a shared `64 x 64` view
|
|
box for the reference-derived gameplay set: `player`, `key`, `exit-closed`,
|
|
`crystal`, `exit-open`, and `spikes`. Player 1 is yellow and Player 2 is blue;
|
|
their matching keys and exits use the same owner color. The player, key, and
|
|
exit silhouettes remain distinct, and Player 2 uses a dashed secondary
|
|
treatment so ownership does not depend on color alone.
|
|
|
|
The closed and opened arched exits show the existing locked and opened exit
|
|
states. The crystal represents the existing vision artifact. The spike symbol
|
|
is kept as a local catalog asset but is not shown for blocked movement; it does
|
|
not add a trap, damage, protocol, or server-state feature.
|
|
|
|
## Input contract
|
|
|
|
Arrow keys and WASD map to `north`, `east`, `south`, and `west` move intentions.
|
|
Touch swipes use the same four strings after a 28 CSS-pixel threshold. Phone
|
|
layouts hide the direction pad and retain swipes; tablet and desktop layouts
|
|
show the direction pad and also accept swipes. The persistent `OK` control
|
|
sends `drop_key` and exposes the accessible label `Сбросить ключ`.
|
|
|
|
On phones, an always-visible four-button directional pad is the primary touch
|
|
control. Its inactive centre prevents diagonal input; each button sends one of
|
|
the same four authoritative movement intentions and a held button repeats no
|
|
faster than every 170 ms. The centered player avatar is also an optional touch
|
|
movement handle. Its transparent
|
|
target is 96 by 96 CSS pixels while the visible avatar can deflect at most 28
|
|
CSS pixels. A 12-pixel dead zone and diagonal hysteresis resolve only cardinal
|
|
intentions. Drag displacement selects a monotonically bounded local cadence
|
|
from 360 ms to 170 ms, which remains slower than the authoritative 150 ms
|
|
cooldown. The browser keeps one sent move in flight and coalesces all later
|
|
drag changes into one latest intention; release discards that unsent intention.
|
|
The deflection is local input feedback only. The maze redraws only after a
|
|
server-authoritative projection and the browser never predicts a cell change.
|
|
For every projection, the browser translates only the received cells so the
|
|
authoritative `self` cell stays under the responsive avatar focal point. On a
|
|
portrait phone this is horizontally centered at 30% of the map height; compact
|
|
landscape uses a stable left-biased unobstructed point away from the directional
|
|
pad. This also keeps the avatar off a wall when the visible projection reaches
|
|
a maze boundary, and is recalculated on resize and rotation.
|
|
|
|
## Feedback and message rail
|
|
|
|
## Compact settings
|
|
|
|
The persistent gear button beside the connection indicator opens one compact
|
|
popover. It shows live connection details and Wi-Fi settings on every screen.
|
|
During an active player match it additionally provides sound, reduced-motion,
|
|
and optional-help controls with visible Russian on/off labels. Opening or
|
|
closing this presentation-only menu never sends a command or changes server
|
|
state. Escape and outside activation dismiss it; player actions also close it
|
|
before opening their existing destination.
|
|
|
|
The header response indicator takes at most one heartbeat sample every five
|
|
seconds, holds one in-flight request, and uses a five-sample median. Good is
|
|
at most 150 ms, delayed is 151--500 ms, and slow is above 500 ms. Hysteresis
|
|
holds good until above 175 ms and slow until at most 450 ms; a sample is stale
|
|
after 15 seconds. Hidden documents and closed sockets do not probe.
|
|
|
|
The browser stores the direction for each outstanding move request. A matching
|
|
server `blocked` rejection may include only that player's approved breakthrough
|
|
stage, which bends the already rendered bush wall for at most 340 ms without
|
|
blocked-move text. A received opened-passage projection paired with an adjacent
|
|
authoritative player move may show one 480 ms hedge-parting dash and four
|
|
bounded decorative accents before settling at the server-approved destination.
|
|
Opened-passage bits render a permanent trampled bush gap with a 60% clear
|
|
centre. Effects have restartable timers and clear on a newer projection, pause,
|
|
result, reset, disconnect, screen change, and control reset. Reduced motion is
|
|
static and settles directly at the received destination.
|
|
|
|
The map draws only a bounded canonical hedge-edge layer generated from current
|
|
received cells. It hashes each visible edge deterministically to choose the
|
|
approved local SVG variant, so the browser neither retains unseen geometry nor
|
|
decides topology. The layer is decorative and cannot receive input.
|
|
|
|
All remaining Russian system messages use one `role=status` live message rail
|
|
above the application top bar. One connection-loss message has highest priority
|
|
and remains until a successful recovery; other messages deduplicate, replace
|
|
lower-priority content, and dismiss after 2.6 seconds.
|
|
|
|
Commands use the version-one protocol envelope and contain intention data only:
|
|
|
|
```json
|
|
{"v":1,"type":"move","seq":7,"requestId":"move-7","payload":{"direction":"west"}}
|
|
```
|
|
|
|
```json
|
|
{"v":1,"type":"drop_key","seq":8,"requestId":"drop_key-8","payload":{}}
|
|
```
|
|
|
|
## Projection handling
|
|
|
|
View chunks are grouped by authoritative view sequence and rendered only after
|
|
all chunks arrive. Each completed view replaces the previous maze DOM in full.
|
|
The client does not merge a new view with previously visible cells, infer
|
|
missing passages, or retain an explored map. Maze dimensions, cells, objects,
|
|
player position, opponent visibility, inventory, vision duration, compass hint,
|
|
phase, and result all come from the server projection.
|
|
|
|
The session token is the only game-related value saved in browser storage. On
|
|
reload, the client sends that token in a `restore` intention and waits for a
|
|
fresh role-appropriate server projection. Position, inventory, effects, hints,
|
|
maze cells, and results are never restored from browser storage.
|
|
|
|
## Presentation states
|
|
|
|
Name selection, lobby, countdown, active play, pause, and match result use the
|
|
shared dark retro-arcade visual system. The fixed player status identifies the
|
|
player and labels inventory, remaining boosted-vision moves, compass direction,
|
|
and match phase. Modal overlays distinguish countdown, pause, and results.
|
|
Rejected movement produces a local wall-bump animation without revealing any
|
|
additional maze state. All player-facing copy and browser-generated errors are
|
|
Russian; protocol identifiers remain English.
|
|
|
|
Sound and supported vibration are enabled after the first pointer or keyboard
|
|
interaction. Both are progressive enhancements: unsupported devices continue
|
|
without an error.
|
|
|
|
Observer presentation and its read-only interaction boundary are documented in
|
|
`OBSERVER_UI.md`.
|