diff --git a/docs/hails-additions/09-water.md b/docs/hails-additions/09-water.md index 4be9eed2..7ab7d8a2 100644 --- a/docs/hails-additions/09-water.md +++ b/docs/hails-additions/09-water.md @@ -1,6 +1,6 @@ # Water -Branch: `gameplay/water`. Status: **water bail and animated water confirmed in play. Splash/ripple effects not implemented.** +Branch: `gameplay/water`. Status: **behaviour matched to retail footage (shallow water solid, deep water floats, board floats, water camera with vignette, entry splash, `water.alpha` look approved in play; small bodies calmer and waves slowed, awaiting check).** ## Problem @@ -124,11 +124,220 @@ WipeoutGround, where `Collision+3481` starts the water ragdoll and the respawn timer. Logged as `WATER_BAIL tick=… state=…`. **Project choice**, not recovered retail behaviour. -Not changed: water stays solid. The water buoyancy code -(`wipeout_state::body::special_surface`, which pushes parts up when below -height + 0.1) suggests that in retail the body sinks slightly into water, so -water may not be solid for the body in retail. Left alone until play shows -whether that matters. +### Water is not solid (skater sinks in and floats, board sinks) + +User, from retail footage: the skater sinks into the water and floats; the board +sinks. One retail video frame shows the underwater-skating glitch (riding the +floor under the water surface), so the surface itself does not block anything. +The wipeout buoyancy code (`wipeout_state::body::special_surface`, pushing parts +up when below height + 0.1) agrees. + +- `skate-core` `board_world.rs`: `is_water_tag` (type 12), `FLOAT_DEPTH = 0.5`. + **Water is not solid:** `query_primitives` (the only path that makes physical + contacts, for board and skeleton) skips water triangles, so bodies pass + through the surface and rest on whatever lies under it. Ray, line and + trajectory queries are untouched, so the wipeout's water trajectory check and + respawn checks still see all water. `water_surface_at(point, above, + max_depth)` returns the water height over a point (XZ barycentric on water + triangles); `water_shallow_at` tells whether geometry lies within + `FLOAT_DEPTH` under a surface point; `deep_water_surface_at` combines them. + History: making shallow water solid (first per triangle, then per contact + point) stopped the jitter but the body lay on top of the water like glass + (user: "looks quite bad"); the jitter was the buoyancy, not the non-solid + surface, so the surface is non-solid everywhere again and only buoyancy is + limited to deep water. +- Water used to be detected from contact reports, which no longer exist for deep + water. `skate-game` `physics/water.rs` restores the same signals from + position: `mark_board` (after the board ground state is rebuilt) sets + collision bit 25 and `surface_twelve_height`; `mark_skater` (end of skeleton + feedback, before the postphysics wipeout checks) sets `material_12` and its + height. Any water counts here, shallow or deep, so all water still bails. A + body counts as in water from 0.05 m above the surface down to 4 m below. +- **Final rule, from retail footage (RPCS3, University channel):** in the 5–10 cm + channel retail bails you and you **lie on top of the water** (body and board on + the surface, water camera, respawn ~3.5–4 s, no splash); in the deep Aletown + canal you sink in and float. So water is **solid where shallow** and non-solid + where deep, decided per contact point: `query_primitives` drops a water contact + only if `water_shallow_at(contact point)` finds no geometry within `FLOAT_DEPTH` + (0.5 m) under it. On-foot support uses the same rule (`contact_toolkit` line hit + point / nearby centre, `ground_query` lines via `Mesh.world`). Body buoyancy and + the board's buoyancy/drag apply only over deep water (`deep_water_surface_at`), + so nothing pushes a body resting on solid shallow water (no jitter). Traces: + University channel, lowest body part 67.996 on a 67.94 surface, calm (0.06 m/s); + Aletown, body floats (8.49–9.04 vs 8.93), board floats (8.91). The two + intermediate versions below (shallow water solid per triangle; then all water + non-solid with a "floor pass" for bodies in water) are superseded; the + floor-pass version was removed. +- *(superseded)* **Shallow floors:** a body lying in University's 5–10 cm channel + still looked like it lay on top of the water (user GIFs). While a body is in + water, geometry within `FLOAT_DEPTH` (0.5 m) under a water surface no longer + holds it: `BoardWorld::set_water_floor_pass`, set by `solve.rs` for the board + query when a board part is in water and for the skeleton query during a water + wipeout (`special_surface`). The body sinks past the channel bottom and floats + like in deep water (trace: parts 67.43–68.05 against a 67.94 surface, settled + bobbing 0.28 m/s, same as the reservoir); the board sinks to the plaza below + (66.22). Ground deeper than 0.5 m still holds. Buoyancy and board drag apply + in any water again (the deep-only masking below was an intermediate step). +- The wipeout buoyancy runs per part only where the water is deep + (`special_surface_where`, `water::part_floats`). User report: in the shallow + channel the body jittered; a part resting on the floor 5 cm under the surface + was pushed up every step (the retail push includes a constant +0.1 term), + lifted off, lost the push and its water drag at the line, fell back, repeat. + Masked, the body lies in the channel on the concrete with its parts half + under the water (part centres at the surface), calm (headless trace: mean + part speed 0.05 m/s once settled). Deep water is unchanged: the body floats about 1 m + deep with the top just above the surface (reservoir trace). +- `apply_board_drag` (just before the solve, after every state sets its drag): + board parts over deep water get at least 0.1 linear/angular drag per step and + no buoyancy, so the board sinks slowly (reservoir: ~1.5 m/s, settles on the + bottom). Project-chosen value. +- DownTown's reachable deep water: the Aletown canal next to the Aletown + spawn (user, from the PS3 version in RPCS3): open water at 8.93 m, 1.12 m deep, + 3.3 m below the quay, x -125..-205, z 436..490. Trace: body floats (parts + 8.50–9.02), board on the canal bed (7.90), camera 1.8 m above. Water triangles a + global scan reports as 20 m deep elsewhere in DownTown lie under walkable ground + (buried planes), not open water. University's reservoir is out of bounds in + retail (user). +- **Retail reference (PS3 version in RPCS3, Aletown canal, user GIFs):** the + board **floats** at the surface next to the skater (an earlier YouTube clip + suggested it sinks; the emulator footage is the reference); the body floats + spread-eagle just under semi-transparent water; a white mist plume plus droplet + sprites, ~3-4 m across, lasts ~0.7 s, with a small puff where the board hits + first; no ripple rings; the camera goes high above and behind, looking down + ~60 degrees and drifting toward overhead, with a dark rounded vignette; + respawn after ~1-3.5 s; Thrasher "Hall of Meat" points count while floating. + Retail water close up: even steel blue-grey with fine dark streaks + (~10-20 cm) that shimmer in place (PCA loop) with a slow drift. +- **Board floats** (`apply_board_drag`): besides water drag, board parts in + water get buoyancy that cancels gravity with the part centre at the surface, + rising linearly to 3 g at 5 cm below and zero 5 cm above. Trace (Aletown): + deck settles at 8.91 against an 8.93 surface. Project-chosen values. +- **Ripple scale:** family 33 (`water.alpha`: DownTown fountains, Aletown canal) + uses 4x the decoded normal-map scale. Compared with `--verify` captures (scratch + camera mod aimed at the canal) against the RPCS3 frames: 1x gives ~1 m blobs, + 4x matches the streak size, 8x is too fine and noisy. Colour/contrast still + differ (ours darker navy with bright reflection bands; retail an even steel + blue). Project choice; the user asked for a loose match. +- **Water camera (final):** `camera/water.rs` `WaterView`, applied to the final + frame in `CameraRuntime::advance`. Driven by the physics water state, not + proximity: `physics/camera_output.rs` passes the wipeout's `surface_height` + while in a water bail (`WipeoutGround` + `special_surface`). User report: the + earlier proximity test (water surface within 0.3 m under the root) fired while + standing on DownTown's fountain walkway, because water triangles reach under + it. The shot: 3 m above the water, 1.75 m behind the skater (60 degrees down), + drifting to 0.9 m (about 73 degrees) over 6 s, aimed 0.2 m above the root; + blends in over 0.25 s and out over 0.5 s; direction fixed at entry. A camera + under any water surface is still lifted 0.3 m above it. +- **Vignette:** the shot's weight (x0.85) goes through + `retail_exposure` (`Settings.timing.z` -> meter compute -> `state.w`) to the + tone pass (`retail_tone.wgsl`), which darkens the edges + (`smoothstep(0.55, 1.3, |uv*2-1|)`), corners nearly black as in retail. +- **Splash:** `water_splash.rs` (`WaterSplashPlugin`). Setup extracts the + game's particle sprites from the owned disc + (`tools/asset_pipeline/particles.py`, environment group: `miscboot.big` + `particletextures.rx2`, names from the `.Texture` strings in texture + order: steam, dust, pebble, grass, leaf, fluff, cameraflash, water) to + `assets/private/particles/*.png`. On the frame the skater root or the board + deck enters **deep** water moving down faster than 1 m/s, a plume of `water` + sprites spawns at the surface: 13 x strength sprites (strength = speed/8, + 0.4-1.2; board x0.45), random roll and spin, rising 1.6-3.8 m/s, spreading + 0.4-1.6 m/s, drag and light gravity, growing 0.8-1.3 -> 1.8-2.8 m, alpha + 0.45-0.72, life 0.55-0.85 s; unlit alpha-blended `StandardMaterial` quads + billboarded to the gameplay camera. No splash in shallow water (retail shows + none). A first version with a generated soft blob plus droplet dots "looked + like snow" (user); the retail sprite fixed that; the first sprite version was + "a bit strong" and was reduced (18 -> 13 sprites, lower alpha/brightness). + Without the sprite file there is no splash (logged). +- **Water colour (tried, rejected):** adding a steel-blue body colour under the + lighting and toning reflections down (strengths 1-3) made the water milky and + flat; the user judged it worse, so it was removed. +- **Water look (contrast, not colour):** measured water-only crops of the + RPCS3 canal frame (`rpcs3_2lxnl5eGDi.gif` frame 0) against our capture from + the same side of the canal. The mean colour already matched, which is why + adding a body colour only washed it out. Contrast was the difference: + + | | mean sRGB | luma p5/p50/p95 | broad stdev (1/8 scale) | fine detail (vs 4 px blur) | + |---|---|---|---|---| + | retail | 71, 84, 95 | 56 / 84 / 93 | 6.3 | 5.3 | + | before | 81, 89, 100 | 30 / 89 / 140 | 28.7 | 11.8 | + | final | 75, 89, 100 | 74 / 86 / 105 | 7.1 | 5.2 | + + Rendering one shader term at a time showed that the broad bright and dark + bands come from the environment-cube reflection. The lightmap, diffuse and + alpha terms are even. Final changes, family 33 only (`retail_world.wgsl`, + all project choices matched to the footage and approved by the user in play): + - **Cube blur:** the cube is read 3 mips blurrier. Tested +2/+3/+3.5/+4/+6/+9: + +3 matched the broad and fine statistics, and +4 or more was flat. + - **Tint:** the reflection is multiplied by (0.82, 1.0, 1.04). Unmodified, it + read grey (user: "not quite the same colour"). + - **Anti-tiling and swells:** the normal used one fine layer at the 4x ripple + scale, which tiled visibly over large stretches (user screenshots of the + DownTown fountain). The normal is now the fine PCA wave plus the same wave + from a second sample (rotated 37 degrees, 0.613x scale, offset), plus 2x + the large slow layer's deviation (broad swells). The user picked this + variant ("less flat water looked really good"). + - **Smaller bodies move less (user request):** `water_bodies.rs` groups the + triangles of family 33 materials that share vertex positions into bodies, + because a map splits one body into chunks on a 100 m grid. Each material + gets its body's area in `WorldParams.decal.y`. The shader scales + `size = log2(area / 100) / log2(40)` (0 at 100 m², 1 at 4000 m² and up): + fine waves x(0.6 + 0.4 size), swells x size². DownTown bodies: Aletown + canal 4368 m² (5 chunks, full motion), DownTown fountain 1268 m² + (size 0.69), others 99-4599 m². University's fountain channel is family + 30 and unchanged. + - **Slower waves (user request: canal "a tiny bit too fast"):** the PCA wave + animation (30 frames at 30 Hz, shared with the ocean) now has a second copy + for family 33, `FrameStateData.pca_slow`. It runs at 0.75x + (`WATER_PCA_RATE`), interpolated between frames (PCA weights combine + linearly) and looping from the last frame into the first. The ocean keeps + the retail rate. The frame state grew from 144 to 256 bytes. + - **Tried and dropped:** + - Dark streaks from the reflection dipping 0.05 above flat. They matched the + canal statistics, but at steeper views (the fountain) they became + repeating blotches. + - A version keyed on normal tilt gave crescent shapes. + - A horizon threshold on the reflection elevation never triggered. + - Method: a temporary env hook in `FrameStateData.clock.w` picked the term or + variant without a rebuild, then was removed. The statistics script crops + water only, with no walls or walkway. Body areas came from a temporary + log. +- **Captures without the user:** `SKATE_VERIFY_AT` (seconds, default 4) sets when + `--verify` takes its screenshot. With a scratch mod that teleports the skater + into the canal (or calls `sdk.camera.set`) this captured the splash, camera + and vignette at chosen moments. +- Trace note: skeleton part 0 ("root") does not collide; judge penetration by + parts 1..23 (an early reading of root height wrongly suggested tunnelling). + +Maps without water have an empty water list, so nothing changes there +(`check_maps.py`: all 10 maps ok, collision triangle counts unchanged). + +First in-play test of this (user GIF, walking into the DownTown fountain on +foot): the skater stood on the water briefly ("like cement"), then sank, and +the camera followed the body under the surface, showing only the underside of +the water. Two more changes: + +- **On foot:** on-foot ground support does not come from contacts but from + query scenes, which still treated water as floor. They now skip water too: + `offboard/contact_toolkit/world.rs` (`line`, `nearby`) and + `offboard/ground_query/lines.rs` (biped ground lines). +- **Camera:** `camera/water.rs` `WaterView`, applied to the final frame in + `CameraRuntime::advance`. While the skater root is in water (from 0.3 m above + water (any depth: a deep-only version let the low bail shot dip under the + 5 cm channel's surface, user GIF), the camera eases to at least 1.8 m above the surface, 3 m + further back horizontally (first tuned 1.2 m / 1.5 m; user: still too close), and is re-aimed at the skater (blended by how far it + moved). A camera under water for any other reason is lifted 0.3 m above the + surface. It lifts fast (0.1 s) and settles back slowly (0.35 s). Values are + project choices; the retail footage only shows the camera pulled back above + the water. + +Headless trace (`tests/water_drop.rs`, ignored diagnostic: drop into +University's fountain basin): the bail starts at the surface; the body sinks to +about 0.5 m, buoyancy brings it back to the surface, and it settles about 0.7 m +down, with the top of the body just above the water, until the respawn. The +camera ends at surface + 1.2 m (before the retune). At that spot the board rests on a solid concrete +ledge 5 cm below the water surface (`query_thin_line` under it: water 67.942, +concrete 67.888, floor 66.099), so it does not sink further; elsewhere it sinks +to the floor. ### Water rendering: extract the ocean animation table from default.xex @@ -183,6 +392,16 @@ that in `clock.z`, used only by the water path (families 30/33); family 14 keeps real time (its retail time source is unconfirmed). Behaviour re-implemented, not copied; the disassembly was reference only. +The 5-unit restart then showed as a hard reset every 10 s (user): each scroll +layer jumps by speed × 5 (0.05 tile for still water, 0.5 tile for flowing +water's 0.1 layer). The PCA animation is not involved (its 29→0 step equals a +normal frame step). Project choice: keep the half-speed rate but loop over +1000 units (≈33 min). Every authored scroll speed (render-parameters +`water[1]`: 0.01, 0.2, 0.1; ocean 0.4, 0.22) is a multiple of 0.001, so each layer +completes whole tiles per period and the restart is invisible; t stays small +for f32 precision. A unit test checks the rate and that every authored speed is +seamless. + ### University water looks darker than DownTown's (data, not a bug) `RENDER_AT` shows the materials at each test spot: University fountain @@ -209,7 +428,7 @@ No retail reference was found to compare; the user accepted the look for now. - `tools.asset_pipeline.test_ocean_pca`: runs `convert` through the real `spawn()` (text-mode pipes) for success and failure. - `retail_render` tests pass (water time unit test included) except - `sky_shader_validates`, a known upstream failure. + `sky_shader_validates`, which was a known upstream failure (since fixed, doc 10). - Asset-backed wipeout tests (`--ignored wipeout`): 4 pass; `marker_reply_restores_on_foot` fails identically with these changes stashed (pre-existing). @@ -217,9 +436,8 @@ No retail reference was found to compare; the user accepted the look for now. 1. Where does retail write `Collision+3481` / `+28`? The wiring above is the best match to the evidence; it is not confirmed against retail. -2. Water triangles are solid one-sided surfaces in our collision world. Does the - retail board/body pass through type 12, or rest on it? (In play: does the - skater stand on the University reservoir?) +2. Answered: water is not solid in retail (user footage). Implemented above; drag + value and the 4 m depth limit are project choices. 3. Industrial's sea: retail respawned the player (user's memory), possibly out of reach in retail. No collision data exists for it; see section 4. 5. Water rendering (resolved, see above; kept for the record): static texture on the fountains, black on the @@ -233,9 +451,13 @@ No retail reference was found to compare; the user accepted the look for now. normal encryption and LZX compression, and setup never unpacks it. Fallback family 1 shows the static diffuse; `ocean.default` has no diffuse, hence black. This affects upstream users equally. -6. Splashes and impact ripples when the player hits water: not implemented. - The executable has a `cWaterEffect` class; the engine has no particle/effect - system for it yet. +6. Splash: done (entry plume from the retail sprite). Retail shows no ripple + rings. The Thrasher "Hall of Meat" counter seen while floating in retail is a + separate scoring feature, not checked here. +7. `water.alpha` look: blurred and tinted cube, anti-tiled normal with swells, + calmer small bodies, waves at 0.75x (see above). The user approved the + canal look; the size calming and slower waves are waiting on their in-play + check. 4. What is surface type 13 (DownTown 88, University 17,790 triangles)? It may be related (shallow water or a splash surface) or something unrelated, such as grass. @@ -245,8 +467,18 @@ No retail reference was found to compare; the user accepted the look for now. - `crates/skate-game/src/physics/player_input/mod.rs` (`publish_water`, unit test). - `crates/skate-game/src/physics/frame.rs` (call site). - `crates/skate-game/src/physics/wipeout.rs` (water bail request). -- `mods/water-test-teleport/` (dev-only test mod: F2/F3/F4 into the water, Shift+F2/F3/F4 view spots; not committed, not for upstream). -- `crates/skate-game/src/retail_render.rs` (frame state written in place, `water_time`, per-shader fallback log), `retail_world.wgsl` (water uses `clock.z`). +- `mods/water-test-teleport/` (dev-only test mod: F2/F3/F4/F7 into the water, Shift+key view spots, F5 position readout; the "Teleported" line clears after 5 s; committed on the fork for testing, dropped from the upstream branch). +- `crates/skate-core/src/physics/board_world.rs` (`is_water_tag`, water skip in `query_primitives`, `water_surface_at`) and `board_world/tests.rs` (`water_is_not_solid_but_reports_its_surface`). +- `crates/skate-game/src/physics/water.rs` (new), wired from `physics.rs` (`finish_skater`), `skeleton_feedback.rs` and `frame.rs`. +- `crates/skate-game/src/camera/water.rs` (new), `camera/runtime.rs` (`water` argument, `water_vignette`), `camera.rs`, `physics/camera_output.rs` (water bail state). +- `crates/skate-game/src/retail_exposure.rs`, `retail_exposure.wgsl`, `retail_tone.wgsl` (vignette). +- `crates/skate-game/src/water_splash.rs` (new), `main.rs`, `app.rs` (plugin). +- `tools/asset_pipeline/particles.py` (new), `test_particles.py`, `asset_exports.py`, `versions.py` (environment group). +- `crates/skate-game/src/verification.rs` (`SKATE_VERIFY_AT`). +- `crates/skate-game/src/physics/offboard/contact_toolkit/world.rs`, `offboard/ground_query/lines.rs` (water skipped for on-foot support). +- `crates/skate-game/src/tests/water_drop.rs` (ignored diagnostic), registered in `physics.rs`. +- `crates/skate-game/src/retail_render.rs` (frame state written in place, `water_time`, per-shader fallback log), `retail_world.wgsl` (water uses `clock.z`; family 33 ripple scale, cube blur and tint, anti-tiling and swells, size calming, `pca_slow`), `retail_material_bindings.wgsl` (`FrameState.pca_slow`), `retail_character.rs` (test initialiser). +- `crates/skate-game/src/water_bodies.rs` (new: water body areas, tests), `main.rs`. - `tools/asset_pipeline/test_ocean_pca.py`. - `crates/skate-data/src/xex/{mod,aes,lzx}.rs`, `crates/skate-data/src/ocean_pca.rs`, `crates/skate-data/src/lib.rs`. - `crates/skate-data/examples/xex_unpack.rs` (diagnostic: unpack + locate table). diff --git a/docs/hails-additions/PULL-REQUESTS.md b/docs/hails-additions/PULL-REQUESTS.md index e44b872c..f0455b03 100644 --- a/docs/hails-additions/PULL-REQUESTS.md +++ b/docs/hails-additions/PULL-REQUESTS.md @@ -14,6 +14,8 @@ verification, open questions). | D | Gameplay data | Skip collision volumes that have no surface IDs | [05](05-collision-volumes.md) | `dec7ade` | — | Draft upstream [#25](https://github.com/SK8-ENGINE/skate-3-rust-engine/pull/25) | | E | Performance | Indexed stock collection lookups (game startup −5.5 s) | [07](07-collections-index.md) | `810defa` | — | Draft upstream [#26](https://github.com/SK8-ENGINE/skate-3-rust-engine/pull/26) | | F | Tooling | Streaming map validator (`--validate-maps`) wired into setup | [06](06-map-validator.md) | `18f41a3` | E (speed only) | Draft upstream [#28](https://github.com/SK8-ENGINE/skate-3-rust-engine/pull/28) | +| H | Gameplay + rendering | Water: detection and bail, shallow water solid / deep water floats, board floats, water camera + vignette, entry splash, water look (family 33 cube blur + tint, anti-tiling, body-size calming, slower waves), animated water (frame-state buffer fix, ocean PCA from default.xex, XEX2 unpacker), ripple scale | [09](09-water.md) | `gameplay/water` branch (`af1b350` onwards) | — (copies #28's `install.py` `spawn()` split) | Prepared 2026-10-01, not opened: applies to upstream `60efdef`; failing tests match upstream exactly | +| I | Test fix | `sky_shader_validates`: add the `VERTEX_POSITIONS` shader def Bevy sets for real meshes | [10](10-sky-shader-test.md) | on `gameplay/water` | — | Not opened; tiny, can go upstream alone | | G | Performance (setup) | Skip decoding duplicate stream copies; run the customiser beside the maps; parallel clothing library and pro roster, plus a native RefPack DLL in `Build.ps1` | [08](08-setup-performance.md) | `8327e94` (+ overlap hunks of `install.py` in `18f41a3`) | — | Draft upstream [#29](https://github.com/SK8-ENGINE/skate-3-rust-engine/pull/29) | Opened 2026-10-01 as drafts from per-PR branches cut from upstream `60efdef` @@ -47,7 +49,13 @@ Notes for whoever opens them: skate-game `production_factory_routes_three_handlers_and_all_five_conditions_to_grind_owner`, `embedded_static_rwcm_hits_distinct_actor_query_ids`, `pipelines_accept_valid_group_outputs_when_fingerprint_changes`, - `sky_shader_validates`; and three skate-data examples that do not compile + `sky_shader_validates` (fixed on `gameplay/water`, see + [10](10-sky-shader-test.md)); and three skate-data examples that do not compile (`apt_data`, `hud_data`, `scoring_flow_data`). +- PR H includes setup changes (environment group: `ocean_pca.py`, `particles.py`) + that refresh that group once, and a dev-only mod (`mods/water-test-teleport/`) + that must be dropped from the upstream branch. The frame-state buffer fix + (`retail_render.rs` `write_frame_state`) also unfreezes family-14 scrolling and + the shadow floor, so it could go upstream on its own first. - Fork-only commits not meant for upstream: `167605a` (ignore the local `.claude/` workspace) and `c2be42b` (docs index; the docs travel with each PR). diff --git a/docs/hails-additions/README.md b/docs/hails-additions/README.md index df117fea..e9db089b 100644 --- a/docs/hails-additions/README.md +++ b/docs/hails-additions/README.md @@ -14,7 +14,8 @@ Each document stands alone. File paths are relative to the repository root. | 3 | [SDL3 gamepad input](03-sdl3-gamepad-input.md) | Game (Rust), build scripts, vendored `sdl3-sys` | Done, verified in play with an Xbox Elite Series 2 | | 4 | [Authored map spawns](04-map-spawns.md) | Asset pipeline (Python) | Done, all 10 maps confirmed in play | | 5 | [Invisible collision volumes](05-collision-volumes.md) | Game data loader (Rust) | Done, all four affected maps confirmed in play | -| 9 | [Water](09-water.md) | Game physics (Rust), diagnostics | Water flag wired up (DownTown, University); awaiting in-play test | +| 9 | [Water](09-water.md) | Game physics, camera and rendering (Rust), setup, diagnostics | Matched to retail footage (RPCS3): shallow water solid, deep water floats, board floats, water camera + vignette, entry splash, canal water look matched, small bodies calmer | +| 10 | [Sky shader validation test](10-sky-shader-test.md) | Game tests (Rust) | Done; all six shader validation tests pass | ## Environment used for verification diff --git a/mods/water-test-teleport/main.lua b/mods/water-test-teleport/main.lua index 943db4d6..59db9130 100644 --- a/mods/water-test-teleport/main.lua +++ b/mods/water-test-teleport/main.lua @@ -2,7 +2,7 @@ -- collision (centroids from `WATER_POINTS=1 cargo run -p skate-data --example -- water_surfaces`). Shift+F2/F3/F4 stand on dry ground facing that water -- (`WATER_VIEW=x,y,z[,reach,rise]`). The University spots only make sense on --- University, the DownTown ones only on DownTown. F6 is the game's camera key. +-- University, the DownTown ones (F4, F7) only on DownTown. F6 is the game's camera key. local SPOTS = { F2 = { water = { label = "University fountain basin (water 67.94 m)", position = { 340.3, 69.94, -294.3 }, heading = 0 }, @@ -16,11 +16,35 @@ local SPOTS = { water = { label = "DownTown fountain (water 18.72 m)", position = { 44.3, 20.72, 241.5 }, heading = 0 }, view = { label = "DownTown fountain, view", position = { 44.2, 19.49, 247.9 }, heading = 3.120 }, }, + F7 = { + water = { label = "DownTown Aletown canal (water 8.93 m, 1.1 m deep)", position = { -182.3, 10.93, 465.9 }, heading = 0 }, + view = { label = "DownTown Aletown spawn, facing the canal", position = { -183.4, 12.76, 433.2 }, heading = 0.034 }, + }, } local held = {} +local show_position = false +-- Seconds the "Teleported" line stays on screen. +local MESSAGE_SECONDS = 5 +local message_timer = 0 return { - on_update = function() + on_update = function(event) + local dt = (event and tonumber(event.dt)) or 0 + if message_timer > 0 then + message_timer = message_timer - dt + if message_timer <= 0 then sdk.ui.text("water-test", "") end + end + -- F5: toggle an on-screen position readout (to report water locations). + local f5 = sdk.input.down("F5") == true + if f5 and not held.F5 then + show_position = not show_position + if not show_position then sdk.ui.text("water-position", "") end + end + held.F5 = f5 + if show_position then + local p = sdk.player.read().position + sdk.ui.text("water-position", string.format("Position: %.1f, %.2f, %.1f", p[1], p[2], p[3])) + end local shift = sdk.input.down("ShiftLeft") == true or sdk.input.down("ShiftRight") == true for key, spot in pairs(SPOTS) do local down = sdk.input.down(key) == true @@ -28,6 +52,7 @@ return { local target = shift and spot.view or spot.water sdk.player.teleport({ position = target.position, heading = target.heading, velocity = { 0, 0, 0 } }) sdk.ui.text("water-test", "Teleported: " .. target.label) + message_timer = MESSAGE_SECONDS end held[key] = down end diff --git a/mods/water-test-teleport/mod.json b/mods/water-test-teleport/mod.json index e874e320..606d4ae7 100644 --- a/mods/water-test-teleport/mod.json +++ b/mods/water-test-teleport/mod.json @@ -4,6 +4,6 @@ "name": "Water Test Teleport (dev)", "version": "0.1.0", "author": "Hails", - "description": "Dev only, for branch gameplay/water: F2 University fountain basin, F3 University reservoir, F4 DownTown fountain drop the skater into the water; Shift+F2/F3/F4 stand on dry ground facing it. F6 is the camera key.", + "description": "Dev only, for branch gameplay/water: F2 University fountain basin, F3 University reservoir, F4 DownTown fountain, F7 DownTown Aletown canal drop the skater into the water; Shift+F2/F3/F4 stand on dry ground facing it. F5 toggles a position readout. F6 is the camera key.", "entry": "main.lua" }