From bb5bd73ebdb345b858a4e68206996b3be793fe57 Mon Sep 17 00:00:00 2001 From: sasa Date: Thu, 10 Sep 2026 22:38:48 +0300 Subject: [PATCH] fix(player): make avatar handle transparent --- PLANS.md | 167 +++++++++++++++++++++++++++++++++++- test/web/test_player_app.js | 1 + web/player.css | 2 +- 3 files changed, 165 insertions(+), 5 deletions(-) diff --git a/PLANS.md b/PLANS.md index d2994a8..c0f5824 100644 --- a/PLANS.md +++ b/PLANS.md @@ -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`. diff --git a/test/web/test_player_app.js b/test/web/test_player_app.js index a372ad8..db4d066 100644 --- a/test/web/test_player_app.js +++ b/test/web/test_player_app.js @@ -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\)/); diff --git a/web/player.css b/web/player.css index e884fbf..7fcbb78 100644 --- a/web/player.css +++ b/web/player.css @@ -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%; }