# Track note: m3 (board, launch, dive, swim, dock) Written by the Coder of milestone M3, 2026-10-05. Workplace: the one project `D:\HADAL - The Descent\TheDescentUE` (the work began in the copy `.wt\m3`, which was merged in and is no longer written to). **Everything under "Results" was run on this PC in this project; what was not run is listed at the end.** ## Results | Chain step | Result | |---|---| | `Tools\build.ps1` | PASS, 0 warnings (the user's editor holds the module, so the build links `UnrealEditor-TheDescent-.dll`; that is normal) | | `Tools\import_assets.ps1` | PASS (new module `assets_36_tender_inside`: 14 two-sided material twins) | | `Tools\run_tests.ps1` | 64 of 65 PASS. The one failure is `Descent.Arm.Reach` (the arm track's unfinished work, not M3; not touched) | | Scenarios | every registered scenario PASS except the two that fail on purpose (`selftest_timeout`, `selftest_expect`); see the list at the end | | Normal start, no scenario (`-game -RenderOffscreen`, 28 s, stopped by its own PID) | boot "10 step(s) ran, all ok", `Player: normal start, mode Title, title card open`, no error or warning of ours | ## Acceptance table (DESIGN 10, M3) | Check | Result | Evidence (numbers are from the logs of the runs) | |---|---|---| | `Descent.Carousel.Clearance` | PASS | 360 degrees in 1 degree steps, three Tenders stowed: 4.085 m between Tender boxes (need >= 2), nearest bay obstacle 0.343 m (port wall; the design says 8.5 m reach against 8.84 m). Hoist path STOWED > BOARD > DOCK_BELOW: 0.655 m (ceiling, at the start); in the shaft 0.785 m to the shaft walls. Obstacles: walls, ceiling, floor, D2 gallery and rail, stairs and rail, hub, pool rails, shaft walls, and the pipe riser and unload boom of section 6.3 as boxes at their designed places (they are not built before M5) | | `Descent.Tender.Stats` | PASS | the table of 4.2 for the three specs and with upgrades; crush 0 at 2,280 m and 1.4 HP/s at 2,320 m; honest voids; docked recharge | | `Descent.Shear.Field` | PASS | 0 at Z <= 25, 10.0 m/s at Z 65 straight down over the centreline, 25 m/s at Z 125, a downward part and a part toward the centreline off it, warning from Z 10, no jump above 0.3 m/s per metre | | `m3_launch_dock` (V5) | PASS | walked to the pool rim with real movement, prompt `Board BT-01 TENDER`, E. Mode sequence logged: `Foot>Board>Docked>Launch>Sub>Dock>Docked`. Released at carrier-local (-29.00, 6.00, -16.03) with -0.94 m/s. Docked back at (-29.000, 6.000, -6.300). `carrier.carousel.min_clearance_m`: smallest seen 0.655 m (need >= 0.3). Dock refused 30 m from the pool. HUD depth = dash depth (`1,837 m`). Shots `bay_carousel`, `launch_mid`, `cockpit`, `tender_close`, `dock_rise` (+ `canopy_open`, `cockpit_docked`, `tender_sea`) | | `m3_carousel` | PASS | 120.000, 240.000, 360.000 degrees; BT-03, BT-02, BT-01 in turn at (-29.00, 6.00, -6.30), yaw 0.00; smallest clearance through the three turns 0.343 m; refused with a Tender out ("a Tender is out"). Shot `rotation_mid` | | `m3_rotor` (V10) | PASS | idle 0.00 rpm; thrust 1.000: 305.00 rpm, the rotors turned 2,745 degrees in 1.5 s, port and starboard opposite; boost 518.5; back to 0 | | `m3_no_walls` (**U7**) | PASS | see the next section | | `m3_swim` (V9) | PASS | walked through the gap in the pool rail, fell in at feet (-28.20, 4.21, -8.64) > `Swim`, no rescue; O2 43.533 > 38.533 over 5.000 s: 1.0000 per second; out through the belly to z -28.5 (carrier-local); back up to the ladder (prompt `E Climb the ladder`, head at z -9.20, pool surface -8.30); `Ladder` > `Foot` at (-29.00, 2.60, -7.48), O2 45.0; with O2 0 health 99.40 > 72.40 in 3.000 s: 9.000 per second, HUD `DROWNING`; death, respawn after 2.60 s in the galley at (24.00, -5.00, -2.98), health 60, O2 45, debt 0 > 900. Shots `swim_under_carrier`, `ladder` | | `m3_crush` (V8) | PASS | 5 s at 2,280.0 m: hull 100.000 > 100.000, warning `APPROACHING CRUSH DEPTH`; 5 s at 2,320.0 m: 7.000 HP lost; dash `!! CRUSH DEPTH 2,300 m`. Extra: 2 s in the honest void 28.00 HP; hull 0 > death "implode", respawn in the galley, debt 4,800, the Tender back in its cradle with hull 100 and power 60 | Extra scenarios (not in the table): `m3_cockpit` (the pilot's views in the bay and at sea, the docked look-around, the roof hatch, the automatic dock when rising into the pool), `m3_ui` (the UI wiring, below). ## U7, no invisible walls: `m3_no_walls` in numbers * (a) East from `canyon_mid` at boost: 84.7 m in 6.1 s, top speed 15.30 m/s, until `contacts` 1. Density at the nose -0.09, 3 m ahead +2.47 (rock); a ray from the eye meets rock 2.00 m ahead. Shot `rock_east`. * (b) Pocket carved at (5000, 0, -300), far outside every v2 bound: the Tender moved 12.95 m along each of +X, -X, +Y, -Y, +Z, -Z with 0 contacts; dash and HUD read `2,080 m`, `2,067 m` (+Z), `2,093 m` (-Z), equal to the expected text each time. * (c) At Z -1,900: dash `3,680 m`, the mounted dash component 3680.0, HUD `3,680 m`, band `BELOW SURVEY`. * (d) Up from the canyon at boost for 25 s (750 steps): highest Z 116.28 (limit 135), where the Shear is 22.82 m/s against 15.3 m/s of thrust; largest change of speed in one step 0.839 m/s (limit 2); the HUD warning was on every time above Z 13 (the HUD model is written at 10 Hz), text `SHEAR CURRENT — ASCENT NOT AUTHORISED`; `ui.hud.shear_warning` true in the dump. Shot `shear`. * (e) The swimmer in the same pocket: 13.01 m along +-X and +-Y, 12.82 m up, 13.19 m down, HUD depth following; then down until the pocket's own rock: stopped 0.45 m from it (its 0.45 m sphere), 2 rock contacts. * There is no position clamp in the flight step, the swim step or the carousel (`pos += (vel + Shear) dt`, then push-outs). * One place needed a decision, see deviation 6: the pool mouth. ## The UI wiring (`m3_ui`, keys injected through `APlayerController::InputKey`) Title card open in mode Title > DIVE > closed, mode Foot. Tab opens the PDA, M the PDA on the map tab, Esc / P / H the pause window (the sim clock stands still, the Tender does not move with W held; closing resumes). W held under an open window gives live intent 0. Tab does nothing over the title card; Space under the PDA does not launch. Then with the real keys: E boards, Space launches, F switches the headlamp, W + Shift fly (14.98 m/s, thrust 1.70), E leaves to swim (2.51 m from the Tender, which stays, state Abandoned), E enters again. HUD model fields written by the controller: `Mode`, `bUiWindowOpen`, `bCrosshair`, `Prompt`, `DepthM/Text`, `Band`, `RatingM/Text`, `CompassHeadingDeg`, `TemperatureC/Label/Text`, `Vitals` (health, nutrition, O2, `bShowO2`), `Vehicle`, `Warnings` (flags and text). Dashboards: `UDescentDashComponent` Main and Cargo mounted in every fleet Tender, fed from `UTenderFlightComponent::MakeDashModel()` in the Dashboards late step (8 Hz). ## What was built (files) New: `Core/Tuning/DescentTune_Tender.h`; `Vehicles/TenderFlight.*` (specs, stats, crush, spheres, `UTenderFlightComponent`), `Vehicles/CarrierCarousel.*` (pure geometry + `UCarrierCarouselComponent`: hub, arms, hoist, launch, dock, boot step `SpawnTenders`), `Vehicles/CarrierHullShape.*`; `Player/DescentVitals.*`, `Player/DescentPlayerModes.cpp` (the mode state machine); `Debug/DescentCommands_Tender.cpp`, `Debug/Scenarios_M3.cpp`, `Debug/Scenarios_M3_UI.cpp`; `Tests/TenderTests.cpp`; `Tools/ue/assets_36_tender_inside.py` (and its output `/Game/Vehicles/Kazah/Materials/MI_*_2S`). Changed: `Vehicles/KazahTender.*` (fleet subsystem, flight, camera, dashboards, lids, paint, collision boxes, `tenders` section), `Vehicles/DescentCarrier.*` (carousel component, `carrier.carousel`), `Vehicles/CarrierBuild.cpp` (the M2 barrier in the pool-rail gap is gone), `Player/DescentCharacter.*`, `DescentCharacterMovement.*` (swim), `DescentPlayerController.*`, `Systems/DescentFixture.*` (carousel console and ladder work), `Core/Tuning/DescentTune_Player.h`, `Debug/DescentCommands_Player.cpp`, `DescentCommands_Carrier.cpp`. Commands added: `Descent.Tender.Board [i]`, `.Launch`, `.Dock`, `.Exit`, `.Enter [i]`, `.Teleport x y z | `, `.LookAt x y z`, `.Set v [i]`; `Descent.Carrier.Carousel`; `Descent.Player.Set v`, `.Swim x y z`, `.PoolDrop`, `.Climb`; `Descent.Player.Use` now presses E in every mode. State: `tenders.items[]` (fleet first: id, name, state, docked, pos_m, yaw/pitch, speed, hull, hull_max, power, rating_m, depth_m, contacts, lamp, hold, bio_fu, rotor_rpm, dash{...}), `carrier.carousel{angle_deg, busy, phase, launch_slot, hoist_z, min_clearance_m, min_clearance_seen_m, rotations, tender_out}`, `vitals{...}`, `econ.debt`, `player.mode_sequence`. ## Shared-file changes None. Nothing in `World\`, `Render\`, `UI\`, `Core\` (other than the two tuning headers that are this track's) or `TheDescent.Build.cs` was edited. Nothing with Arm in its name was touched. ## Deviations from the design, and why 1. **Cockpit field of view 104.5 degrees, not 72.** The design's 72 is v2's three.js camera, whose FOV is vertical; Unreal's is horizontal. With 72 horizontal the dash, 24 degrees below the horizon, was below the bottom of the picture. 104.5 horizontal is 72 vertical at 16:9. (The on-foot camera of M2 still uses 72 horizontal: the same question is open there; not changed.) 2. **Dash sizes 0.62 m and 0.26 m, not 1.10 m and 0.30 m.** The design's sizes are v2's cockpit. The Kazah's console is 0.62 m wide and its canopy about 1.2 m at that station: a 1.10 m panel went through the hull. Place and tilt relative to the eye are the design's; the cargo display sits 6 cm further outboard so the two do not overlap in view. 3. **Two-sided material twins for fleet Tenders.** The Kazah is a single-sided shell: from the pilot's eye every hull and frame face was culled and the cockpit showed no Tender at all. `assets_36` makes `MI__2S` (child instances, two-sided on) and `InitFleet` uses them on Body, Cockpit_Interior, Canopy_Lid, Canopy_Glass, Hatch_Lid. 4. **The canopy turns about the top of its rear hoop** (1.207, 0, 1.24), not about the exported pivot at the hoop's bottom: about the bottom the 1.27 m hoop swung a metre back through the hull roof and the hoist clamp (seen in a shot). 55 degrees and 0.5 s are kept. 5. **Carrier hull against the Tender's nine collision spheres**, not one 2.0 m sphere at the origin: with one sphere the nose went 1.7 m into the hull before anything stopped it. The hull is an analytic body of revolution (`HullRadius(x)`), since the blockout hull has no physics. 6. **The pool mouth.** For a free Tender the hull shape is closed across the mouth (otherwise a Tender entering at the wrong yaw clips the shaft walls, V5). So that nothing invisible holds a Tender under the open mouth, rising into it docks without E: inside the pool rectangle, above carrier-local z -13.4, still pushing up, the hoist takes over (`m3_cockpit`: began at z -13.14, 0 contacts). The swimmer passes through the mouth freely; in the shaft the head stays 0.35 m under the pool surface (design). 7. **The tool housing is mirrored to starboard in code** (negative Y scale), per the user's decision in BRIEF 3; the tool itself is M4. 8. **The HUD vehicle panel** is filled in every Tender mode but shown only docked and looking more than 35 degrees aside: in the cockpit the dash shows the same model (the HUD model says the panel is for when no dashboard is on screen). 9. **Swimming starts** when the feet are 0.2 m under the pool surface, and a body teleported into water swims at once; the pool-surface rule is eased (2.5 m/s) so a drop from the deck sinks in instead of snapping. 10. **Debt** has no owner before M5: `FDescentVitalsState::Debt`, state field `econ.debt`. **Nutrition** stays at 42 % (M8). 11. `Descent.World.Goto` (the world track's file) still moves only the pawn; `Descent.Tender.Teleport ` moves the Tender. `Descent.Player.Give` was not made: there is no inventory before M4. 12. Opening a window lets go of every held key (the engine's UI-only input mode does that): after closing, W must be pressed again. 13. An abandoned Tender drains only its lamp; the hunger multiplier is 1 until M8. ## Guessed, not seen, not run (stated plainly) * **Not driven by a person.** Keys were injected into the controller's input, which proves the bindings; the mouse (Mouse2D) was not injected: steering and looking were driven through the intent (`Descent.Player.Intent`). The title card's DIVE was called through its action, not clicked. The windows' own closing keys (Tab / Esc inside the window) are the UI track's code and were not pressed here. * **Guessed:** the cockpit glow level (0.15, not judged in a picture), the top beacon's place (-0.9, 0, 1.58; not found in any shot), the swimmer's honest-void damage (14 per second, the Tender's number), the blockout of hub, arms, clamp and mast. * **Not exercised by a scenario:** the swimmer in the Shear (the code adds it; only the Tender was flown into it), the collision of a Tender with the carrier hull at speed (covered by the `HullShape` test only), the impact-damage toast, the 20 % power radio line, a Tender imploding while nobody sits in it. * **Observation about the Shear function (the world track's):** off the centreline its direction has a 0.8 part toward the centreline, so at the top a Tender is carried to the centreline and then steps across it every sim step (the speed stays steady: 0.84 m/s largest change). * **Not built in M3 by design:** sounds (crush alarm, breath; M9), the drill and suction (M4), unloading (M5), the `tool` and `hardpoints` fields of `tenders`. * A non-unity build (`-DisableUnity`) was not run. ## Scenario list of the final chain (last full run, after the last code change, 2026-10-05) PASS (34): `core_sim`, `kazah_closeups`, `kazah_showcase`, `look_bloom`, `look_calibrate`, `look_deep_dark`, `look_deep_lamp`, `look_dry_mask`, `look_near_wall`, `look_rooms`, `look_sweep`, `look_throat`, `m12_boot`, `m1_carve`, `m1_commands`, `m1_terrain_tour`, `m1_world_slices`, `m2_dry`, `m2_exterior`, `m2_moving_base`, `m2_walk`, `m3_carousel`, `m3_cockpit`, `m3_crush`, `m3_launch_dock`, `m3_no_walls`, `m3_rotor`, `m3_swim`, `m3_ui`, `smoke`, `ui_dash`, `ui_gallery`, `ui_hud_layout`, `ui_windows`. FAIL on purpose (2): `selftest_expect`, `selftest_timeout`. One regression appeared on the way and was fixed: `smoke` teleports the pawn into open water and checks the position to 1 cm; the body fell for one frame before it began to swim (2.8 cm). A body teleported into water now swims at once (`ADescentCharacter::TeleportTo`). Pictures looked at by the Coder (`Saved\Shots\`): `m3_launch_dock_bay_carousel` (three Tenders in three paints, hub, arms, hoist mast, none touching), `_launch_mid` (the Tender leaving the pool mouth, seen from below), `_cockpit` and `m3_no_walls_shear` (readable dash and cargo display, the canopy's front rim and the nose; the Kazah has a frameless bubble, so no strut crosses the view), `m3_cockpit_tender_close_bay` (the same view as `m3_launch_dock_tender_close`: the glass with seat, headrest and tub through it), `_canopy_open`, `_dock_rise`, `m3_carousel_rotation_mid`, `m3_swim_swim_under_carrier`, `m3_swim_ladder` (dark: the rungs from 0.8 m), `m3_cockpit_hatch_open` (dark), `m3_cockpit_bay_ahead`, `_bay_left`, `_nose_from_above`, `_sea_ahead`, `m3_rotor_rotors`, `m3_no_walls_rock_east` (the headlamp's spot on the wall that stopped the Tender; the scenario backs off 1.3 s first, because pressed against the rock the lamp lights only its own 9 cm, and asserts centre luma > 0.05: measured 0.118. `m3_no_walls` was run again after that change: PASS, same numbers). Not looked at: `pocket_cockpit`, `deep_cockpit`, `cockpit_crush`, `respawn_galley`, `pool_gap`, `after_ladder`, `tender_sea`, `board_prompt_view`, `bay_carousel_aft`.