fix(player): make avatar handle transparent

This commit is contained in:
2026-09-10 22:38:48 +03:00
parent 8949891a6b
commit bb5bd73ebd
3 changed files with 165 additions and 5 deletions
+163 -4
View File
@@ -2125,7 +2125,7 @@ The following planning-time observation motivated the milestone:
## Milestone 021 — Mobile player focal-point repositioning
**Status:** `IN_PROGRESS`
**Status:** `COMPLETE`
### Objective
@@ -2259,11 +2259,170 @@ The following planning-time observation motivated the milestone:
RAM `58868/327680` bytes and factory flash `1027966/2097152` bytes.
`/home/sasa/.platformio/penv/bin/pio run -t buildfs` passed and produced the
configured `2031616/2031616` byte LittleFS image.
- Milestone 021 remains `IN_PROGRESS`: the project-owner representative mobile
portrait and compact-landscape touch confirmation required by acceptance
criterion 12 has not been recorded. Milestone 022 has not been started.
- On 2026-09-10, a project-owner live UI check found that the original 40%
map-region target still appeared centered on the full phone screen because
the map begins below the status area. The owner requested a more elevated
player position, so the active milestone target and implementation were
adjusted to 30% of map height; mobile confirmation remains required.
- On 2026-09-10, the project owner manually checked the revised mobile layout
and confirmed that the raised, transparent player handle no longer obscures
nearby maze passages and that the layout works as expected. This satisfies
the remaining manual confirmation criterion; Milestone 021 is `COMPLETE`.
## Milestone 022 — Persistent bush-wall breakthrough
**Status:** `NOT_STARTED`
### Objective
Add an always-available perseverance mechanic for both players: five deliberate
attempts against the same interior wall permanently open a passage through its
bushes, with convincing staged vegetation reactions and a final breakthrough
effect, while preserving ESP authority, hidden-state protection, bounded
resources, and fair rules.
### Scope
- Do not start implementation until every preceding milestone is `COMPLETE`.
- Before implementation, reconcile `MVP.md`, `ARCHITECTURE.md`,
`docs/UI_STYLE.md`, and supporting player-UI documentation with mutable
in-match maze topology. The new contract supersedes the current rule that a
rejected wall movement can never change position.
- Keep the ESP authoritative for impact counting, timeout handling, passage
mutation, player movement, projection updates, and all breakthrough events.
Browsers continue to send ordinary bounded cardinal movement intentions and
never decide that a wall has weakened or opened.
- Track one bounded perseverance streak per active player. A qualifying streak
consists of five server-accepted movement attempts made from the same cell in
the same cardinal direction against the same still-closed interior wall.
- Reset that player's streak when 3,000 ms or more elapse after the previous
qualifying impact, the player makes a successful move, the player attempts a
different direction, the player is paused or disconnected, or the match is
reset or completed. Rejected `rate_limited`, malformed, stale, duplicate, and
unauthorized commands neither increment nor complete a streak.
- On the fifth qualifying impact, atomically open the wall in both adjacent
cells and complete the originally requested move into the neighboring cell.
The passage remains open for the rest of the active match and can be used by
either player from either side without further impacts.
- Never allow a breakthrough through the maze's outer perimeter or into an
invalid cell. Key, artifact, exit, result, visibility, and occupancy rules
apply to the fifth-attempt movement exactly as they do to an ordinary move.
- Do not combine progress from two players or from opposite sides of a wall.
Each player has an independent streak, but a completed opening is one shared
authoritative topology change visible and usable according to normal game
rules.
- Represent permanent opened-wall state with a bounded structure sized from
the configured maximum maze dimensions. Clear it when the active match is
discarded; persistence across reboot is not required.
- Extend the protocol only as needed to deliver server-approved impact stages
to the initiating player and topology changes to affected projections.
Sequence or revision data must make duplicate, reordered, or delayed events
harmless. A player must never receive coordinates, wall state, impact
progress, or decorations outside that player's current allowed projection;
observers remain read-only and may receive the full authoritative state.
- Reconcile the mechanic with Milestone 020 feedback: impacts one through four
replace the generic blocked shake with increasingly strong but restrained
bush bending, rustling, and leaf-shedding cues; the fifth uses a short parting
or flattening animation, a bounded leaf burst, and immediate movement through
the new gap. No warning toast or engineering-style text is shown.
- Make the permanent opening visually read as a trampled bush passage when it
is inside the current server-approved projection. Use offline CSS/SVG assets,
bounded DOM nodes or canvas work, no runtime downloads, and no unbounded
particle allocation.
- Coalesce rapid feedback and cancel transient effects on pause, disconnect,
result, screen change, projection replacement, and application reset. Sound
and vibration remain optional enhancements and cannot be the only feedback.
- In reduced-motion mode, show server-approved static deformation stages and a
final non-animated opened-bush state without shaking, swaying, or flying
particles. Preserve clear color-independent silhouettes and contrast.
- Keep the mechanic equally available to both player roles with identical
thresholds and timing. Add deterministic checks that it introduces no
role-specific rule or information advantage even though player actions may
make the live topology asymmetric after match start.
- Preserve the existing firmware, compressed web-asset, and LittleFS budgets.
### Deliverables
- Updated gameplay, authority, projection, protocol, accessibility, and visual
contracts in canonical and supporting documentation.
- Bounded authoritative per-player perseverance tracking with an exact
five-impact threshold and 3,000 ms expiry.
- Atomic permanent interior-wall opening and fifth-attempt movement, including
shared bidirectional use and safe handling of game objects and results.
- Server-approved staged bush-impact feedback, final breakthrough animation,
permanent trampled-passage presentation, and reduced-motion equivalents.
- Deterministic host and browser tests plus representative two-player,
observer, mobile, reduced-motion, and physical-device verification evidence.
### Acceptance criteria
1. Four qualifying attempts against the same closed interior wall do not
change authoritative topology or player position. The fifth qualifying
attempt atomically opens that wall in both adjacent cells and moves the
initiating player exactly one cell through the new passage.
2. A perseverance streak increments only for server-accepted attempts from the
same player, cell, and cardinal direction against the same closed interior
wall. Tests cover exact threshold boundaries and every non-qualifying input
class.
3. The streak resets after a gap of 3,000 ms or more, any successful move, or
an attempted direction change, and on pause, disconnect, result, match
reset, and application reset. Tests cover just-inside, exact-boundary, and
just-outside timeout behavior using a deterministic clock.
4. Rate-limited, malformed, stale, duplicate, observer, and otherwise
unauthorized commands never weaken or open a wall. Duplicate or reordered
protocol events cannot replay an impact, animation, topology mutation, or
movement.
5. Outer-perimeter walls and invalid destinations can never be opened. The
fifth-attempt move obeys the same occupancy, object pickup, exit, visibility,
result, sequencing, and broadcast contracts as an ordinary successful move.
6. Once opened, the passage remains open bidirectionally until the active match
ends and either player can traverse it normally. Progress is not pooled
between players or opposite approaches, while completed topology is shared.
7. Player projections reveal only currently allowed cells and passage state.
Impact progress is sent only to the initiating player when permitted, and no
client can infer an unseen wall, remote impact, opening, object, or route;
observer sessions remain read-only.
8. Impacts one through four produce distinct, restrained server-approved bush
deformation cues whose intensity communicates progress without text. The
fifth produces a convincing short vegetation-parting or flattening effect,
a bounded leaf effect, and synchronized movement through the gap.
9. A breached wall inside the received projection is recognizable as a
permanent trampled-bush passage from either side. It remains understandable
without sound, vibration, text, or color alone and does not obscure the
player, objects, adjacent passages, controls, or top message rail.
10. Feedback uses a documented fixed maximum of timers, generated elements,
and particles per client. Repeated input coalesces safely, and all transient
effects clear on pause, disconnect, result, screen change, projection
replacement, and application reset without leaving stale transformations.
11. With reduced motion enabled, the same authoritative progress is clear from
static deformation stages and the final opened state; no shake, sway,
travel animation, or flying particle is required to understand the event.
12. Deterministic host tests cover counting, reset rules, timeout boundaries,
atomic topology mutation and movement, perimeter protection, two-player
independence and shared passage use, object/result interactions, message
ordering, bounded storage, symmetry of rules, and hidden-state protection.
13. Browser tests cover all five visual stages, event deduplication and
ordering, permanent passage rendering from both sides, effect cleanup,
supported mobile and desktop viewports, observer presentation, and
reduced-motion and non-color cues.
14. All host tests pass, compressed web assets remain within the 1.5 MiB budget
and LittleFS partition, firmware remains within the factory partition, and
physical-device testing confirms stable two-player play under repeated and
simultaneous wall impacts.
15. The project owner confirms on a representative mobile browser that five
impacts feel deliberate but not tedious, the bushes visibly resist before
giving way, the final breakthrough and movement feel synchronized, and the
permanent gap is obvious and enjoyable, or explicitly approves a
documented evidence deviation.
### Evidence
The following planning-time decisions define this milestone:
- On 2026-09-10, the project owner selected a permanent breakthrough rather
than a one-time squeeze or temporary opening. The owner accepted five hits
against one wall from one cell and one direction, with progress reset after
3 seconds, movement, or a direction change, and required the mechanic to be
always available to both players.
- Implementation, automated verification, physical-device checks, and owner
acceptance are deferred until all preceding milestones are `COMPLETE`.
+1
View File
@@ -288,6 +288,7 @@ test("player assets are local, Russian, accessible, and responsive", () => {
assert.match(html, /id="mute-toggle"/);
assert.match(css, /\.avatar-control \{[^}]*width: 96px; height: 96px/);
assert.match(css, /\.avatar-handle \{[^}]*transition: transform/);
assert.match(css, /\.avatar-handle \{[^}]*background: transparent/);
assert.match(script, /resetAvatarControl/);
assert.match(script, /playerCellSize/);
assert.match(css, /\(hover: none\) and \(pointer: coarse\) and \(max-height: 720px\)/);
+1 -1
View File
@@ -155,7 +155,7 @@ input:focus { border-color: var(--aqua); box-shadow: 0 0 0 3px #43ead229; }
.avatar-control { position: absolute; z-index: 8; left: var(--player-focal-x, 50%); top: var(--player-focal-y, 50%); width: 96px; height: 96px; margin: -48px; padding: 0; border: 0; border-radius: 50%; background: transparent; cursor: grab; touch-action: none; user-select: none; }
.avatar-control:active, .avatar-control.active { cursor: grabbing; }
.avatar-handle { position: absolute; left: 50%; top: 50%; width: 52px; height: 52px; display: grid; place-items: center; border: 3px solid currentColor; border-radius: 50%; background: #0b0712; color: var(--p1); box-shadow: 4px 4px 0 #000, 0 0 0 3px #43ead236; font-size: 1.25rem; font-weight: 1000; transform: translate(-50%, -50%); transition: transform .12s steps(3, end); }
.avatar-handle { position: absolute; left: 50%; top: 50%; width: 52px; height: 52px; display: grid; place-items: center; border: 3px solid currentColor; border-radius: 50%; background: transparent; color: var(--p1); box-shadow: 4px 4px 0 #000, 0 0 0 3px #43ead236; font-size: 1.25rem; font-weight: 1000; transform: translate(-50%, -50%); transition: transform .12s steps(3, end); }
.player-two-mode .avatar-handle { color: var(--p2); }
.avatar-control.active .avatar-handle { transition: none; }
.avatar-player-icon { width: 74%; height: 74%; }