While a structure is being placed, the land beyond reach draws dimmed #133

Owner

Tracker: #3612

Step 6 of plan #3558 (decision D2, refusal row 14). While the player arms a structure (Factory, Turret, Airport, Helipad, Silo or Shipyard), the ground and water beyond their reach draw dimmed. Disarming, placing, a right-click, arming a house or aiming the bomb clears it.

What it delivers

  • The mask is the shared rule's reach refusal. beyondReach in src/shared/site.ts marks every map tile that lies beyond STRUCTURE_RANGE of the ground of every city the player holds. Those are the tiles where inReach fails, which siteRefusal refuses as SiteRefusal.Reach when no earlier refusal (terrain, a held tile, a street, a plot or the block) applies. heldLayouts names the cities a player holds, and both the replica's site view and the veil call it.

  • The veil (a new src/client/render/reach.ts) is one plane over the map, at TILE_TOP + 0.05. That is above the land's top and a street's paving, and under every unit, tree and building. It draws black at 45% over each tile beyond reach, with crisp tile edges (nearest filtering). It runs SEA_MARGIN past the map's edges. Its texture holds the map's tiles inside a one-texel border at full dim, and the clamped texture carries that border across the margin, so every point off the map draws dimmed: nothing can be placed there.

    • It recomputes its mask and re-uploads its texture only when the grounds of the cities the player holds change, as a city is captured or lost. Each frame while armed it only compares those grounds with the ones it last drew.
    • It draws after the ground's scars and craters, which it dims, and before every other see-through layer, so the placement ghost draws over it undimmed.
  • Nothing draws at rest. The veil is hidden until a structure is armed. Its program is compiled with the view (three r186's compile prepares hidden meshes too), so arming adds no shader program.

  • Arming (src/client/input/pointer.ts): the Commander's onPlacing hook shows the veil when a structure is armed and hides it when placing changes to anything else. GameView.render derives the held cities from the frame's players, cities and layouts.

  • The Map Yard gains &placing=<type>, a structure's UnitTypeId (for example &placing=4 for a Factory). Once the yard settles, it dims the land beyond player 1's reach, as arming that structure does, and holds. The view and finish links keep it, and ?bench benches it.

  • The help's Build line and changelog.md record it.

  • The HUD layout phone test (src/client/hud.phone.ts) runs each scene as its own test on each screen, on one page per screen, instead of one test looping over all 29 scenes. Each test then takes a fraction of vitest's 5 s timeout inside the full phone suite. This is bug #3419. At 375b644 it failed run 235's second full, the gate step's full (job 47), run 239's three quiet full runs (at 5002–5006 ms) and run 241's own phone suite. Run 233's full at 942a9c5 and run 235's third full at 375b644 passed it.

Evidence

  • Tests

    • src/client/render/reach.test.ts, on a 72×48 harness map with four towns, two of them near opposite corners so their reach runs off the map, and a sea:
      • The veil's mask equals siteRefusal(…) === SiteRefusal.Reach tile for tile, for a land structure on land and a shipyard on water.
      • It is recomputed (the texture's version moves) when a city is captured and when one is lost, and not for the same grounds in new objects.
      • A ray cast down at each tile's centre reads that tile's refusal from the texture, through the texture's transform, sampled by the nearest texel with the edges clamped as WebGL samples it.
      • With the corner towns held, so the reach meets all four edges, rays past each edge, 0.5 and SEA_MARGIN − 0.5 tiles out along its whole length, corners included, all read dimmed.
      • Mutants each fail it: unflipped texture rows, no hold check, the mask clipped at x = 1, the texture offset dropped. So do the reviewed veil without the border (all four edges read lit), the border left at 0, the tiles not shifted one texel in, and the offset not shifted by one texel.
    • src/client/input/pointer.test.ts: arming a Factory shows the veil. It hides when the Build tile is pressed again, on a right-click on the map, on the Launch plate and when Building is armed. Arming a Turret and placing it hides it too. All 5 cases fail on the pointer without the hook.
    • src/client/map.phone.ts, in every finish at 0.5× under reduced motion: &placing=4 adds exactly one draw call and no shader program from the settled yard to the held frame. The lush probe, which lies beyond player 1's reach, draws at 0.5–0.6 of its brightness at rest.
  • Map Yard renders (WebKit, iPhone 15 landscape, reduced motion), every finish at 1×, 0.5× and the overview, at rest and with &placing=4: no page error, and in each finish and view the placing load draws one more call and two more triangles than the rest load, with the same program count.

  • Off the map (?land, where player 1's towns at 5,15, 79,6, 118,20 and 116,35 reach the west, north and east edges): every finish at the overview, view=edge and 0.25×, for the reviewed veil at 942a9c5 and this head with &placing=4, and this head at rest.

    • The reviewed veil drew lit bands out of the NE and E edges across the margin, and in the edge view the margin past the east edge was split into a lit and a dimmed part. With the border, the whole margin draws dimmed.
    • In the edge view, an off-map box past the east edge reads a brightness of 74 in every pixel at rest. With the reviewed veil it read 41–74 (mean 68.2), and with the border it reads 41 in every pixel (0.55 × 74), in all four finishes.
    • The border changes no draw: calls, triangles and programs are the same as the reviewed veil's in every finish and view.
  • Draw checks (the standing Map Yard WebKit bench, iPhone 15 landscape, default finish), run at 942a9c5. The border since grows only the veil's texture, from 128² to 130² texels, and adds no call or triangle, so they were not rerun. There were 5 sessions of 7 interleaved runs per arm, 1,155 pages in all.

    • The arms were base 4c87e97, head 942a9c5, head ?placing=4 (Factory), and ?doom=battle, ?doom=battle&spare, ?doom=nuke and ?doom=nuke&spare on both base and head.
    • Both builds came from git archive trees served by vite preview. Figures are medians of the 35 runs per arm, in ms a frame, with the phone estimate (7.5 × the delta) in brackets.
    view base head (at rest) head − base placing placing − head
    1× 1.490 1.487 −0.003 (−0.03) 1.510 +0.023 (+0.18)
    0.5× 2.213 2.213 +0.000 (0.00) 2.230 +0.017 (+0.12)
    0.25× 2.670 2.677 +0.007 (+0.05) 2.673 −0.003 (−0.03)
    • At rest: head draws the same calls and triangles as base in every view, 67 / 65 / 41 calls. Its frame time is within ±0.007 ms of base's.

    • Placing: the veil adds 1 call and 2 triangles. It costs at most +0.023 ms a frame (+0.18 ms on a phone). The per-session deltas at 1× were +0.020 to +0.030 ms.

    • Destruction peaks (doom − spare, head against base):

      view battle, head battle, base nuke, head nuke, base
      1× +0.073 +0.077 +0.063 +0.067
      0.5× +0.083 +0.083 +0.037 +0.063
      0.25× +0.107 +0.120 +0.093 +0.093

      Each peak's head − base gap is within ±0.03 ms (±0.2 ms on a phone).

  • Gates on 375b644, on s32:

    • sak-gate fast run 1 failed npm test 882/883, only on craft's "lands two gunships on their helipad…" with "Test timed out in 5000ms", the known s32 contention timeout. That file passed alone, 22/22 in 3.2 s.
    • sak-gate full run 2 passed npm test 883/883. test:phone gave 66/67, failing only on the HUD 1280×720 layout timeout, bug #3419. Every Map Yard test passed, the ?placing test among them. hud.phone.ts run alone then passed 31/31 in 20 s.
    • sak-gate full run 3 passed: npm test 883/883 in 28.6 s, and test:phone 67/67 in 202 s.
  • Gates after the gate step's full (job 46 on s32 at 375b644, which failed npm test 876/883). Every failure was "Test timed out in 5000ms" or hud-mock's follow-on assertions after its scene loop timed out: hud-css text contrast, hud-mock's scene loop, two craft gunship-landing tests and collapses' no-scale test. A later run on the same head, with no code change:

    • The timed-out files each passed alone in 1.1–3.3 s.
    • sak-gate full runs 1 and 2 started while Spotlight indexing and another session's Gradle build loaded s32. Both failed npm test on the same kind of timeouts, at 47.7 s and 44.5 s. In the same window, the base tree (4c87e97) and the head each failed craft's gunship-landing timeout, at 36.4 s and 37.2 s.
    • sak-gate full runs 3, 4 and 5 started on a quiet machine. Each passed npm test 883/883 in 27.6–29.6 s. Each gave test:phone 66/67, failing only on the HUD 1280×720 layout timeout (bug #3419) at 5002–5006 ms, and every Map Yard test passed.
    • Run alone, hud.phone.ts passed 31/31 in 20–23 s, with the 1280×720 case taking 2.8 s. The base tree's phone suite failed the same test at 5002 ms. This branch changes no HUD code: its only HUD-visible change is one sentence in the help's Build line.
  • The HUD layout test split (#3419), on s32:

    • Before, at 375b644: the gate step's full (job 47) and this run's own full phone suite each gave 66/67, failing only the 1280×720 loop over 29 scenes at 5002 ms, as run 239's three full runs on a quiet machine had.
    • After, at 546c7da: the full phone suite passed 179/179, the 4 loop tests having become 116 scene tests. Under the full suite the 1280×720 screen's 29 scenes summed to 5.2 s, more than the old single test's 5 s limit. The slowest scene took 654 ms (base, the first, which loads the mock cold), and the next 349 ms. Run alone, hud.phone.ts passed 143/143 in 19.4 s.
    • With factory taken out of the scenes that must show Selected and a control that does not exist added to base's, 8 tests failed: the base and the factory scene on each screen, each naming its fault.
  • Gates on 546c7da, on s32:

    • sak-gate full run 1 passed: npm test 883/883 in 29.0 s, and test:phone 179/179 in 204.6 s.
    • The gate step's full (job 48) failed npm test 882/883 in 37.6 s, only on craft's "lands two gunships on their helipad…" with "Test timed out in 5000ms". This is the load timeout of bug #3595.
    • On the same head, with no change, a later run's sak-gate full run 1 passed: npm test 883/883 in 27.7 s, and test:phone 179/179 in 202.4 s.
Tracker: [#3612](https://sak.swipelab.com/tasks/3612) Step 6 of plan [#3558](https://sak.swipelab.com/tasks/3558) (decision D2, refusal row 14). While the player arms a structure (Factory, Turret, Airport, Helipad, Silo or Shipyard), the ground and water beyond their reach draw dimmed. Disarming, placing, a right-click, arming a house or aiming the bomb clears it. ## What it delivers - **The mask is the shared rule's reach refusal.** `beyondReach` in src/shared/site.ts marks every map tile that lies beyond `STRUCTURE_RANGE` of the ground of every city the player holds. Those are the tiles where `inReach` fails, which `siteRefusal` refuses as `SiteRefusal.Reach` when no earlier refusal (terrain, a held tile, a street, a plot or the block) applies. `heldLayouts` names the cities a player holds, and both the replica's site view and the veil call it. - **The veil** (a new src/client/render/reach.ts) is one plane over the map, at TILE_TOP + 0.05. That is above the land's top and a street's paving, and under every unit, tree and building. It draws black at 45% over each tile beyond reach, with crisp tile edges (nearest filtering). It runs `SEA_MARGIN` past the map's edges. Its texture holds the map's tiles inside a one-texel border at full dim, and the clamped texture carries that border across the margin, so every point off the map draws dimmed: nothing can be placed there. - It recomputes its mask and re-uploads its texture only when the grounds of the cities the player holds change, as a city is captured or lost. Each frame while armed it only compares those grounds with the ones it last drew. - It draws after the ground's scars and craters, which it dims, and before every other see-through layer, so the placement ghost draws over it undimmed. - **Nothing draws at rest.** The veil is hidden until a structure is armed. Its program is compiled with the view (three r186's `compile` prepares hidden meshes too), so arming adds no shader program. - **Arming** (src/client/input/pointer.ts): the Commander's `onPlacing` hook shows the veil when a structure is armed and hides it when placing changes to anything else. `GameView.render` derives the held cities from the frame's players, cities and layouts. - **The Map Yard** gains `&placing=<type>`, a structure's `UnitTypeId` (for example `&placing=4` for a Factory). Once the yard settles, it dims the land beyond player 1's reach, as arming that structure does, and holds. The view and finish links keep it, and `?bench` benches it. - The help's Build line and changelog.md record it. - **The HUD layout phone test** (src/client/hud.phone.ts) runs each scene as its own test on each screen, on one page per screen, instead of one test looping over all 29 scenes. Each test then takes a fraction of vitest's 5 s timeout inside the full phone suite. This is bug [#3419](https://sak.swipelab.com/tasks/3419). At 375b644 it failed run 235's second `full`, the gate step's `full` (job 47), run 239's three quiet `full` runs (at 5002–5006 ms) and run 241's own phone suite. Run 233's `full` at 942a9c5 and run 235's third `full` at 375b644 passed it. ## Evidence - **Tests** - src/client/render/reach.test.ts, on a 72×48 harness map with four towns, two of them near opposite corners so their reach runs off the map, and a sea: - The veil's mask equals `siteRefusal(…) === SiteRefusal.Reach` tile for tile, for a land structure on land and a shipyard on water. - It is recomputed (the texture's version moves) when a city is captured and when one is lost, and not for the same grounds in new objects. - A ray cast down at each tile's centre reads that tile's refusal from the texture, through the texture's transform, sampled by the nearest texel with the edges clamped as WebGL samples it. - With the corner towns held, so the reach meets all four edges, rays past each edge, 0.5 and `SEA_MARGIN` − 0.5 tiles out along its whole length, corners included, all read dimmed. - Mutants each fail it: unflipped texture rows, no hold check, the mask clipped at x = 1, the texture offset dropped. So do the reviewed veil without the border (all four edges read lit), the border left at 0, the tiles not shifted one texel in, and the offset not shifted by one texel. - src/client/input/pointer.test.ts: arming a Factory shows the veil. It hides when the Build tile is pressed again, on a right-click on the map, on the Launch plate and when Building is armed. Arming a Turret and placing it hides it too. All 5 cases fail on the pointer without the hook. - src/client/map.phone.ts, in every finish at 0.5× under reduced motion: `&placing=4` adds exactly one draw call and no shader program from the settled yard to the held frame. The lush probe, which lies beyond player 1's reach, draws at 0.5–0.6 of its brightness at rest. - **Map Yard renders** (WebKit, iPhone 15 landscape, reduced motion), every finish at 1×, 0.5× and the overview, at rest and with `&placing=4`: no page error, and in each finish and view the placing load draws one more call and two more triangles than the rest load, with the same program count. - **Off the map** (`?land`, where player 1's towns at 5,15, 79,6, 118,20 and 116,35 reach the west, north and east edges): every finish at the overview, `view=edge` and 0.25×, for the reviewed veil at 942a9c5 and this head with `&placing=4`, and this head at rest. - The reviewed veil drew lit bands out of the NE and E edges across the margin, and in the edge view the margin past the east edge was split into a lit and a dimmed part. With the border, the whole margin draws dimmed. - In the edge view, an off-map box past the east edge reads a brightness of 74 in every pixel at rest. With the reviewed veil it read 41–74 (mean 68.2), and with the border it reads 41 in every pixel (0.55 × 74), in all four finishes. - The border changes no draw: calls, triangles and programs are the same as the reviewed veil's in every finish and view. - **Draw checks** (the standing Map Yard WebKit bench, iPhone 15 landscape, default finish), run at 942a9c5. The border since grows only the veil's texture, from 128² to 130² texels, and adds no call or triangle, so they were not rerun. There were 5 sessions of 7 interleaved runs per arm, 1,155 pages in all. - The arms were base 4c87e97, head 942a9c5, head `?placing=4` (Factory), and `?doom=battle`, `?doom=battle&spare`, `?doom=nuke` and `?doom=nuke&spare` on both base and head. - Both builds came from `git archive` trees served by vite `preview`. Figures are medians of the 35 runs per arm, in ms a frame, with the phone estimate (7.5 × the delta) in brackets. | view | base | head (at rest) | head − base | placing | placing − head | |---|---|---|---|---|---| | 1× | 1.490 | 1.487 | −0.003 (−0.03) | 1.510 | +0.023 (+0.18) | | 0.5× | 2.213 | 2.213 | +0.000 (0.00) | 2.230 | +0.017 (+0.12) | | 0.25× | 2.670 | 2.677 | +0.007 (+0.05) | 2.673 | −0.003 (−0.03) | - **At rest:** head draws the same calls and triangles as base in every view, 67 / 65 / 41 calls. Its frame time is within ±0.007 ms of base's. - **Placing:** the veil adds 1 call and 2 triangles. It costs at most +0.023 ms a frame (+0.18 ms on a phone). The per-session deltas at 1× were +0.020 to +0.030 ms. - **Destruction peaks** (doom − spare, head against base): | view | battle, head | battle, base | nuke, head | nuke, base | |---|---|---|---|---| | 1× | +0.073 | +0.077 | +0.063 | +0.067 | | 0.5× | +0.083 | +0.083 | +0.037 | +0.063 | | 0.25× | +0.107 | +0.120 | +0.093 | +0.093 | Each peak's head − base gap is within ±0.03 ms (±0.2 ms on a phone). - **Gates on 375b644**, on s32: - `sak-gate fast` run 1 failed `npm test` 882/883, only on craft's "lands two gunships on their helipad…" with "Test timed out in 5000ms", the known s32 contention timeout. That file passed alone, 22/22 in 3.2 s. - `sak-gate full` run 2 passed `npm test` 883/883. `test:phone` gave 66/67, failing only on the HUD 1280×720 layout timeout, bug #3419. Every Map Yard test passed, the `?placing` test among them. `hud.phone.ts` run alone then passed 31/31 in 20 s. - `sak-gate full` run 3 passed: `npm test` 883/883 in 28.6 s, and `test:phone` 67/67 in 202 s. - **Gates after the gate step's `full`** (job 46 on s32 at 375b644, which failed `npm test` 876/883). Every failure was "Test timed out in 5000ms" or hud-mock's follow-on assertions after its scene loop timed out: hud-css text contrast, hud-mock's scene loop, two craft gunship-landing tests and collapses' no-scale test. A later run on the same head, with no code change: - The timed-out files each passed alone in 1.1–3.3 s. - `sak-gate full` runs 1 and 2 started while Spotlight indexing and another session's Gradle build loaded s32. Both failed `npm test` on the same kind of timeouts, at 47.7 s and 44.5 s. In the same window, the base tree (4c87e97) and the head each failed craft's gunship-landing timeout, at 36.4 s and 37.2 s. - `sak-gate full` runs 3, 4 and 5 started on a quiet machine. Each passed `npm test` 883/883 in 27.6–29.6 s. Each gave `test:phone` 66/67, failing only on the HUD 1280×720 layout timeout (bug #3419) at 5002–5006 ms, and every Map Yard test passed. - Run alone, `hud.phone.ts` passed 31/31 in 20–23 s, with the 1280×720 case taking 2.8 s. The base tree's phone suite failed the same test at 5002 ms. This branch changes no HUD code: its only HUD-visible change is one sentence in the help's Build line. - **The HUD layout test split (#3419)**, on s32: - Before, at 375b644: the gate step's `full` (job 47) and this run's own full phone suite each gave 66/67, failing only the 1280×720 loop over 29 scenes at 5002 ms, as run 239's three `full` runs on a quiet machine had. - After, at 546c7da: the full phone suite passed 179/179, the 4 loop tests having become 116 scene tests. Under the full suite the 1280×720 screen's 29 scenes summed to 5.2 s, more than the old single test's 5 s limit. The slowest scene took 654 ms (base, the first, which loads the mock cold), and the next 349 ms. Run alone, hud.phone.ts passed 143/143 in 19.4 s. - With `factory` taken out of the scenes that must show Selected and a control that does not exist added to base's, 8 tests failed: the base and the factory scene on each screen, each naming its fault. - **Gates on 546c7da**, on s32: - `sak-gate full` run 1 passed: `npm test` 883/883 in 29.0 s, and `test:phone` 179/179 in 204.6 s. - The gate step's `full` (job 48) failed `npm test` 882/883 in 37.6 s, only on craft's "lands two gunships on their helipad…" with "Test timed out in 5000ms". This is the load timeout of bug [#3595](https://sak.swipelab.com/tasks/3595). - On the same head, with no change, a later run's `sak-gate full` run 1 passed: `npm test` 883/883 in 27.7 s, and `test:phone` 179/179 in 202.4 s.
agra merged commit ebf9fb66fe into task/3558-build-inside-the-city-and-a-flat-map 2026-10-23 14:40:23 +03:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
agra/mini-alert!133
No description provided.