dayz-vehicles
Skill20 starsChanged 42 days agoMITView on GitHub
Author, import, debug and ship DayZ ground/water vehicles: CarScript cars, trucks, quads/ATVs and motorbikes plus Boat-based watercraft. Covers the config.cpp/model.cfg contract, drivetrain/engine/suspension, Axles/Wheels, Crew/get-in/actions, AnimationSources, structural parity, LODs, wheel/crew proxies, View/Fire/Geometry, placement, winding and dedicated-server packaging failures. Use for car/truck/quad/ATV/moto/boat, drivable vehicle, CarScript, civiliansedan parity, importing Blender/OBJ/ripped racing-game vehicles, "get-in broken", wheels missing/not steering/spinning backwards, white or untextured vehicle, vehicle build/pack/binarize or proxy pose. Always invoke before authoring/debugging a vehicle entity, config.cpp or model.cfg. Use dayz-aviation for flight and dayz-model-pipeline for generic assembly.
In this repository47 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- .github/copilot-instructions.mdCopilot instructions
- 3d-generation-harnessSkill
- ai-3d-to-dayzSkill
- ardy-motion-generationSkill
- blender-animationSkill
- blender-assemblySkill
- blender-visual-reviewSkill
- dayz-3d-viewerSkill
- dayz-ai-patternsSkill
- dayz-animation-pipelineSkill
- dayz-aviationSkill
- dayz-basebuildingSkill
- dayz-charactersSkill
- dayz-clothingSkill
- dayz-doorsSkill
- dayz-environment-hazardsSkill
- dayz-feature-specSkill
- dayz-mcp-verifySkill
- dayz-model-pipelineSkill
- dayz-mod-workflowSkill
- dayz-motorbikesSkill
- dayz-p3d-auditSkill
- dayz-p3d-inspectorSkill
- dayz-particlesSkill
- dayz-pbo-buildSkill
- dayz-pbo-reverse-engineeringSkill
- dayz-persistenceSkill
- dayz-physics-engineSkill
- dayz-preflightSkill
- dayz-proxy-alignSkill
- dayz-realistic-animation-directorSkill
- dayz-sound-systemSkill
- dayz-test-ingameSkill
- dayz-texture-pipelineSkill
- dayz-ui-developmentSkill
- dayz-undergroundSkill
- dayz-weaponsSkill
- enforce-script-referenceSkill
- mixamo-retargetSkill
- ovh-cloudSkill
- rigorous-data-auditSkill
- rip-vehicle-importSkill
- systematic-debuggingSkill
- task-observerSkill
- uv-clean-atlasSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
13 rules. About 0 are common, 11 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈11
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 22
If asset is a new Grub source-game car, proxy-split and with moving parts, use family B.
- Distinctive: line 25
If a frozen plan/runbook already exists for asset, follow that contract in flight.
- Distinctive: line 26
If no row applies or family adapter is missing: STOP.
- Very specific: line 145
NOT enough. (2) Verify the bake OFFLINE before deploying: extract the PBO and string-scan the binarized .p3d for the animation SOURCE …
- Distinctive: line 344
If a product task is blocked TWICE by the harness, stop the product and decide explicitly: scope the harness as its own project, or run …
- Distinctive: line 376
Import fidelity, digest equality and structural diffs do NOT catch this — they compare the piece to itself, not to where it belongs.
- Distinctive: line 411
Render the same material on a known-good control: If a vanilla-derived control looks right with the same route, the pipeline is exonerated …
- Distinctive: line 419
Cost rule: if two rounds of pipeline auditing come back clean, "the texture is applied wrong" is REFUTED — change branch instead of running …
- Distinctive: line 431
Before running the ladder on a ripped vehicle (GTA, ripped racing-game, any game rip), census which source material each face carries and …
- Distinctive: line 457
When the user reports that a vehicle draws somewhere the entity is not — body on the ground while the vehicle climbs, a part left behind, a …
- Very specific: line 596
delete loose verts after every decimate (bmesh.ops.delete(bm, geom=[v for v in bm.verts if not v.link_faces], context="VERTS")).
- Distinctive: line 667
Do not "fix" a small triangle.
- Distinctive: line 823
BEFORE deploy — Blender cannot judge DayZ winding (right- vs left-handed cull).
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
DayZ Ground Vehicle Modding
Family selector — day-0 critical path (CAMBIO-2)
- Identify family by asset origin and architecture; not by symptom.
- If asset is a new Grub source-game car, proxy-split and with moving parts, use family B.
- Family B → open
../rip-vehicle-import/SKILL.mdand follow only its adapter, golden, and allowlist. - That adapter opens asset
asset-contract.jsonas third and final day-0 file; schema, Blender export, and primitive are machine inputs, not additional documents maintained by agent. - If a frozen plan/runbook already exists for asset, follow that contract in flight; do not migrate it here.
- If no row applies or family adapter is missing: STOP. Do not improvise from this atlas.
| Input signal | Adapter | Day-0 files |
| source-game Grub, new car, proxy-split + doors/moving parts | Family B | This router → ../rip-vehicle-import/SKILL.md → <asset>\asset-contract.json |
| Asset already in flight | Frozen project runbook | Whichever cited in its current plan |
| Other family or insufficient data | None | STOP |
Rest of this body is diagnostic atlas and does not form part of day-0 critical path. Only consulted when adapter or symptom links a specific section.
Preflight before re-importing / re-assembling any piece (SP-193): first measure FACTORY FACE-LEVEL completeness of piece against already packed model. A "rotten" mesh is usually complete and defect lies elsewhere; see SP-193 at end of this atlas.
Drivable wheeled vehicles in DayZ extend CarScript — there is no generic "vehicle" base, and
you do not build one from EntityAI or Inventory_Base. CarScript gives wheels, seats, sound
integration and the damage system for free; you configure the rest. This skill is the
vehicle-specific layer on top of dayz-model-pipeline (generic geometry / LODs / textures) and is
the ground counterpart to dayz-aviation (anything that flies belongs there).
Scope: cars, trucks, quads/ATVs, motorbikes — anything wheeled and drivable, modeled from scratch or imported (Blender / OBJ) from another game.
REDIRECT CAMBIO-1: header family selector and chosen adapter are the sole day-0 path.
Vehicle type matrix (invariant): Car and Boat are SIBLINGS, both directly under Transport
(car.c:98 / boat.c:31) — NOT parent/child. Transport owns crew/get-in/flip/fuel; Car owns
wheels/brakes/CarFluid; Boat owns propeller/buoyancy/BoatFluid(fuel-only). A truck is a plain
CarScript config with 3 axles + double wheels, NOT a new class. Boats, truck double-wheels, ATV and
the motorbike gap → references/vehicle-types-boat-truck.md (historical: since DayZ 1.30 Exp motorbikes are a vanilla family, see the dayz-motorbikes skill).
DayZ 1.30 Exp (build 1.30.164014) changes the matrix (added 2026-09-16, source_verified on the extraction at E:\DayZ-Exp-Extract\1.30.164014\exp):
- [EXACT] A third sibling exists:
class Motorbike: Transportwithsimulation = "motorbike"(dta\bin\config.cpp:1054-1059; engine proto3_Game\Vehicles\Motorbike.c:30), scripted byMotorbikeScript(4_World\Entities\Vehicles\MotorbikeScript.c:53), which does NOT inheritCarScript. Two-wheel work goes to thedayz-motorbikesskill; this atlas stays for cars, trucks, quads and boats, plus the ViewPilot and the first-person view of any family (SP-248, SP-249 andreferences/proxies-and-get-in.md). - [EXACT]
CarScriptwas refactored around components owned byTransport:VehicleLightsComponent(CarScript.c:187, 229-233),VehicleHornComponentandVehicleVFXComponent(CarScript.c:189, 343-368). The old overrides are marked[Obsolete]:CreateRearLight/CreateFrontLight(CarScript.c:2937-2945),IsVitalCarBattery/IsVitalTruckBattery(:2811-2817, replaced byTransport.NeedElectricitySourceDevice()atTransport.c:821),CarPartsHealthCheck(:2976-2979, replaced byTransport.PartsHealthCheck()atTransport.c:437/ override atCarScript.c:2121). The horn actions areActionVehicleHornShort/Long(CarScript.c:2319-2320). Contact data isVehicleContactData.m_Impulse(Transport.c:1144-1148, used atCarScript.c:1291-1292). Wheel classes gainedtyreRoughness,tyreLongitudinalFriction,tyreLateralFriction(DZ\vehicles\wheeled\config.cpp:749-751; absent in 1.29). - [DESIGN] A 1.29 car mod that overrides any of the obsolete members compiles against the attribute but its override is no longer called; re-verify each override against the 1.30
CarScript.cbefore shipping. Migration checklist and the "1.29 statements that are now false" table:dayz-motorbikes/references/vehicle-1.30-refactor.md. Sections of this atlas that predate 1.30 (lights viaCarLightBase,CarPartsHealthCheck,data[0].impulse) describe 1.29 and are not yet rewritten. - [EXACT]
Transport.GetWeightCoef()default1.0(Transport.c:475-478); vanillaTruck_01_Base2.0(Truck_01_Base.c:120-123),Offroad_021.5(Offroad_02.c:133-136),Motorbike_010.5(Motorbike_01.c:87-90),Motorbike_020.75(Motorbike_02.c:77-80). Digest M's claim thatMotorbikeScript.c:646returns0.25is false: that line usesGetWeightCoef(), it does not define it. - [EXACT]
CarScript.HasGangedTailAndBrakeLights()defaultfalse(CarScript.c:468-471);trueon Offroad_02 (Offroad_02.c:68-71) so reverse/position swaps do not kill the brake selection (CarScript.c:2061-2094). - [EXACT] Vehicle light toggle is
UAToggleVehicleLights/ToggleVehicleLightsActionInput(ActionInput.c:849-855,ActionSwitchLights.c:16-18);UAToggleHeadlightis headgear (ActionInput.c:776-782).ForceUpdateLightsStart/Endare[Obsolete("1.30: no replacement")](CarScript.c:2805-2809). - [EXACT] Get-in calls
CanGetIn()thenIsAreaAtDoorFreeDiagand does callCanReachSeatFromDoors(..., 1.0)from script (ActionGetInTransport.c:36,54,64). - [EXACT][CLAIM-VEH-130-PORT-BLOCKERS] Two 1.30 changes break a 1.29 car outright, beyond its looks.
CarScript.ToggleHeadlights()was deleted without an[Obsolete]stub (1.29CarScript.c:2088), so anoverrideof it stops the4_Worldcompile. A car that does not override the three electricity-source methods reportsNO_BATTERYwith a battery attached (1.30.164200transport.c:835-838,carscript.c:1768-1774): no engine start and no light switch. Two more showed up in game on 1.30.164200: a backslash script module path loaded none of the LFQuad's scripts, silently, and every wheel class without a<class>_Ruinedtwin raised a script error when its first wheel was created (1.30.164200inventoryitemtype.c:17-46). Detail and the override check:references/dayz-1-30-vehicles.md(table and the path claim below it, section 2, section 11, checklist items 0 and 11-13). Seen porting the LFQuad to 1.30.164200 (2026-10-26). - Lights, horn, fluids, boats, wreck cisterns-as-Well,
speedGeoms, and the motorbike 3PP camera pointer:references/dayz-1-30-vehicles.md. Motorbike camera ID 32 lives indayz-motorbikes/references/rider-animation.md.
Per-tick event asymmetry (invariant, added 2026-07-29, LFHeli OH-1): CarScript has NO
EOnSimulate — its ctor registers only POSTSIMULATE/POSTFRAME (carscript.c:325-326) and ALL
its per-tick work (engine, fluids, part health, fuel/engine auto-stop, exhaust and wheel FX, much of
it gated IsServerOrOwner() — vanilla runs it on the owner client, but in mods replica-dependent checks (CheckVitalItem) must stay server-only (LL-305)) lives in EOnPostSimulate
(carscript.c:948). BoatScript overrides BOTH (boatscript.c:347 and :385). Consequences on a
car: hook EOnPostSimulate for per-tick logic; super.EOnSimulate is the empty stub at
enentity.c:201-203, so calling or skipping it does nothing either way — treat any comment claiming
it "runs engine/fluids twice" as false. Custom solvers that add SetEventMask(EntityEvent.SIMULATE)
and pump it by hand (aviation) → dayz-aviation preflight invariants.
(hasta 1.29: those carscript.c line numbers.) (desde 1.30 Exp: SetEventMask is CarScript.c:207-208; EOnPostSimulate is CarScript.c:807. Full line-drift table: references/dayz-1-30-vehicles.md.)
Cockpit screen → live engine map (RTTextureWidget bridge) (invariant, added 2026-08-19, SUB_BRZ RT spike):
Any hiddenSelections screen can display the ENGINE's map renderer (what LBmaster's GPS uses) instead
of baked tiles: create the RT via CreateWidget(RTTextureWidgetTypeID, …) (children render INTO its
texture, enwidgets.c:28 — they never paint to the viewport, so no HUD witness), hang a MapWidget
under it with CreateWidgets(layout, rt), then hook the entity in this exact order:
SetObjectMaterial(idx, "Mod\data\x.rvmat") FIRST (no leading backslash), SetObjectTexture(idx,
"$rendertarget") second, proto native void SetGUIWidget(IEntity, int idx, RTTextureWidget)
(enwidgets.c:637) LAST. A later SetObjectMaterial rebinds the base texture slot and silently undoes
the hook — a uniform light-grey screen means $rendertarget resolved to the white fallback texture.
Zero call-sites exist on disk for SetGUIWidget/RTTextureWidget; verified in-game on 1.29 diag.
MapWidget GetScale() defaults to 0.85; at 0.05–0.6 it draws line-work only (contours + roads, no
urban/forest fill) — pick the zoom with the user's eye gate. If the hooked vehicle leaves the client
streaming bubble its GetPosition() reads <0,0,0> and the map recenters to the map corner:
production code must re-resolve the vehicle every tick, never cache the entity reference. MCP test
flow that actually seats and starts: vehicle_get_in_client (ownership get-in; vehicle_enter
answers seated:1 without sticking) → engine_set. Recipe, captures and gotchas:
LFVehicleUI_dev\HANDOFF.md snapshot 2026-08-19.
Closed 2026-08-19b — RT unusable for a live GPS: the RT freezes the FIRST composed frame
(later SetScale/SetMapPos/vehicle movement never recompose it), and a MapWidget child only ever
contributes the vector layers (roads + contours) — the raster fill (land, forest, buildings) never
reaches the RT, even with a 24 s warm-up between widget creation and the entity hook. The bridge
recipe above remains valid for STATIC screen content only. Live vehicle GPS goes through baked
tiles on hiddenSelections instead (next invariant).
Vehicle screen tiles: terrain satmap, not navigation usermap — and apply server-side (invariant, added 2026-08-19, SUB_BRZ Plan B):
Two tile sources share the same s_XXX_YYY_lco.paa naming and the same 32×32 grid of 480 m tiles
on chernarusplus, but look completely different: dz\gear\navigation\data\usermap\ is the PAPER
map art (light background, red roads, contour lines — what the ItemMap shows), while
dz\worlds\chernarusplus\data\layers\ is the colored aerial satmap (the real "GPS look").
A GPS screen wants the layers path; world name must be parameterized per terrain. Second half of
the invariant: SetObjectTexture/SetObjectMaterial called ONLY client-side is silently reverted
to the config hiddenSelectionsTextures[] whenever the server pushes a visual-state resync —
observed reproducibly on GEAR CHANGE. Applying on the dedicated server does NOT fix it
(refuted in-game 2026-08-19 afternoon): scripted SetObject state does not replicate
server→client.* The working pattern is the skin-mod one: re-assert the full scripted visual
state client-side on EVERY OnVariablesSynchronized (no packed==applied early-out; gate only
the diagnostics). Also force-hide every bank window on the first apply
(SetAnimationPhaseNow(name, 1.0) loop): windows spawn with the config-default texture and
their initial hide phase is not guaranteed applied. The force-hide only exists at runtime if
the hide animations actually BAKED — see the skeletonBones invariant below. Satmap-in-bank
verified in-game 2026-08-19 (SUB_BRZ, 1.29 diag, human driver gate).
Tile overlap: neighboring satmap layer tiles share 32 px — sample only the tile core (added 2026-09-14, SUB_BRZ s93). [EXACT] In dz\worlds\chernarusplus\data\layers\, column 480 of a tile is column 0 of its east neighbor and row 480 is row 0 of its south neighbor: a per-tile screen map must sample only the core 16/512..496/512 of each tile. Without the crop the measured seam ratio was ~1.9-2.1 and roads jumped ~30 m; with it, 0.90-1.12 (measured offline on the 1.30.164014 Exp satmap tiles). [DESIGN] Name index of s_AAA_BBB_lco.paa: AAA is the tile column tx, BBB encodes the row as 31 - tz, and UV (0,0) is the northwest corner.
Sliding-plate tile window: fix the border row and size the bezel with wy + COVER + T (SP-402, added 2026-10-01, SUB_BRZ s95). [DESIGN] On a sliding-plate tile map with the marker centred, the hidden margin under the window must reach one full tile past the lowest window corner: with a free bottom row it is 1.5 tiles deep (97.5 mm under the centre on a 7x5 grid of 32 mm tiles, window +/-88.63 x +/-52.92 mm, bezel 99 mm). Pinning the bottom row with minValue/offset0 in its _y class (same slope), whenever its top edge stays COVER below the lowest projected window corner, cuts the requirement to 88.68 mm and the bezel to 90 mm. Under the window the requirement is always wy + COVER + T; with a centred marker nothing below one full tile reduces it. If a smaller bezel is wanted, the lever is tile size (zoom), not the window - say so with a preview.
Seated-view geometry lives in the View Pilot LOD (invariant, added 2026-08-19, SUB_BRZ v1.2):
A car with a View Pilot LOD (resolution 1100) renders THAT copy of the geometry for seated
occupants, not LOD0. Any geometry/UV/selection patch that must be visible from the driver seat
(screen UV remaps, marker quads, interior fixes) has to be applied to BOTH res 0 and res 1100 —
patching only LOD0 looks fine from outside and unchanged from the seat, which reads as "the fix did
nothing". Seat-only geometry (1PP gap closures) goes in res 1100 alone (SP-469). Also from the same
session: a tiny 8×8 DXT PAA is invalid (mip chain below 4 px → engine renders the grey fallback);
for flat-color config slots use a procedural #(argb,8,8,3)color(r,g,b,1,CO) instead of a file.
Animations only bake for selections that are skeletonBones (invariant, added 2026-08-19, SUB_BRZ grey-screen root cause):
AddonBuilder silently DROPS every model.cfg animation whose selection is not declared in the
model's CfgSkeletons skeletonBones[]: the build exits 0, the anim classes stay in the source
model.cfg, the AnimationSources stay in config.cpp, and SetAnimationPhaseNow on their sources
becomes a silent no-op. Measured differential (SUB_BRZ): gps_marker was a bone → its 4 anims
baked into the ODOL; the 28 nav_w* windows were in CfgModels sections[] but NOT bones → all
28 type=hide anims dropped, so the untextured bank could never be hidden and rendered as an
opaque grey panel 0.5–1.85 mm in front of the real screen — a grey that reads as "my texture
apply fails" while the apply is actually landing fine behind it. Two rules: (1) every animated
selection — hide anims included — must be listed in skeletonBones[]; being in sections[] is
NOT enough. (2) Verify the bake OFFLINE before deploying: extract the PBO and string-scan the
binarized .p3d for the animation SOURCE names, which are plaintext in the ODOL (e.g.
re.findall(rb'nav_w\d\d_h', odol) must yield all 28 distinct names). Zero hits = the animation
does not exist at runtime, whatever model.cfg says. Debugging corollary: the engine grey
fallback is per-SECTION (texture_index=-1); when a screen shows uniform grey, check for
coplanar untextured geometry IN FRONT of the selection being textured before blaming the
texture path or the apply call.
Verify new geometry by its ODOL SECTION, not by strings (invariant, added 2026-08-19, SUB_BRZ marker hunt): Geometry added to a MLOD by tooling can be present in every string table of the binarized p3d — selection name, material, texture — and still never render, because whether the engine draws a section is decided by fields the strings do not show. Measured on the SUB_BRZ dash marker across seven builds:
Section special | Meaning | Renders? |
0x0002C000 | hidden-selection + opaque pass | yes (all working textured sections) |
0x0002C100 | hidden-selection + alpha pass | alpha-dependent, unreliable for tiny quads |
0x0000C000 | no hidden-selection bits | never |
Two authoring rules follow. (1) A bone-skinned textured selection must ALSO be listed in
CfgModels.sections[], or binarize emits is_sectional=0 / sections=[] and there is no
draw call for the bone — being a skeletonBones entry is necessary but not sufficient.
(2) The face texture must be a real .paa FILE: a procedural #(argb,...) face texture
on new geometry produces 0x0000C000. Procedurals are fine in rvmat stages, not as the
face texture of a section you need drawn.
Gate before every deploy that adds geometry: extract the PBO and parse the ODOL section
table, asserting is_sectional==1, non-empty sections, and the 0x20000 bit. The LOD
address table is found by chaining (lod_end[i] == lod_start[i-1], max end ≈ file size);
a naive scan for the first plausible table hits a false positive. Reference implementation:
<vehicle-import>\work\navscreen_planb\gate_sections.py (built on an external ODOL→MLOD converter's
odol_reader). String-only scans pass while the geometry is undrawable — that gate cost
five wasted in-game cycles before it existed.
PAA files: always encode with ImageToPAA (added 2026-08-19):
A hand-rolled PAA whose mip chain runs below 4 px is invalid DXT. The failure mode depends
on the pass: an 8x8 DXT1 _co renders as the engine grey fallback, while a _ca with
2x2/1x1 mips renders as nothing at all on an alpha section. Encode every texture with
DayZ Tools\Bin\ImageToPAA\ImageToPAA.exe from a >=64 px source and let it build the mips.
Recycle existing faces instead of authoring new ones (invariant, added 2026-08-21, SUB_BRZ marker):
On an imported/ripped vehicle model, faces ADDED by tooling can be perfectly formed and still
never render, while every modification of pre-existing faces shows up reliably. Measured over
ten builds on SUB_BRZ: a static witness quad — no bone, no animation, bright file texture,
section 0x0002C000, winding/flags/normals identical to neighbouring faces that render, and
nothing occluding it — stayed invisible in-game; a bank-window quad that already existed was
shrunk, retextured and re-UV'd in the same file and rendered immediately. When a model needs a
new visible element, look for geometry already in it that can be repurposed (an unused overlay
quad, a hidden variant, a duplicate panel): you inherit its section, its bone and its proof of
rendering. Reserve authored geometry for models you build from scratch, and if you must add
faces to an imported model, prove one renders before building anything on top of it.
Translation offsets: make the memory axis exactly 1 m and the ambiguity disappears (added 2026-08-21):
For type="translation" in model.cfg, offset0/offset1 are documented inconsistently: they
behave as metres in some sources and as a fraction of the axis vector in others, and picking
wrong is silent — the selection simply moves somewhere off the model, which reads exactly like
"my geometry does not render". SUB_BRZ lost six build cycles to a marker translated with
offset1 = 1.0 against a 169 mm axis: under the metre reading each phase step threw it half a
metre out of the cabin. Author the memory axis 1.0 m long in the travel direction and set
offset1 to the travel in metres (0.169 for 169 mm of sweep). Both readings then produce the
same displacement, so the question never has to be answered. When a hidden/animated selection
is invisible, disconnect its animations and rebuild before blaming geometry or materials — a
detached control build is one cycle and it splits the two causes cleanly.
Reading a dark-art dash at night (added 2026-08-19):
Instrument-cluster art driven by dashboardMatOff (emissive 0) is invisible in an in-game
night, which reads exactly like "the texture never applied". Pin server time
(serverTime="YYYY/M/D/12/0" in serverDZ.cfg) before judging any interior art gate, and
give the OFF material a faint emissive so the furniture stays readable after dark.
Crew / engine state on a non-owner occupant (SP-288, origen LFHeli LF-008):
Car.EngineIsOn() on a client that is not the Pawn owner returns false even when the
authority has the engine running — no exception, no warning. Measured on the occupant of
seat 1 with a two-component probe in the same frame (natEng=false syncEng=true) and the
server log showing no stop. Any client subsystem gated on EngineIsOn() shuts itself off
(thermal camera died instantly: LF-008). Remedy: replicate the state
(RegisterNetSyncVariableBool written in OnEngineStart/Stop) and have every client
path read native OR replicated; an m_EngineOnAuthority (OwnerState) snapshot does NOT
serve passengers. When a client check decides to turn something off, print BOTH sources in
the same frame.
Scripted-pilot limitation — UAInput has no injection API (SP-276b, added 2026-08-17, LFHeli LF-001): the dayz-mcp bridge cannot pilot a vehicle whose mod neutralizes the CarController (SetSteering(0) every tick) and reads its own UA axes, because UAInput does not admit value injection. The working path is a DIAG script in the OWNER that substitutes the UA reading before WriteToMove: with the Pawn pattern of active forces the server consumes the axes from the Move (ConsumeMove → ReadFromMove), so the script needs no sync and no RPC. Plan v0: LFHeli_dev/plans/2026-08-17-piloto-scriptado-diag-v0.md. Status: pending — conditioned on the scripted pilot actually flying (LL-218: do not promote a mechanic without in-game adjudication); SP-276 patch (a) was adjudicated in-game 17-Aug.
Mirror-vs-rotation invariant (added 2026-08-02, LFHeli HH-60G): before you declare an
imported model "mirrored" and reach for the det=-1 import rule, check all THREE axes. A table
of memory points cannot tell a reflection from a rotation on its own — points have no handedness, so
only the PATTERN of sign flips does:
The general test is the SIGNED VOLUME, not counting axes. Take four LABELLED, non-coplanar
corresponding points (A,B,C,D) and compute det[B-A, C-A, D-A] on each side. An individual point has
no handedness, but a labelled tetrahedron does:
- signed volume keeps its sign -> proper transform (
det=+1) -> rotation. The model is FINE anddet=-1would ADD a mirror that is not there. - signed volume flips sign -> improper transform (
det=-1) -> the reflection is real; apply thedet=-1import rule plus pseudovector (-S.n) normals. - volume ~= 0 -> your four points are coplanar: pick others, the test says nothing.
Counting inverted axes is only the axis-aligned shortcut of that test, and it is easy to misread:
| axes flipped (axis-aligned case only) | what it is |
| ONE inverted, other two intact | reflection |
| TWO inverted, third intact | rotation of 180 deg about the third axis |
| all three inverted | point reflection = 180 deg rotation + mirror; decompose before acting |
Real case, and it nearly cost a full re-emission of 16 .p3d: one session measured only X, saw the
sign flipped and concluded "mirrored"; the user authorised de-mirroring. Measuring Z too showed
38/38 points with X and Z inverted and Y intact — a 180 deg yaw (det=+1), which is the pipeline's
normal convention, not a defect. Control that settled it: the sister airframe that already flies
in-game has the SAME convention (nose at -Z in the file). Applying det=-1 would have introduced a
real reflection where none existed and broken a winding policy already verified in-game. The
review that settled it did exactly the tetrahedron test: +6.076679 in file against +6.076514
in runtime, ratio +0.999973 - same sign, proper transform, argument closed without a game cycle.
Say what the test does NOT cover. A proper file->runtime transform does not rule out a mirror
baked in EARLIER (before the .p3d, in the source mesh or the exporter) nor a pre-mirrored UV
island, which is a texture defect and shows up as backwards lettering with the geometry perfectly
correct. Those are separate investigations with separate evidence; do not let a clean parity check
close them.
Corollary worth more than the rule — prefer RELATIVE measurements. A comparison made within one file, through one reader is robust to whatever axis convention your reader uses; an absolute file-vs-runtime comparison is not. "The 10 crew proxies carry a frame rotated 180 deg from the 15 piece proxies" is sound even if you never establish which handedness py3d reports, because both sides went through the same lens. When a diagnosis can be phrased as a relative comparison against a control inside the same asset, phrase it that way and it survives being wrong about conventions.
Imported-mesh budget invariant (added 2026-07-29, LFHeli HH-60G): a triples/triangle ratio
measured on a DECIMATED mesh does not extrapolate to an authored one. Decimate collapses edges
and breaks normal sharing, so it inflates resolved (point, normal, uv) triples per triangle. Measured
on the same asset: 1.301 on the Decimate-ratio export (0.26-0.6 per group) versus 0.941 on the
source's own Medium LODs — 28 % apart, and enough to change how many sub-.p3d you plan for (2.43
projected LODs against 1.75 measured). Rule: measure the ratio on the geometry you will SHIP, not on
whatever export you have lying around. Corollary, and it is the bigger win: if the source ships an
authored LOD ladder (GTA .yft High/Medium/Low, ripped racing-game, Sollumz-imported .blend datablocks), use it
instead of decimating — swapping obj.data to the <piece>_medium datablock is usually better in
triples AND satisfies the "the agent never decimates visual" clause of a product spec. Re-measure per
model; never copy a ratio or a resolved ceiling between assets.
Packaging invariant — a fix that lives in the generator and not in the binary does not exist
(added 2026-07-29, LFHeli HH-60G): when the cycle's fix is adding or changing a PROPERTY of a
.p3d (autocenter, class, sbsource, lodnoshadow), a SHA-based manifest cannot see it — it
compares the same wrong file on both sides and reports green. Real case: autocenter=0 was added to
the hull's visual LOD in both assemblers and the PBO was packed minutes later from .p3d built
before; measured afterwards on the 48 extracted models, the 8 sub-models carried it and all 40
hulls did not. The post-ExtractPbo gate must READ the property, per LOD, on the extracted files.
Two riders that make the gate survive contact:
- Declare the expectation in a table with a reason per row, never as "all LODs". A healthy pack contains controls that exist in order NOT to change (an A/B control arm, a byte-frozen baseline); a blanket gate rejects them and gets switched off on its first run.
- The producer must satisfy its own gate. Here "all LODs" would have rejected the canonical
assembler's own output, because the Memory LOD has no geometry to re-centre and therefore never
carries
autocenter. Exclusions are declared, not assumed.
Deployment-identity invariant — the artifact your gate validates must be byte-linked to the
artifact the game loads (added 2026-07-29, SUB_BRZ S42): a native-contract gate that consumes a
separate ODOL stage says NOTHING about the deployed PBO. -packonly copies whatever format sits in
the compilable tree, and pairing MLOD↔ODOL stage files by homonymous path is not closure. Measured
case: the gate validated the ODOL stage doors (~2.1 MB each) while the live PBO carried the source
MLOD (8.7/8.1 MB, byte-identical to the MLOD stage) — nothing in the pipeline linked the two
artifacts, and the mismatch surfaced only during post-crash analysis. Rule, before ANY deploy of a
gated artifact: list the PBO, extract or slice every .p3d entry by offset, and require (a) the
expected signature (ODOL where the contract is ODOL) and (b) SHA-256 equal to the approved stage —
fail on wrong signature, missing entry, casefold/slash collision, or hash mismatch. Corollary:
record the mounted PBO's SHA-256 contemporaneously at every runtime launch; without that seal, no
post-crash analysis can prove which artifact the process actually loaded.
Native-crash fingerprint — compare CODE BYTES, not fault addresses (added 2026-07-30, SUB_BRZ
S42): the RPT's Fault address is not an identifier — ASLR moves the module base, so the same
code site prints different addresses across runs and reads as "different bugs". What identifies the
detection site is Prev. code bytes + Fault code bytes (16 bytes each) plus the engine Version.
Measured case: six C0000374 crashes split across two addresses (3523D5F9 and 57B5D5F9) had
byte-identical code bytes and identical low 16 bits — one code site, one engine build. Reading
them by address had produced the (false, and formally rejected) claim that the ODOL-era and
MLOD-era crashes were unrelated; the byte comparison also killed the hypothesis that the artifact
FORMAT was the discriminating variable, since the crash appeared in both. Report "same/different
detection signature", never "same/different bug": C0000374 is detected on free, not where the
out-of-bounds write happened, so an identical signature still does not prove identical cause.
ITERATION BUDGET — check this BEFORE opening a front (added 2026-07-27)
Measured over one month of vehicle work (125 session handoffs: MercedesAMGLF 46, SUB_BRZ 38, LFHeli 37): 26 sessions ended in STOP/BLOCKED/FALSIFIED against 24 green. The three failure modes below are process gates, not config facts — and each burned dozens of sessions on a different project before being named. They cost more iterations than any invariant below.
- Count the cycles spent on one objective, and make the count block.
DZ-R5already says "after 2 rebuilds without progress, STOP and change strategy"; it does not bite because nobody tracks N. Carryciclos_en_este_objetivo: Nin the HANDOFFLIVE-STATE, and at N>=3 the re-entry gate must pick one of three exits before touching code: change strategy, escalate to a human, or drop the objective. Evidence: MercedesAMGLF Task 9 reached v64 across 29 sessions, and what finally closed it was shifting 28 proxy triangles by -0.240 m in X. - Keep the test harness OUT of the product task. Task 9 mixed "align the Mercedes proxies" (product) with "build an automated live verifier" (daemon, broker, lease, preflight, camera contract, JPEG SHA) — the harness silently took the bulk of the time and became the project. If a product task is blocked TWICE by the harness, stop the product and decide explicitly: scope the harness as its own project, or run that one test by hand and move on.
- When the judge is the eye, offline analysis gets ONE round. Green offline gates do not
predict the live result. A Task 9 build reported
263/263 global + verifier 35/35 hard PASSplus every SHA, and then:Visual: FAIL of evidence, client hung. The LFHeli "seis frentes" night spent ~25 Codex sessions to produce refutations only (binding intact, re-bake refuted, clock and interpolation refuted) and concluded the discriminator was the user's own screenshot. If the discriminator is visual or feel, spend one offline round ruling out cheap causes, then go to the game — extra offline rounds postpone the verdict, they do not replace it.
Corollary already paid twice: SUB_BRZ needed 30+ sessions to conclude that a decimated rip mesh cannot reach game-ready quality (pivot to a human artist), and the LFHeli HH-60G reached the same verdict independently weeks later. A visual red that survives N rounds is a signal to escalate to a human, not to run round N+1.
REDIRECT CAMBIO-1: familia B →
../rip-vehicle-import/cookbooks/family-b/radial-puerta-ausente.md.
INV-ALIGN — a faithful import does NOT mean the pieces line up (added 2026-07-27)
A bit-exact import and correctly-fitting pieces are different properties. When a model is assembled by script, each piece is placed at a procedurally computed anchor (centroid, bbox centre, a reference point). If that anchor diverges from the real reference, the pieces sit wrong while every import check passes. Paid three times (MercedesAMGLF, SUB_BRZ, LFHeli):
- MercedesAMGLF: the 28 wheel proxies sat -0.240 m in X from their Memory hubs. Fix: measure the offset with py3d and shift only those triangles (max error 0.0 m afterwards).
- LFHeli OH-1: import fidelity 12/12 at max 3.2e-7 m, and the
SEATSprocedural anchor still diverged 583 mm from the source mesh.
Gate before declaring an assembly good: for every proxy, measure its anchor against the real reference (the matching Memory point, or the source mesh position) and assert the delta is ~0. Import fidelity, digest equality and structural diffs do NOT catch this — they compare the piece to itself, not to where it belongs.
Do NOT "fix" it by adjusting the pipeline transform. That is the intuitive move and it is a false fix: it displaces everything else that was already correct. Correct the individual anchor.
False friend — autocenter=0 is not this bug. It is a real and separate requirement (the
engine recenters LODs by bbox without it, displacing collision ~20-25 cm from visual; see
dayz-model-pipeline and dayz-p3d-audit), but it does not cause or cure anchor divergence.
MercedesAMGLF spent a full RCA on that hypothesis and closed it as unproven: adding autocenter=0
to all 18 proxy LODs changed nothing geometric, and the protrusions and see-through were already
present in the earlier baseline. Do not re-run that investigation.
TEXTURE OR MESH? — discriminate BEFORE auditing the texture pipeline (added 2026-07-27)
"The textures look wrong" on an imported vehicle is a symptom with at least three unrelated causes, and auditing the texture pipeline is the most expensive way to find out which. Run this ladder first; each rung is minutes, the full audit is sessions.
Audit the UVs, not the textures. Exact SAT overlap + island count + density (
uv-clean-atlas,uv_audit.py). Overlap > 0, atomised islands (2-4 faces each) or wildly uneven density means the problem is upstream of any.rvmat— no material wiring fixes it.No-overlap means an exact
SAT=0(SP-214, added 2026-08-31). A Monte-Carlo estimate has a measured nonzero floor, so its displayed0%is not proof. Route vehicle UV repair touv-clean-atlasand use its current chart/SLIM/fold/SAT and semantic-packing contract;PartUVis not the vehicle default. One anti-fold round is a cost target, not a correctness exception: run the same exact SAT detector after every round, continue when it is nonzero, and block any mesh that does not reach zero. This pack owns the routing and acceptance gate, not a second copy of that implementation.- Ask where the mesh came from. A decimated rip cannot yield game-ready UVs: the cause is topology, not the unwrap algorithm or the texture route. The fix is retopo (bake the high-poly onto a clean low-poly), and no amount of pipeline correction substitutes for it.
- Render the same material on a known-good control. If a vanilla-derived control looks right with the same route, the pipeline is exonerated and the defect is in this asset.
- Only now audit
.rvmatstages,_co/_nohq/_smdipaths,.paaconversion andhiddenSelections— seedayz-texture-pipeline, and itsreferences/vehicle-materials-and-color-variants.mdfor the vehicle-specific shader choice (Super where faces carry a_co, NormalMapSpecularMap for constant-colour parts, tiled detail for overlapping or un-baked UVs).
Cost rule: if two rounds of pipeline auditing come back clean, "the texture is applied wrong" is REFUTED — change branch instead of running a third. An audit that exonerates the pipeline is evidence about where the bug is NOT, and repeating it produces no new information.
Evidence for the rule: on LFHeli OH-1, F6 ran a double audit that exculpated the entire offline
chain (UVs identical across blend/MLOD/ODOL, old PAA identical to new, rvmat uvSource=tex, zero
hiddenSelections, no faces inside the watermark box) — the textures were never the problem. On
SUB_BRZ the documented root cause was the decimated rip's topology, reached after six rounds and
a pivot to a human artist. In both cases the ladder above would have branched away on rung 1 or 2.
Rip-specific rung 0 — a "paint" material in the source has NO diffuse; never substitute a livery sheet (added 2026-07-27)
Before running the ladder on a ripped vehicle (GTA, ripped racing-game, any game rip), census which source
material each face carries and what the import mapped it to. Game rips colour the body through the
host game's paint system, not through a texture: in GTA those materials are
vehicle_generic_smallspecmap* / "primary" and their diffuse is legitimately absent or a
zero-byte file. An importer that needs some texture per face will silently fall back to whatever
is available — and if what is available is the model's livery/decal sheet, the result is signs,
numbers and camo stamped across the whole fuselage. The symptom reads as "the UVs are wrong" or
"textures are crossed", and both readings send you down the expensive branch.
- Discriminator (minutes): count faces per
(source material -> texture)pair. A single texture carrying a large share of the model, especially a decal/livery atlas, is the signature. Cross-check the source file size on disk: a 0-byte or missing DDS proves the fallback fired. - Fix direction: those faces want a flat colour
_co(the host game's paint slot has no DayZ equivalent), not the decal sheet and not a re-unwrap. Their UVs were never meant to address a texture, so no UV work is involved. - Measured evidence. LFHeli HH-60G (2026-07-27):
hh60g_sign_1_co.paa— a 22.4 MB livery sheet — carried 23,716 / 35,385 faces (67%), of which 8,695 (24.6% of the model) were pure fallback (7,568 of the body's[PRIMARY]material plus all four doors), withvehicle_generic_smallspecmap3.ddsconfirmed 0 bytes on disk. The user's independent report was "things and colours drawn where they don't belong". The OH-1 line hit the same class separately (rotor blades wearing camo and a registration number) after the PAA route had been exonerated. - Status honesty: the diagnosis is measured; the flat-colour fix was not yet confirmed in-game at the time of writing — treat the fix direction as unverified until a cycle closes it.
A mesh DRAWN in the wrong place is invisible to every script transform — qualify the probe first (SP-138, added 2026-07-29)
When the user reports that a vehicle draws somewhere the entity is not — body on the ground while the vehicle climbs, a part left behind, a piece floating — the reflex is to instrument the transforms and compare. That reflex has already been paid for and it returns nothing.
Measured on LFHeli OH-1 over a 26 m climb: root position, GetRenderTransform, both bones, the
physics body (dBodyGetWorldTransform) and the pilot's world position agreed digit-for-digit
while the hull was drawn on the ground. Recorded in that project's own code at
LFHeliCore\scripts\4_world\LFHeli\LFHeli_Base.c:3106-3113. The consequence is the general rule:
Any probe derived from the entity root inherits the root's blind spot. A drawing-position defect lives downstream of every transform Enforce exposes, so no amount of transform logging can see it.
What does work — a marker the engine projects from a WORLD point without consulting the entity's
transform, read off a screenshot: Debug.DrawSphere at GetPosition() plus a ladder of rungs at
fixed spacing below it. The rung spacing is what makes the defect quantitative (gap in metres =
rungs × spacing) instead of an eye report. Reference implementation: LFHeli_Base.c:3114-3128.
Probe qualification, mandatory before any number adjudicates: read the magnitude in the regime
where the drawing is CORRECT (on the ground) and in the regime where it is WRONG (airborne). If it
does not change while the eye sees a difference, that magnitude is blind — stop using it. A delta
of exactly 0.000 is a tautology signal, not precision.
The companion trap, on the inference side. Over flat ground, "the visual Y is frozen at its take-off value" and "the transform is computed from a ground-referenced magnitude" produce the same two signatures: gap grows with height, gap returns to zero on landing. Arithmetic, confirmed by external review. So the flat-ground signature must never be used to EXCLUDE a family of causes — it identifies nothing. Discriminate by changing the regime, not by reasoning:
- horizontal traverse at constant world Y over terrain ≥15 m lower — ground-referenced tracks the terrain, frozen-Y does not;
- descend without touching down and climb again — a replication/state split shows hysteresis, a pure function of height does not.
- Status honesty: the blind-spot measurement and the flat-ground ambiguity are verified; the two discriminant manoeuvres above were designed on 2026-07-29 and had not been run in-game at the time of writing. Treat them as an untested procedure until a cycle closes it.
REDIRECT CAMBIO-1: familia B →
../rip-vehicle-import/cookbooks/family-b/get-in-ausente.md.
INVARIANTS YOU WILL HIT — preflight checklist (read BEFORE authoring, not after the in-game fail)
These recur on every vehicle. They were each won the hard way on one project and then re-derived from scratch over dozens of iterations on the next (LFQuad → SUB_BRZ → MercedesAMGLF), because they were not promoted to this checklist in time. Check them up front; full detail behind each pointer.
BEFORE copying a donor — verify WHERE the engine is, not how many seats it has (SP-205, origin 2026-08-08, SUB_WRXSTI T8).
sedan_02.c looks like the vanilla 4-seat sedan control and is rear-engine: it closes CarBattery
and SparkPlug behind the trunk and the CarRadiator behind the hood
(scripts\4_world\entities\vehicles\inheritedcars\sedan_02.c:130-148), and gates CanDisplayCargo
on the hood (sedan_02.c:191). Porting that to a front-engine car leaves the engine parts behind
the wrong lid — a bug that only shows in-game. The correct control for front-engine + 4 seats is
civiliansedan.c. Rule: before copying a donor, verify WHERE the engine lives, not only how
many seats it has. Rear seat on that 4-seater (measured vs civiliansedan_mlod.p3d; in-game
confirmation pending WRX live test #1): Memory pos_cargo1(_dir), pos_cargo2(_dir),
seat_con_1_2, seat_con_2_2; GetSeatAnimationType cases 2/3 → VEHICLESEAT_PASSENGER_L /
VEHICLESEAT_PASSENGER_R and CrewCanGetThrough cases 2/3 (civiliansedan.c:95-107,
:214-244) — the base returns false (transport.c:493-499). Distinct crew_cargo proxy index
and seat-box lead: invariants 28 and 30.
Four-seat completion rider (SP-205, added 2026-08-31). Rear seats also need
seat_cargo1/2 boxes dual-tagged to their exact componentNN in Geometry and
ViewGeometry; crewcargo1/2 proxies in ViewGeometry and FireGeometry using one
crew_cargo path with distinct instance indexes; Crew.Cargo1/2 entries with
actionSel, proxyPos, getInPos, and getInDir; and the two passenger cases
in both GetSeatAnimationType and CrewCanGetThrough. CarScript already maps
all four seat_* selections to seat_con_1_1, seat_con_2_1,
seat_con_1_2, and seat_con_2_2 (carscript.c:2674-2689), so an override of
GetDoorConditionPointFromSelection that only repeats that table is dead code.
That base table is the LEFT-HAND-DRIVE mapping (SP-426, added 2026-10-01, WRX STI B-01).
[EXACT][CLAIM-GETIN-LHD-BASE-TABLE] On a right-hand-drive car whose memory points are named by
physical side (driver door = seat_con_2_1), the base table pairs every seat with the FAR door:
each seat is evaluated against the opposite door and CanReachSeatFromDoors fails the 1.0 m
plan-distance test from the correct one, so no get-in appears with names, points and componentNN
all correct. Signature in query_get_in_condition: unreachable from the correct door and
available from the opposite one. Override it crossing the sides (driver->seat_con_2_1,
codriver->seat_con_1_1, cargo1->seat_con_2_2, cargo2->seat_con_1_2), as vanilla does for its
own layouts (OffroadHatchback.c:364-379, Van_01.c:269-287, DayZ 1.30.164014 Exp). On RHD cross
GetSeatAnimationType for the rear seats too (vanilla pairs L with the physical left side,
CivilianSedan.c:119-134); the driver keeps VEHICLESEAT_DRIVER, whose get-in animation will look
LHD-sided - an engine limit, not a mod bug.
BEFORE any numbered gate below — calibrate the gate itself (SP-132, added 2026-07-29; LFHeli HH-60G + OH-1). Two rules in this file already cover this — §"Calibration + scope (so a gate is trustworthy, not a false green)" and §METHOD habit 2 ("never close a phase on a tautological gate"). Both were live and neither fired across four gates in one week, because both read as being about car render/winding gates. They are not: they govern every item below, and capacity, parity, telemetry and policy gates alike. The four shapes, so they are recognizable on sight:
- A threshold copied instead of measured.
hh60g_assemble.py:29carriedRESOLVED_LIMIT = 65535; the engine's real resolved-vertex ceiling on that model is 46.133 (42% lower), so the gate passed modelsbinarizerejects withToo many vertices. The cap is model-dependent (×1.4205 on HH-60G, ×1.46 on OH-1) — never copy it between models, re-measure with binarize. - A policy that is a mathematical no-op.
uniformandkeepdotwere geometrically inert (dot(-cross,-n) == dot(cross,n)); four variants were built on them before the identity was checked. - A gate that shares the producer's frame. OH-1 seat parity compared under pure translation — the
same assumption the sub-model was generated with.
delta 0.000proved producer and gate agreed, not that either was right. - A field read selectively.
attachment_count=2was signed PASS because that one field matched expectation; the regression sat in the same output 20 min before the user hit it in-game.
A fifth shape, and it fakes an ABSENCE rather than a green (added 2026-08-19, SUB_WRXSTI): matching
a LOD by its nominal resolution. LOD resolutions on disk are not the round numbers the docs use —
FireGeometry reads 6999999976046592, not 7e15. A bucket keyed on round(7e15), or any ==
against the nominal, comes back EMPTY, and a gate built on it reports "the selection is absent" about
something that is present. Measured cost: a cross-check of the config's componentNames[] against the
.p3d reported all six damage zones missing, and they were all there in FireGeo — the report reached
the user as a red before the control against vanilla caught it. Always match with a RELATIVE tolerance
(abs(res - nominal) <= nominal * 1e-3), and note the direction of the failure: this one does not
show up as a suspicious green you might question, but as a confident red about missing data, which is
the shape people act on fastest.
One minute, before trusting any gate: (a) run it against a KNOWN-BAD case — green on a known-bad means the bug is the gate, not the artifact; (b) name in one line the assumption the artifact was BUILT with, and confirm the gate does not reuse it. A gate that has only ever been seen green is unmeasured, not verified.
- Gates that stay red stop being sensors (LL-244). If a known failure is deliberately accepted, pin its exact signature — entities and measurements — and report
KNOWN_REDseparately fromNEW_RED. Any drift, including an improvement, is new evidence because the pin describes a state, not a ceiling. A gate that returns the same verdict for N runs without making a new red distinguishable has become a constant.
- Gate #0 — mesh + UV health BEFORE anything else (SP-052). For ANY imported model (ripped or not),
the FIRST step is a mesh+UV audit, because a broken mesh produces false downstream diagnostics (a
"double wall" that was mirrored UV on a single wall; a "convexity" issue that was open design-boundary
edges). (a) audit
mesh_health.py(boundary / non-manifold / dup-verts / dup-faces / winding per piece) +uv_audit.py(islands / overlap / density / mirrored); (b) sanitize with an ADAPTIVE weld (ladder 0.5→0.01 mm; gates: NM must NOT grow + UVs preserved — a blind 0.5 mm weld FUSES legitimately-touching pieces and CREATES non-manifold); (c) unwrap BY SECTIONS perAI/20_Knowledge/uv-mapping-dayz.md§0.5/§5; (d) gates: overlap ~0% for bake, uniform density, atlas legible by a human, checker render with SMOOTH shading (flat shading + grid raster read as a false "low-poly/blurry"). Tools inLFQuad_dev\tools\(mesh_health.py, mesh_sanitize.py bpy-headless; uv_audit.py py3d) — portable to any vehicle. →AI/20_Knowledge/uv-mapping-dayz.md, winding #10(f).
0b. Gate #0b — when LOD1/2 come from DECIMATE instead of an artist, audit the modifier's OUTPUT
(added 2026-08-06, MercedesAMGLF). No artist is now the normal case, so per-object COLLAPSE +
planar DISSOLVE is the default LOD path. The modifier leaves loose vertices — vertices no
triangle indexes. Every count-based gate stays green (triangles, resolved, identity, monotonicity,
frame parity), and the streams ship broken: a per-vertex extract loop driven by mesh.loops never
reaches a loose vertex, so whatever the array was initialised to is what gets written. Initialised
to (0,0,0) → zero-length normals in NORMAL.bin. Measured: source had 0 loose in 224.700 verts;
after decimation, 786 at LOD1 and 4.024 at LOD2 — plus 16 normals of length down to 3e-6 that a
> 0 usability test happily passed. Three checks close the whole class:
- delete loose verts after every decimate (
bmesh.ops.delete(bm, geom=[v for v in bm.verts if not v.link_faces], context="VERTS")). NOT one of the three forbidden calls — it removes only geometry no triangle references. Skip the bmesh round-trip entirely when there is nothing to delete, so must-keep pieces never pass through it. - assert every emitted normal is unit length and normalise on emission. The real cut is 0.5, not
0: a unit normal survives float32 within ~1e-7 of 1.0, so anything shorter is a cancelled sum.
Substitute from NEIGHBOUR normals, never from
poly.normal— if the import transform negates an axis it is a mirror, and the winding-derived normal points the opposite way (see #10 winding). - assert every index falls inside its own piece's vertex range. No count gate sees an out-of-range index; it explodes at assembly.
Gate corollary — a drift number that mixes classes is not a measurement. Split survivors into
INTACT (incident-triangle set identical to LOD0 → drift MUST be ~0; any drift here is a real defect),
RECOMPUTED (topology changed → drift is legitimate) and DEGENERATE (counted, never averaged in).
Mixed together, a p99 of 90.00° hid a real defect AND hid that untouched geometry was clean at
≤0.26°. An exact round angle is a fingerprint, not a result: 90.00 is acos(0) = a null vector on
one side. Match survivors on a ~1e-4 m grid, not 1e-6 — positions are written as float32 and a 1e-6
grid drops about a third of the untouched vertices at random, which is why the samples looked tiny.
Position is a candidate bucket, not identity (SP-163, added 2026-08-31).
The grid above may find candidates, but a seam can place distinct normal/UV corners
at the same coordinate. Pair intact data by a carried source vertex/corner and
primitive ID. If that identity was lost and a bucket is non-unique, return
INCONCLUSIVE, not PASS or FAIL. Run source against itself through the same matcher
and require approximately zero residual before trusting any product comparison.
- "Get in" radial needs a real SCRIPT CLASS, not a bare config class. A
class <Mod>: CarScriptwith no.cruns as bareCarScript, which inheritsTransport.CrewCanGetThrough()→false(scripts/3_game/vehicles/transport.c:493) → the get-in action is filtered out and never appears, no bone/proxy/componentNN fix helps. Author<Mod>_Base extends CarScriptoverridingCrewCanGetThrough+GetAnimInstance+GetSeatAnimationType(+CfgModsworldScriptModule). Verified in-game LFQuad D34 + MercedesAMGLF + SUB_BRZ. →vehicle-structural-parity.md"Crew get-in". - The script module must actually LOAD or the class never binds — silent.
CfgModsfiles[]with back-slashes / a.p3dpath that resolves to*.p3d.p3d→ module not loaded → no get-in, no error. →SKILL.md§"Binding del script",rip-import.md. - REDIRECT CAMBIO-1 (familia B):
../rip-vehicle-import/cookbooks/family-b/wheelpresent-0.md. - Seats and wheel hubs must be
componentNN-tagged (dual-tag) — AND each seat component must be raycast-collidable. The engine enumerates collision components only bycomponentNN; a seat/hub that is a standalone island (0% componentNN overlap) is invisible → spawn blocker / no seat. Tag the SAME faces with acomponentNNtoo. Then the ViewGeo seat cube must be INWARD-wound with every point flag =0x0000003F: a py3d box left outward + flags 0 passes every offline shape/winding/dual-tag check yetRaycastRV(ObjIntersectView)does NOT hit it → the get-in cursor falls through to component0 → the driver "works" by fallback but the CODRIVER never resolves. The decisive, in-game-confirmed copilot blocker on BOTH SUB_BRZ (s9) and MercedesAMGLF (s12, headlesshit=1 comp=6 crewIdx=1). The s9 patch changed winding and flags together and never isolated the flag as cause; the safe rule is to copy the sealed vanilla control's convention, never py3d's default. → "componentNN DUAL-TAG" + "CRITICAL EXTENSION 2026-06-28" (vehicle-structural-parity.md). positive control (LFQuad/Croco), never trust py3d's default. → "componentNN DUAL-TAG" + "CRITICAL EXTENSION 2026-06-28" (vehicle-structural-parity.md). Resolve the seat'scomponentNN; never hardcode its number (SP-079, added 2026-08-31). Assembly order moves component numbers. For everyseat_*selection, compare both point and face membership against eachcomponentNNin the same LOD and require exactly one exact 1:1 match. Zero or multiple matches is a hard failure. Re-run the equality after any transform or transfer so the seat/component dual tag cannot be lost silently. - Crew/wheel proxies must exist in BOTH ViewGeometry AND FireGeometry; the proxy triangle uses the
engine identity frame (
R=((-1,0,0),(0,0,1),(0,1,0)), model-space), NOT py3drotation=None. → parity + rip-import.
5b. wheelHub names a GEOMETRY/MEMORY selection, not the visual wheel_X_Y — and the visual
wheel_X_Y IS the proxy's own triangle under a second name (added 2026-08-07, MercedesAMGLF).
class Wheels { class Left { wheelHub="wheel_1_1_damper_land"; } } resolves to a selection that
lives in Geometry 1e13 (a ~0.20 m hub box) and Memory 1e15 (1 point) — vanilla
DZ/vehicles/wheeled/config.cpp:297. It is NOT the wheel_X_Y selection on the visual LOD.
Measured on two independent cars: that visual companion has the SAME centroid and the SAME bbox
as its proxy:<path>.NNN selection, i.e. it is one triangle carrying two names (vanilla shows it
as 1 face too). Consequences, each of which cost a session:
- A gate of the form "proxy anchor -> companion centroid == 0" measures nothing about the wheel.
It computes
|anchor - centroid|of a single triangle, which is a pure function of that triangle's SIZE. Measured on the same model: 0.131757 m for a 0.339x0.204 triangle vs 0.000745 m for py3d's canonicalscale=0.001. Against the real hub both artifacts read 0.000000 m. Point the gate atwheel_X_Y_damper_land. - Proxy triangle size bakes NO scale. The frame is derived from UNIT vectors
(
py3d/__init__.py:174-179, port ofproxy_frame.derive_frame), and vanilla's binarised proxy matrix is orthonormal (|aside|=|up|=|dir|=1.000000onciviliansedan.p3dODOL v54). A 1 mm canonical triangle and a 0.34 m one place the same wheel; SUB_BRZ shipped onscale=0.001. Do not "fix" a small triangle. - Vanilla reference, if you need one:
civiliansedanputs the hub 0.106334-0.107333 m INBOARD of the wheel proxy, the whole delta in X. Hub coincident with the proxy (0 m) is tighter than vanilla, not a defect. - Before matching a magnitude to another open debt, decompose it by AXIS. 0.1318 m was read
as "the wheel drawn 13 cm off its simulated position" because it resembled a 0.136 m track
debt; the 0.1318 lives entirely in Y/Z (
dX = 0.000000exactly on all four wheels) and the track debt lives entirely in X. Orthogonal — the resemblance was numerology. - Read a gate's PRODUCT verdict from a run WITHOUT its negative fixtures, then repeat it on
the known-good artifact as a control. Run with
--negative-fixtureand the fixture's deliberate reds (yaw-180 families, stripped companions) interleave with the product rows and read as product failures.
5c. Owner-only driving hitch: audit hub-ground clearance and remove LandContact
before blaming scripts (SP-355, added 2026-08-31). The driver owns the physics
solve while a passenger interpolates, so smooth passenger motion does not clear
collision geometry. Working DayZ car controls use the tyre's PhysX sphere plus a
small wheel_*_damper_land Geometry box (about 0.20×0.22×0.22 m) and no
LandContact LOD. Require hub_y - mounted_tyre_radius to exceed both
Suspension.travelMaxUp and travelMaxDown. A wheel-sized hub or retained legacy
LandContact points at the tyre plane can strike terrain every bump and make the
owner's solver hitch. ObstacleGenerator { carve=1; } and visual face count do
not discriminate this defect; a heavier known-good control refutes them.
- Wheel
angle1sign: measure the axle in the.p3dBEFORE setting it. An offline check predicts reversed spin without an in-game cycle. →build-packaging-and-debug.md§2-3. - Imported-model winding: keep the RAW glTF winding VERBATIM — NEVER orient to a normal oracle
(authored/
__N) nor to a radial/centroid heuristic (see #10j). The pipeline's__Nwas never authored (rip_p2_group.py:110,131— smooth normals of the pre-mirror winding) andorient_authoredinverted EVERY piece; repair only the ~0.5% source-inconsistent FACES by MAJORITY flood-fill per connected component, AFTER de-dup (#10e). Blender backface-cull render = HINT only (its convention false-greened once — it shows the +cross side, the engine renders the ANTI-cross side); in-game render is the gate. → #10(j) +SUB_BRZ_dev\reviews\2026-07-02-s20-plan-reimport-unico-v2.md. - One vital igniter per car — vanilla defaults BOTH SparkPlug AND GlowPlug to vital, so a bare
CarScriptcar silently demands BOTH.CheckOperationalRequirementssetsNO_IGNITERif either vital plug is missing (carscript.c:2004SparkPlug /:2011GlowPlug); everyIsVital*defaults true (carscript.c:2734-2762,transport.c:303). A PETROL car (CivilianSedan) keeps SparkPlug vital and overridesIsVitalGlowPlug()→false(civiliansedan.c:363) +IsVitalTruckBattery()→false(:358); a DIESEL car (Offroad_02) mirrors it —IsVitalSparkPlug()→false(offroad_02.c:389).attachments[]must declare ONLY the vital plug andOnDebugSpawnmust attach THAT part. "Engine won't start" with a SparkPlug already attached = the un-overridden GlowPlug, NOT a removed requirement —IsVitalGlowPlug→falseIS the vanilla petrol pattern (the car still requires its SparkPlug). Bit SUB_BRZ (petrol car defaulted to needing a GlowPlug it had no slot for; a "remove the check" framing got it backwards). →vehicle-config-and-modelcfg.md§engine parts. - Engine STARTS then immediately STALLS (eng=1 for one tick → eng=0 rpm=0) with fuel + ALL vital parts
attached → the car is missing its
Engine(andFuelTank) DamageZone.CarScriptstops a running engine EVERY tick whenm_EngineHealth <= 0(carscript.c:991), andm_EngineHealth = GetHealthLevelValue(GetHealthLevel("Engine"), "Engine")(carscript.c:2572-2573) — aDamageSystem. DamageZoneswith noclass Engineresolves "Engine" health to RUINED → perpetualEngineStop(). A missingclass FuelTankcompounds it (carscript.c:1004ruins the Engine zone when FuelTank reads RUINED). EVERY vanilla car ships BOTH zones; an imported/regen car declaring only body zones (chassis/front/back/roof) LOOKS configured but the engine never sustains. Fix: addclass Engine+class FuelTanktoDamageZones— the proven shape (vanilla/SUB_BRZ) pointscomponentNames[]at a FireGeodmgzone_engine/dmgzone_fueltankselection; a config-only health zone (emptycomponentNames[]/memoryPoints[]) is the minimal variant (verify it registers). DISTINCT from #8: #8 gates whether the engine STARTS (vital igniter parts); #9 gates whether a STARTED engine STAYS ON (engine-health zone). Bit MercedesAMGLF (stalled with SparkPlug+CarBattery+CarRadiator+fuel+coolant ALL present; SUB_BRZ ran only because it carried both zones). Verified vscarscript.csource + SUB_BRZ in-game parity. - Imported-car offline VISUAL gates that LIE — the #7 family (offline geometry heuristic != in-game truth). The per-session changelog of sub-entries #10(a)-#10(o) (steering-axis fit, single-sided see-through, debug fluids, raycast oracle, rip duplicate faces, per-piece winding uniformity, ViewPilot interior, bright-triangle shading seams, the import-orientation saga, glass occluder twins, foreign-LOD material transfer, gap skirts, hub-lift decoupling, get-in-preserving patches) lives in
references/visual-gates-and-winding.md(s14->s23, SUPERSEDED entries archived there). The single operative winding rule stays here:
- THE RULE (import orientation, #10j): keep the raw glTF winding VERBATIM for ALL pieces (net rip→DayZ =
(-Fx, Fy+Y0, -Fz), det=+1, preserves the authored visible side end-to-end); stored MLOD normals = smooth(+cross) of the FINAL winding. NEVER orient winding to a normal oracle or to outward-of-centre. Repair ONLY source-inconsistent components by MAJORITY flood-fill per connected component (never minority-area).glass*int_apanes are legitimate cabin-side glass (do not delete as z-fight). Full mechanism, measurements, and the SUPERSEDED (h)/(i) history:references/visual-gates-and-winding.md#10(j)/(f). - Winding is per profile (SP-217). rip/vehicle with measured transform det=+1: preserve (
#10j). Generic DCC import with no profile:dayz-model-pipelineRule 12 (det=-1 map, face order kept, normals negated; no face reversed). The in-game A/B (SP-070) adjudicates correction; a lineage gate adjudicates preservation. Do not apply genericdayz-model-pipelineSmart UV / decimation / flip defaults to a vehicle. (Aligned 2026-10-03 with Rule 12, measured in game 2026-10-01: the generic case read "Generic DCC import with no profile: flip visual by default (SP-071; proxies exempt).")
- Vanilla forward convention is nose = −Z (ENGINE-NATIVE), and the "visual side" of an imported car is NOT one homogeneous block — measure EVERY body proxy separately before any bulk rotation (MERCEDES_AMGLF review 2026-07-03; convention verified offline on native data, the yaw-180 fix's in-game gate pending).
- Convention measured on the raw vanilla ODOL (NOT a debinarized MLOD, so no converter-flip doubt):
sedan
light_1_1Z=−2.387,dmgzone_front−2.355, front axle −1.655, exhaust at +Z (tail). Nose=−Z is engine data. (Side-finding: the debinarized sedan MLOD is sign-faithful to the ODOL.) - Perceived-"forward" chain: the 3PP vehicle camera inherits the SEATED PLAYER's yaw (crew-proxy
orientation, sim side) + an MS offset —
dayzplayercameravehicles.c:39-49,93-105(m_fIgnoreParentYawcommented out at:194), defaultstransport.c:452-463. Headlights and hubs play NO role in what the player sees as forward. Traction sign is C++ (car.c:199-274proto native) — inferred −Z from the working sedan; not script-readable. - Symptom map: shell visual yaw-180 vs sim ⇒ 1st gear reads as "drives backwards" AND wheel
proxies sit mirrored (−X,−Z) off their hubs, BOTH at once. Diagnose by comparing
dmgZone_front/hubs (sim) vs headlights/wheel-proxies (visual) against nose=−Z.
Historial del texto superado:
history/cambio-1-superseded-family-b-rules.md§“Invariante 11”.
- CORRECTION to THE TRAP (added 2026-08-01, adjudicated from the applied fix's dated backups +
in-game outcome — supersedes the "interior/dash/steering/occlusion correctly sim-aligned" claim
above): on the Mercedes those
mb_contents were VISUAL-aligned (coherent with the yaw-180 shell), and the executed fix had to rotate the ENTIRE cabin cluster — all 6mb_contents + crew proxies (View/Fire) +pos_driver/codriver(+_dir)+drivewheel(+_axis, re-authored to the vanilla rake) — in a second same-day pass (backupspre-visual-yaw180-2026-07-07\vspre-cabin-align-2026-07-07\: shell rotated, cabin still old), after a first shell-only pass per the 07-03 scope left the cockpit 180° off the body. The sim-aligned claim came from content-bbox reading plus validatingmb_steeringagainst a memory point of the SAME misaligned cluster (self-validation — the SP-132 "gate shares the producer's frame" shape). Rules that survive: (a) still measure each proxy separately, but adjudicate cabin orientation with ANATOMICAL PAIRS (seat↔steering-wheel↔column-rake↔driver side) against untouched sim anchors (hubs, dmgZones, engine memory) — never with mixed-content bbox or the cluster's own memory points; (b) the correct rotation unit is EVERYTHING that defines the visual+pose experience: shell + body proxies + crew proxies +pos_*/drivewheel*cockpit points. Post-fix coherence verified offline (anchors exact on hubs, LHD seat side, wheel 0.34 m ahead of seat, rake (0,0.516,0.857) vanilla-parallel) and live (Task 9 v64 PASS + manual drive 2026-07-23, no inverted-drive or backwards-pose reports). Full adjudication:MERCEDES_AMGLF_dev\reviews\2026-07-02-fable-review-plan-OUTPUT.md. R22 refinement (2026-08-01, Codex re-measured the same backups — narrows the rule above): the "rotate the entire cabin cluster" adjudication is Mercedes-SPECIFIC; the durable rule is two-sided — (1) adjudicate cabin orientation with the anatomical pairs against untouched sim anchors, then (2) rotate exactly the adjudicated cluster IN EVERY FILE/LOD THE HOST REFERENCES, measuring each file, never presuming: the applied 07-07 fix itself MISSED the 12mb_*_lod1/_lod2.p3dreferenced by the host's visual LODs 1-2 (byte-identical to pre-fix, hash-verified by two independent reviewers) — so mid-distance LODs still draw the body proxies in the OLD orientation. The original warning to measure every proxy separately SURVIVES; what was wrong was its Mercedes factual claim, not its method. - Cheap steering-wheel check: if the
drivewheel_axismemory pair is near-VERTICAL (Y-dominant direction), the wheel sweeps left-right like a wiper instead of rotating in its plane. The axis must be ~perpendicular to the rim plane (Z-dominant, some Y). Evidence + probes:MERCEDES_AMGLF_dev\reviews\2026-07-03-fable-review-b1b6-plan.md.
- REDIRECT CAMBIO-1 (familia B):
../rip-vehicle-import/cookbooks/family-b/coche-blanco.md. - **Distance-LOD ladder: a single-visual-LOD import renders its FULL face count at ANY distance —
Historial del texto superado:
history/cambio-1-superseded-family-b-rules.md§“Invariante 13”. author the ladder BEFORE fighting LOD0 decimation.** (added 2026-07-07, SUB_BRZ s25 measured) Vanilla civiliansedan ships 5 visual LODs (14,636 → 10,364 → 3,717 → 1,713 → 123 faces); SUB_BRZ shipped 1 (231k always) and the admin-preview/spawn freeze did NOT move with dedup (−18.5 MiB) or shadow (32k→3.5k) fixes — it is render/face-bound. Distance LODs res 2/3/4 are baked FLAT into the main (no proxy refs: proxies of res-0 only render while res-0 renders) from the rip's authored LODs (planar-dissolve measured ceiling = ÷2-2.6); exclude the cabin from far LODs (vanilla does). Day-1 check: count visual LODs vs the control — ≥2 is also the product-spec floor (AC1.4-class). LOD0 decimation stays user-gated and becomes a LAST resort, not the first. s26 EXECUTED the ladder (SUB_BRZ deployed, measured) — measured continuation of the contract: (a) Planar-dissolve (Blender limited-dissolve, shape+UV preserving) tops out at ÷2-2.6, NOT ÷5-8 — the rip's authored LODs are mid-detail racing meshes, not impostors. SUB_BRZ body 58.7k → 34.5k@15° / 27k@30° / 22.4k@45°. It yields ONE good near LOD (res2 ~27k, quality intact, no gate). To reach vanilla parity (sedan res3/res4 = 3.7k/1.7k) you need COLLAPSE decimation, which deforms and is user-gated — so MEASURE the planar ceiling and ask how far to take the ladder BEFORE promising res3/res4.decimate_blender.py(planar) preserves material-slot + UV; collapse does not. (b) Bake far-LOD stored normals OUTWARD — SUPERSEDED in part by (d) for the winding census. The body winding gives +cross INWARD (the build negates paint/lamp classes vianegate_classes); a flat LOD baked with raw +cross renders ~60% inside-out (black). Fix that BLACK failure in the stored normals; thecross·(centroid−carCenter)winding target comes from (d)'s census against the LOD0 control (the corrected SUB_BRZ far LOD measured ~2%, not 100%), and vertex order is handled there. resolved<65535 holds easily (26k for a 27k-face body). (c) The transplant assumes exactly ONE visual LOD (p4_transplant_dyn assert len(vis)==1). A ladder needs a multi visual-LOD transplant: replace the deployed visual block with ALL work res<100 LODs (sorted), KEEPING every struct LOD. GATE it with a self-test on a COPY: struct LODs (Geo/Mem/HP/ViewGeo/FireGeo) must come out BIT-IDENTICAL (get-in), then re-verify real deployed vs backup. Flat body source = shell res0 minus proxy faces + the chunk p3ds (cabin already excluded — it lives in the intact interior proxy). Render-review the baked LOD before deploy (blender-visual-review). (d) A far-LOD baked WITHOUT the shell's make-consistent/orient pass ships with its WINDING globally INVERTED → the body reads TRANSPARENT (see-through) at mid/far distance — a DISTINCT failure from (b)'s BLACK. (added 2026-07-09, SUB_BRZ s29 measured+fixed) Transparent = winding/cull wrong (anti-cross faces point INWARD → culled from outside); black = stored normals point inward (shading). Diagnose OFFLINE by census: fraction of far-LOD faces withcross·(centroid−center)>0vs the LOD0 control that renders fine — far-LOD ~98% cross-outward while LOD0 ~17% ⇒ winding inverted. FIX: if stored normals ALREADY point outward (storedN-outwardhigh, i.e. it reads transparent not black), a GLOBAL vertex-order flip (face.vertices.reverse()on every face, do NOT touch normals — they ride the vertices) suffices — census 98%→2%, matching LOD0. Reach for make-consistent+orient (rip_winding_core.repair_winding_majority) ONLY when the far-LOD is topologically MIXED (low interior-edge consistency), not merely globally inverted (G5 simple-first). Root cause on SUB_BRZ: the in-line s26 ladder assembler took the dissolve winding VERBATIM + raw +cross normals and skipped make-consistent (which the shell'srip_winding_coreruns) → res=2 shipped see-through,outward=38.9%was printed and ignored, no offline gate. GATE any baked far-LOD by census-vs-LOD0 BEFORE deploy — Blender cannot judge DayZ winding (right- vs left-handed cull). Instrument replicates consumer ORDER: culling runs BEFORE depth test (LL-485); group hits at t_min+epsilon and ask whether ANY face camera, and reread controls when fix changes geometry class that instrument resolves. - Steering wheel vs hands: measure rim-center delta AND plane tilt vs the control BEFORE moving
anything — the seat anim is the control's, so hands land on ITS wheel plane. (added 2026-07-07,
SUB_BRZ s25 measured) SUB_BRZ: rim-center delta vs crew anchor already in parity (1-1.6 cm) but rim
tilt 15.8° vs sedan 31.0° → ±r·sin(Δ) = ±2.1 cm at r=0.159 = exactly the user's "top hand 2 cm
behind, bottom hand 2 cm in front". Fix = rotate the wheel PIECE about its own rim center to the
control's tilt and re-author
drivewheel/drivewheel_axismemory points parallel to the new rim normal (SUB_BRZ's axis was 38° off its own rim; the sedan's is exactly parallel — a misaligned axis also wobbles the G5 wheel animation). Do NOT recline the crew proxy when the positional delta is in parity. Reusable tool:<vehicle-import>\s25_plan\measure_wheel_vs_crew.py(fits rim by outer-radial PCA; handles debinarized selections whose point-membership is lost via faces, LL-018). Same open symptom on MercedesAMGLF — apply there. - Hub level must use the radius of the wheel you actually MOUNT, not the rip's rim.
[HYPOTHESIS with numbers → day-1 coherence check; in-game pending] (added 2026-07-07, SUB_BRZ s25)
SUB_BRZ profile
WHEEL_R=0.3637(source-game rim) while the mounted wheel isCivSedanWheelradius 0.34 (DZ\vehicles\wheeled\config.cpp:4755-4772) → 2.4 cm hub-vs-radius mismatch, same order as the observed damper rest −4.3/−5.6 cm ("wheels slightly up"). Day-1 check:WHEEL_R == mounted-wheel radiusor document why not.
15a. The symptom of a wrong hub level is "it spawns sunk and corrects itself once a player
mounts" — and vanilla parks the hub ABOVE the tyre radius, not level with it (measured
2026-09-09, Arma2Quad ex-LFQuad2; offline, in-game pending). Invariant 15 says to match the
mounted wheel's radius but gives no control value and no way to recognise the defect from a
user report. Both, measured: the quad's four wheel_*_damper_land sat at cy=+0.3231 with a
mounted wheel of radius=0.350149, i.e. hub_y − radius = −0.0271; the control
civiliansedan_mlod.p3d sits at +0.3627 against CivSedanWheel radius=0.34
(DZ\vehicles\wheeled\config.cpp:4771), i.e. +0.0227. Sign is the whole diagnosis:
negative puts the wheels under the terrain at spawn and the settling suspension lifts the body,
which reads to a player as a sync bug; vanilla's positive margin spawns slightly clear and drops.
Confirm the config radius against the wheel MESH before touching the body — measure the
outer radius in the plane perpendicular to the spin axis (here 0.3501/0.3502, so the config was
right and the hub was wrong).
15c. A ported vehicle that "drifts on its own, even on flat ground" is a tyreRollResistance
defect, and the roll-away threshold is tan(slope) > tyreRollResistance (measured
2026-09-09, Arma2Quad; closed by telemetry). The quad's healthy wheels carried
0.0015 against 0.008-0.015 on every vanilla wheel
(DZ\vehicles\wheeled\config.cpp:876, 4775, 8985, 13403, 18077), and its ruined ones
0.03 against 0.2-0.75. That moves the threshold from 0.86 deg to 0.086 deg: no DayZ
terrain is that flat, so the vehicle creeps everywhere and the report reads as a physics
or sync bug. Three things this invariant exists to stop, all of which cost cycles here:
(a) mass is irrelevant - gravity and rolling resistance both scale with weight and
cancel, so "it is too heavy / too light" explains nothing; (b) vanilla vehicles do not
hold still through friction, they hold still because they are BRAKED - every vanilla
config carries Brake.driverless = 0.1 and Car.c:223 declares
SetBrakesActivateWithoutDriver, which actionpushcar.c:95 disables to push a car, so a
vanilla car with a driver seated and the engine off ALSO rolls downhill; (c) the
ground is never flat - surface_query returns the normal, and spots that look level
measured 0.72-4.8 deg. Day-1 check on any port: diff the whole wheel class against the
nearest vanilla wheel, not just radius. Discriminating rolling from sliding is one
line of telemetry: rolling satisfies omega == v / r (here 0.051 measured against 0.048
predicted, 6 %); a mismatch means slip or a decoupled animation.
Fix the ORIGIN, not the hubs. Translating the whole model leaves every body-to-hub relation
intact, so the settled pose is unchanged and only the spawn pose moves; raising the hubs alone
changes ride height by the same amount and breaks a pose the user already accepted.
And measure autocenter instead of fearing it: the warning that the engine re-centres a LOD
lacking autocenter=0 did NOT apply here — 10 of 12 LODs lacked it, with bbox centres 0.62–1.63 m
off origin, and the model renders aligned in-game, so a global translation survives. If it were
re-centring, those LODs would already be scattered by up to a metre relative to Geometry.
15b. Wheel-mesh facing needs an asymmetric witness, not a symmetric bbox (SP-254,
added 2026-08-31). Split wheel points by the sign of the mesh's shortest axis
and compare minimum radius in the perpendicular plane. The closed exterior face
reaches the hub (rmin≈0); the open interior begins near the rim. A negated bbox
range passes before and after a 180° turn and is therefore tautological. For
the measured x-axle wheel, correct a reversed side with the 180° Y rotation
(x,y,z)→(-x,y,-z) and rotate points and stored normals together. Calibrate the axis-to-side mapping on an independently
known-good mounted wheel, never on the candidate, then repeat the measurement on
the wheel blob extracted from the deployed PBO.
15d. Two hidden selections cannot own the same face: a hiddenSelection that overlaps
another one never gets a material slot, and SetObjectMaterial(index, ...) on it is a
SILENT no-op (measured 2026-09-10, Arma2Quad; closed in game). The quad's third bulb
(light_dashboard, hiddenSelections[8], which is SELECTION_ID_DASHBOARD_LIGHT at
scripts/4_world/entities/vehicles/carscript.c:301) never lit, while light_1_1 and
light_2_1 lit from the SAME rvmat pair through the SAME call
(carscript.c:2487). The only difference lived in the model: the bulb's 12 faces were
also claimed by camo1, which is hiddenSelections[9]. camo1 won the faces, so index 8
addressed nothing.
The diagnostic is a face-ownership census INSIDE the same file, listing for every
sections[] entry which OTHER selections claim its faces. Every light that worked came
back exclusive; the dead one was the only shared one. That is a relative measurement
through one reader, so it holds regardless of how your p3d library reports conventions.
Overlap with an ANIMATION selection is fine and must be preserved - here the same 12
faces are also in drivewheel, the handlebar bone, because the bulb has to turn with the
bars. Only hiddenSelections compete for a material slot.
Being listed in model.cfg sections[] is necessary but NOT sufficient:
light_dashboard was in sections[] the whole time.
Fix by removing the faces from the other hiddenSelection, and drop a point from it only
when no REMAINING face of that selection still uses it, so shared border vertices survive.
Three hypotheses were checked and refuted BEFORE this one, each worth a build+test cycle:
(a) "the faces need their own material" - given to 48 faces across LODs 0-4, still dead;
(b) "the selection is empty in the ViewPilot LOD" - every light selection is empty there,
including the ones that work; (c) "the engine renumbers by real sections so index 8 is
lost" - malformed, because hiddenSelections[] and sections[] agree on indices 0-8 and
only diverge at 9. Confirmation is cheap and does not need the light to work: with the
lights OFF the bulb switched from the body material to the lights material, which alone
proves index 8 now reaches those faces.
Corollary that removes a suspect: UpdateLightsServer is NOT server-only - carscript.c:2095-2101
guards only the CLIENT half with #ifndef SERVER, so SetObjectMaterial runs on the client
too and replication is never the explanation for a light material that fails to apply.
15e. Through dayz-mcp, a client-side get-in is not enough for any action the SERVER
validates (measured 2026-09-10). vehicle_get_in_client establishes the client
ownership that engine_set, vehicle_control and vehicle_trace need, but
ActionSwitchLights checks car.CrewMemberIndex(player) == VEHICLESEAT_DRIVER in
ActionCondition on the server
(scripts/4_world/classes/useractionscomponent/actions/interact/vehicles/actionswitchlights.c),
so it was rejected silently: action_use kept answering started: 1 and nothing lit, three
times. Calling vehicle_enter (server-side seat) as well made the very next identical
action_use work. Rule: for a server-validated action use BOTH verbs, and never read
started: 1 as evidence - verify the effect. Two related bounds cost a round trip each:
engine_set takes start/stop, not on/off, and vehicle_control.hold_ttl_s is
capped low (900 and 120 rejected, 30 accepted), so a vehicle parked on a slope starts
rolling again the moment the hold expires.
Also: camera_set refuses with camera_unavailable_vehicle while
GetCommand_Vehicle() is non-null, and a moving vehicle cannot be exited, so a scene that
rolls away traps the camera. Set the static camera BEFORE getting in - it survives the
get-in and gives the external view of a seated rider.
And it can be worse than a refusal: camera_set can leave the client RENDER frozen while
simulation, audio and input keep running (measured again 2026-09-10, this time on a user's
live session). The player walks, hears their own footsteps, and their server position
advances, but the picture never changes. The signal is in capture_screenshot:
distinct_frames: 1 with max_adjacent_delta: 0 over several frames, while
query_player_state shows the position moving. There is NO recovery short of
dayz_test_stop + a full dayz_test_run: restore_gameplay has no path to green in that
state (it always answers restore_unverified, because camera_get returns
camera_unavailable_no_scripted_camera - which IS the released state - and its "Retry"
advice cannot ever succeed), and dayz_test_run(mode="client") refuses with
client_already_polling, because polling is taken as proof of health and it is not. Do NOT
try to force a repaint by moving the window host-side with SetWindowPos: it does not restore
the render, and under a 150% display scale it leaves the window oversized and black.
Practical rule: use a scripted camera only when nothing else will do, and before handing the
game back to a human, confirm the render is alive with distinct_frames.
Two more traps from the same session: deleting a vehicle a player was put into with
vehicle_enter, without taking them out first, desynchronises client and server - the client
draws them seated while the server has them on foot with no vehicle within 40 m, and holding
the get-out key does nothing because the prompt is client-side and the action is validated
server-side. And once a player is bound to a vehicle, vehicle_get_in_client on ANOTHER one
answers not_seated; neither vehicle_release nor player_teleport nor
ActionGetOutTransport frees them. Only deleting the vehicle does. That makes "measure ours,
then measure a vanilla control in the same session" impossible without a relaunch.
15f. Raising maxSteeringAngle without scaling increaseSpeed[] makes the vehicle turn LESS
in practice, not more (measured 2026-09-10, Arma2Quad; caught by the user, not by me).
increaseSpeed[] and decreaseSpeed[] inside class Steering are {km/h, deg/s} pairs: the
RATE at which the steering ramps toward the requested lock. Time to full lock is
angle / rate, so the ramp only makes sense relative to the angle it was written for.
Arma2Quad carried {0,40,30,20,100,5} - byte-identical to vanilla OffroadHatchback
(DZ/vehicles/wheeled/config.cpp:636), whose maxSteeringAngle is 30. Taking ours from
50 to 60 while leaving 20 deg/s at 30 km/h moved time-to-lock from 2.5 s to 3.0 s, against
the donor car's 1.5 s. In a real corner nobody holds the key that long, so the ceiling went up
and the usable steering went DOWN. The user's report was exactly right and worth quoting as
the symptom: "sigue sin girar tanto como esperaria y ya no creo que sea por el radio de giro
de las ruedas" - it still does not turn as much as expected, and it is no longer the wheels'
angle. Rule: whenever you copy a vanilla Steering block, the ramp belongs to the DONOR's
lock angle; rescale it with the angle, and state the resulting time-to-lock in the commit so
the next reader can check it in one division.
Before blaming the steering at all, check the MASS - and it is not in config.cpp. In DayZ
the vehicle mass lives in the Geometry LOD of the p3d (resolution 1e13) as a PER-POINT
table: py3d exposes it as Point.mass on every point of that LOD, and lod.mass is only
their SUM. Read the table before saying anything about how the mass is distributed. This
invariant first shipped (03ba883) claiming Arma2Quad carried "1500 kg on one point"; the
per-point read refuted it in one line - all 214 points carried mass (1.2-46.2 kg), with the
centre of mass centred laterally (X -0.0001) and low (23 % of the hull height). The
distribution was sound; only the TOTAL was wrong: 1500 kg against 250-320 kg for a real ATV,
four to five times the yaw inertia the tyres must overcome. Changing it is coordinated work,
not a tweak - see 15g. What does not change either way is the roll-away threshold: mass
cancels there (15c).
15g. Changing a vehicle's mass: scale every Point.mass by one factor, and scale stiffness,
damping and compression by the SAME factor - never travelMaxUp/Down (measured
2026-09-10, Arma2Quad 1500 -> 750 kg). A uniform factor leaves the centre of mass and the
front/rear split exactly where they were, so the only thing that changes is inertia. Ride
height comes out of u = -travelMaxDown + F/k: holding k/F holds the stance, so
stiffness scales with the mass. damping and compression scale the same way because the
damping ratio is c / (2*sqrt(k*m)), and halving both k and m halves sqrt(k*m). Travel is
geometry and stays. Result: front u -0.00005 m before and after, rear -0.00470 vs -0.00469,
and in game a freshly prepared quad settled at y = 139.97918701171875 at BOTH masses, bit
for bit. Gate the mass INSIDE the PBO, not in the source: pull the packed .p3d out and sum
Point.mass with py3d. Keep .p3d backups out of the addon folder - AddonBuilder packs them.
Traps surfaced by halving the mass. (a) The user reported "gira menos que antes, sin mas" -
it turns less, with no slide and no bounce, and the lock angle had not changed. Suspected
cause, NOT confirmed: the same corner is now taken faster, where increaseSpeed[] is lower.
Flattening the ramp above the speed the driver had already approved (35 -> 60 deg/s at
60 km/h) drew "aun mejorable", not a clean yes. (b) Do NOT read centeringSpeed >
increaseSpeed at speed as the defect: in DZ/vehicles/wheeled/config.cpp centering is
higher at 60 km/h in 8 of the 10 Steering blocks (up to 6.5x, :14054-14056) and equal in
the other 2 (:741-743, :18466-18468) - it is the vanilla norm. (c) Check
Clutch.maxTorqueTransfer against the torqueCurve peak before adding torque: Arma2Quad had
140 against a 320 peak, below every vanilla car (165 at :14094 to 720 at :779). On
DRIVE_FWD, a first-order Coulomb estimate (not measured in game) says first gear was already
asking 6307 N of the 4749 N of front grip at 750 kg, so torque past that becomes wheelspin on
the steering axle, which works against turning.
15h. To change the fuel a vehicle spawns with, override EEOnCECreate, not EEInit
(source-verified; a script spawn does NOT reach it - measured, see below). Vanilla
CarScript.EEOnCECreate fills a random 0-35 % of GetFluidCapacity(CarFluid.FUEL)
(scripts/4_world/entities/vehicles/carscript.c:2967-2974). It runs when the entity is
created as new by CE/debug (scripts/3_game/entities/entityai.c:1385-1388), while a load
from storage goes through AfterStoreLoad (:1390-1393) - so a fill there cannot turn into
free fuel on every restart, which the same code in EEInit would. Fill ADDS
(scripts/3_game/vehicles/car.c:375-376), so top up rather than pour the whole capacity into
a tank that may not be empty:
Fill(CarFluid.FUEL, GetFluidCapacity(CarFluid.FUEL) * (1.0 - GetFluidFraction(CarFluid.FUEL))).
OnDebugSpawn is a separate path and fills on its own. Measured 2026-09-11 with a vanilla
Hatchback_02 as the control, which must land in 0-35 % if the path runs: neither a plain
script spawn (ECE_PLACE_ON_SURFACE) nor the debug spawner's own flags
(ECE_PLACE_ON_SURFACE|ECE_INITAI|ECE_EQUIP_ATTACHMENTS, the ones
4_World/plugins/pluginbase/plugindeveloper.c:405 uses) fire EEOnCECreate: both controls
read fuel_fraction 0, and so did the modded vehicle. Only a real Central Economy spawn
exercises the override, so observing it needs a types.xml entry plus an event. Without a
vanilla control in the same run, that zero reads as "my override is broken".
Drive-ready TEST KIT: fill ALL fluids + attach the radiator, not just FUEL (RECURRING: LFQuad + SUB_BRZ s28). An admin/harness kit that only
Fill(CarFluid.FUEL,...)leaves OIL/COOLANT/BRAKE empty → the oil gauge goes RED and the engine BREAKS while driving (looks like a physics/drivetrain bug, it is NOT). A drive-ready kit mustFillFUEL + OIL + COOLANT + BRAKE andCreateAttachment("CarRadiator")(coolant needs the radiator). VanillaFillUpCarFluids()does NOT do all of it, and mirroringOnDebugSpawnis not enough either. It fills exactly FUEL, COOLANT and OIL — BRAKE is not among them (scripts/4_world/entities/vehicles/carscript.c:3191-3196, read 2026-08-20; a single definition in the tree, every other hit is a call, so no subclass overrides it). The radiator does not come from it either: that isSpawnUniversalParts()(carscript.c:3141-3145), gated onIsVitalRadiator(), viaCreateInInventory— which searchesCARGO | ATTACHMENTand may land in either (inventory.c:876-893), so a debug spawn does not guarantee an ATTACHED radiator. Fill BRAKE and attach the radiator explicitly; a hand-rolledKitManualin the missioninit.chas to do both.CarFluidenum = FUEL/OIL/BRAKE/COOLANT (scripts/3_game/vehicles/car.c:18);Fill/GetFluidCapacityatcar.c:376/359.How the gap was found (SUB_WRXSTI, 2026-08-19). This invariant used to name
FillUpCarFluids()as satisfying it. A mod car whoseOnDebugSpawncalls it and stops — which is the obvious, vanilla-faithful thing to write, and what both SUB_BRZ and SUB_WRXSTI shipped — spawns its test car with the brake reservoir EMPTY. That is the exact state this invariant says breaks the engine while driving, reached by following vanilla. Add the fourth fill explicitly:Fill(CarFluid.BRAKE, GetFluidCapacity(CarFluid.BRAKE))— capacity from the declared value, not a copied200.0, sincebrakeFluidCapacityis1unless the car redeclares it (DZ\vehicles\wheeled\config.cpp:244, the only declaration in that file, verified 2026-08-20). Corollary worth more than the fix: an invariant that names the right requirement AND names a vanilla call as satisfying it will be read as "call that and you are done". Verify the call, not the claim.- Raise tyre grip WITHOUT touching the wheel binding: a
CarWheelsubclass of the vanilla wheel. To add grip to a car that slides, do NOT rewire slots/proxies (risks re-opening the freeze/wheel-bind). Declareclass <Mod>_Wheel : CivSedanWheel { scope=2; tyreGrip=0.98; };— it INHERITS the vanillainventorySlot[](DZ\vehicles\wheeled\config.cpp:4761lists CivSedanWheel_1_1.._2_2 + Spare) so it drops into the sameCivSedanWheel_*slots and reuses the sedanwheel proxy; only tyreGrip changes. ThenOnDebugSpawn/kit create<Mod>_Wheel. Grip is DECOUPLED from the visual wheel model (custom source-game rim = separate visual job). SUB_BRZ s28. - Diagnose "the car slides" BY AXIS — longitudinal (accelerating) vs lateral (cornering) need different fixes. Sliding when you FLOOR it (power-oversteer, RWD) → tyreGrip + throttle/torque down. Sliding when you TURN (body roll unloads the inside tyre) → tyreGrip + LOW CoM + downforce + roll stiffness. A low CoM helps the CORNERING slide (less lateral weight transfer) but NOT the accelerating slide — in RWD a very low CoM even transfers LESS weight rearward under throttle (slightly less traction). Never sell "lower the CoM" as the fix for wheelspin. SUB_BRZ s28: accel-slide dropped with grip 0.95→0.98 + throttle 0.8→0.72; corner-slide needed CoM/downforce.
- Transplanting a donor mod's drivetrain: match (gear x central x final) / wheel-radius, never
the ratios verbatim. (added 2026-07-10, SUB_BRZ s31) Wheel force per engine-Nm = total_reduction /
wheel_radius and per-gear road speed = rpm x radius / reduction, so a donor running a different
wheel radius shifts EVERY gear by r_donor/r_yours. Compare radii FIRST; the CentralDifferential
ratio is the compensation lever. Case: SUB_BRZ (CivSedanWheel 0.34) adopting the Gelenvagen
drivetrain (0.48 wheels, central 1.4) keeps central 1.0 — 0.34/0.48 ~= 1/1.4 cancels the donor's
central, reproducing its per-gear speeds AND per-Nm force <1% off; copying central 1.4 verbatim
would run 29% shorter than the donor feel. The failure class burned a full session first (s30: an
offroad central 1.5 compounded ratios authored for central 1.0 → gears ~50% short, 6th
unreachable). Corollaries: (a) donor force on half the mass = ~2x the acceleration — scale
expectations by mass before judging "too much/too little power"; (b) engine
frictionTorqueis calibrated to the donor's mass/curve — engine-braking decel scales as fT x reduction / (radius x mass), and when the radius/reduction terms cancel (as above) the mass-equivalent value is simply fT x m_yours/m_donor; adopt the donor value for its cruise character but pre-compute that fallback before the in-game cycle.
Cross-vehicle durable record (which project won each, links to the three): vault note
AI/20_Knowledge/dayz-vehicles-crossproject.md.
- Config inherits a vanilla car but your SCRIPT class does not extend that car's script = SWEEP every parent script override (SP-059). If the config says
class X: OffroadHatchbackwhile the script isclass X extends MyBase(withMyBase extends CarScript), the config->script binding does NOT drag in the parent car's script overrides - those live on the vanillainheritedcars\<parent>.c, which your class never inherits. Every method with a hostile default in the base that ALL vanilla cars override then bites:CrewCanGetThrough(false inTransport,transport.c:493-> get-in impossible; see #1),IsVitalGlowPlug(true inCarScript-> engine won't start; see #8),GetAnimInstance/GetSeatAnimationType(Error()inTransport,transport.c:465,475),Get3rdPersonCameraType(Error(),transport.c:483). Procedure: diff method-by-method against the parent config's.c(4_world\entities\vehicles\inheritedcars\<parent>.c) and replicate EVERY runtime-contract override, not just the two pose ones - this is DZ-R7 (sweep the invariant to all call-sites) in config form. Origin: LFHeli R21-008 -LFHeli_Placeholder: OffroadHatchback(config) +extends LFHeli_Base(script) had no get-in and no engine start after passing two reviews; the first fix restored only the two pose methods.
20b. A working control's skeleton is baked into its ODOL; inherited config must be
audited by EFFECTIVE value (SP-145, added 2026-08-31). Shipped controls often
have no loose model.cfg. Read the baked skeleton and selection names through
an external ODOL→MLOD converter (not distributed with this pack), and validate
the reader first on a model whose model.cfg is available: name, isDiscrete,
bone count and every bone/parent pair must match. Binarized bone names are
lower-case. A uniform zero from both control and candidate is a tool-failure
signal; confirm known bytes directly before drawing a conclusion.
For a vehicle that inherits from the working vanilla control, an undeclared
property has the parent's effective value and cannot be a differential by
itself. Keep only differences where both independent controls agree and the
candidate differs, then inspect: candidate-only declarations; model-bound
skeleton/animation/selection data; and values both controls override but the
candidate neither declares nor inherits. Finally resolve every inherited model
name (animDamper, wheelHub, and peers) against the candidate .p3d; the
reference is inherited even when the named bone or selection is absent.
- REDIRECT CAMBIO-1 (familia B):
../rip-vehicle-import/cookbooks/family-b/attach-invisible.md. - An attached item with its OWN radial actions (CarDoor open/close, hood, trunk) needs a raycast-visible ViewGeometry — point flags 0x0000003F — or the action NEVER appears (SUB_BRZ s38). The action chain resolves the TARGET by raycast:
ActionCarDoorsOutside.ActionConditioncaststarget.GetObject()to CarDoor and reads the selections of the hit VG COMPONENT of the ITEM (actioncardoorsoutside.c:34-46); a VG whose points carry flags 0x0 is not hit byRaycastRV(ObjIntersectView)— the same mechanism as the seat-cube blocker (preflight #4, in-game verified SUB_BRZ s9 + MercedesAMGLF s12) — so the item under the cursor never resolves and the radial is silently filtered, with config, script overrides, slots, bones and anim sources all CORRECT. Contract for the item's VG: (a) componentNN dual-tagged with a selection named EXACTLY what the vehicle'sGetAnimSourceFromSelectionexpects (e.g.doors_driver); (b) every VG point flags 0x0000003F; (c) inward winding (copy a fixed seat cube as control). Symptom signature: attachment renders/attaches/damages fine,GetCarDoorsStateworks, but no open/close radial (and hence no get-in-through-door). Diagnose offline in seconds: census the item's VG point flags vs a working control BEFORE touching config or scripts. Origin: SUB_BRZ s38 D4e; the door fix's own in-game gate CLOSED 2026-08-24 (the radial action DOES appear on the attached panel), but the raycast mechanism is the twice-verified #4 one. componentNNdual-tag is the confirmed fix for simultaneous seat/wheel selection failures; ascending LOD order is match-vanilla practice, not a proven cause (LFHeli OH-1 v2, 2026-07-17). Sorted-without-dual-tag still failed; sorted-plus-dual-tag spawned; dual-tag-without-sort was never isolated. Therefore a py3d/hand-assembled model withseat_* not found/wheel ... no proper selectionmust be checked for collision-selection dual-tag first.model.lods.sort(key=resolution)may remain as deterministic authoring hygiene, but no gate may report that sorting fixed the defect.binarizeaccepts either order silently.
Historial del texto superado:
history/cambio-1-superseded-family-b-rules.md§“Invariante 23”.
binarizeis the three-state offline load oracle;RESOLVED_LIMIT = 65535is a false friend (SP-122, LFHeli HH-60G, 2026-07-29).
Historial del texto superado:
history/cambio-1-superseded-family-b-rules.md§“Invariante 24”.
Measured evidence for this invariant — the three verdict states, the real ceiling in triple
units, headroom accounting and what does NOT discriminate — is in
references/binarize-vertex-budget.md.
- GTA V rip intake (dlc.rpf) — mod RPFs are usually UNENCRYPTED and fully parseable offline; the vehicle skeleton maps 1:1 to DayZ needs (LFHeli HH-60G, 2026-07-18). Check the encryption dword at offset 12 of the RPF7 header FIRST:
0x4E45504F('OPEN', standard for OpenIV-built mod RPFs) = no GTA V install, no NG/AES keys needed. Verified on-disk layout: 16-byte header (7FPRmagic, entryCount, namesLength, encryption); 16-byte entries discriminated by dword2 (0x7FFFFF00= directory, high bit set = resource, else binary); file offsets in 512-byte sectors; binary entry with fileSize==0 = stored verbatim (nested .rpf — recurse in place). A resource on disk = 16-byte RSC7 header IN THE CLEAR (magicRSC7, version — 162 yft / 13 ytd —, sysFlags, gfxFlags) + raw-deflate payload from +16 (zlib wbits=-15); scene-standard standalone.yft/.ytdfiles are exactly that byte range copied verbatim, so extraction is a copy, not a re-encode. Skeleton conventions worth knowing before Blender (string-scan of the decompressed payload suffices — no FRAG parsing): rotors ship THREE statesrotor_main/_slow/_fast(+rotor_rearsame) = direct map to the DayZ static+blur rotor pattern; doorsdoor_[dp]side_[fr]+handle_*; glass as own bones (windscreen,window_*— glass census for free);seat_[dp]side_*,wheel_lf/rf/lr,gear_*incl.gear_door_*; guns/turrets are separate bones (turret_*,weapon_*) = clean v1 exclusion. Toolchain [IN-VIVO VERIFIED 2026-07-18]: Sollumz 2.8.3 imports binary.yftdirectly (PyMateria/szio, Windows-only) on Blender 5.1 (4.0-5.1 supported), while CodeWalker demands a GTA V game folder on first run — so for OPEN rips the primary chain is own-extractor → Sollumz binary import, CodeWalker only as fallback. Two verified gotchas: (a) PyMateria's native.pydfails to load from a long path ("DLL load failed ... filename too long", MAX_PATH) — install Sollumz into a SHORT isolated Blender profile viaBLENDER_USER_RESOURCES=C:\tmp\blp(also keeps the user's running Blender untouched); headless install =bpy.ops.extensions.package_install_files(repo="user_default", enable_on_install=True)+<addon>.dependencies.install_dependencies(online_access_override=True, optional_dependencies_to_install={"pymateria"}). (b) The.yftdoes NOT carry its textures (they live in sibling.ytd), and Sollumz's high-levelgta5.try_load_assetreturns None for a.ytd, and its.yftimport wires 47 named-but-empty image nodes — the FIX is the RAW PyMateria binding:pmg8 = szio.gta5.native.provider_gen8.pmg8;res = pmg8.TextureDictionary.import_rsc(Path(ytd));td = res.result;td.texturesis a Map(name→Texture);tex.export_dds(path)writes the decoded DDS (PyMateria does all the RSC7+format). ~generic base-game textures (vehicle_generic_*) live in GTA'svehshare.ytd(not in the mod) and stay missing = pink in Blender, but IRRELEVANT for DayZ (glass gets a vanilla.rvmat; the rest are secondary spec/detail overlays). Origin: LFHeli HH-60G intake (rpf_extract.py/ytd_extract.py/bl_install.pyinLFHeli_dev\model_src\HH60G_intake\work\; textured artist package verified by render). - A rip's BONE NAMES are not its MESH PIECES, and an exclusion REGEX silently classifies content nobody has looked at (LFHeli HH-60G, 2026-07-28). Two failures of the same import, both invisible offline and both only surfaced by a full in-game cycle. (a) Regex exclusion. The day-1 census marked
exclude_v1with^(mod_[a-z0-9]+|turret_|weapon_|siren\d|extra_\d|hbgrip_)to drop "GTA cruft". That swept inmod_n(31.598 tris = the whole nose kit: refuelling probe, rescue hoist, radome, nose pods) andmod_s(52.550 tris = the entire interior, seats included). The exporter inherited the classification and the vehicle shipped with no nose and a hollow cabin; 94.084 tris of legitimate content, 38% of the source, lost for ten days without one warning. Rule: the exclusion list is LITERAL NAMES, one justifying line each — never a regex. A regex classifies pieces nobody has looked at yet; a literal list forces you to look. (b) Bone names != mesh pieces. The string-scan of the skeleton (item 25 above) listshandle_*next todoor_*, so the routing table citedhandle_dside_f/pside_f/dside_r/pside_r. Measured against the actual LOD levels: those four names exist in no level of the.yftand in no mesh of the imported.blend— the skeleton carries bones with no geometry behind them. Item 25's skeleton-convention list is a hypothesis about names, not a piece inventory; intersect it with the per-level mesh list before routing anything. Corollary that decides the fix: "the export LOST a piece" and "the export CITED a piece that never existed" look identical from a silentif obj_by_name(n)skip and lead to opposite repairs (go find it vs delete the row). Gates that would have caught both on day 1, cheap and offline: SOURCE = the piece set of the richest LOD level, measured (herehh60g.yft/High= 49, set-identical tohh60g_hi.yft/VeryHigh); every source piece appears exactly once in a routing group or in the literal exclusion list, partition verified by set equality, not by totals; every routed name is in SOURCE; and a missing artefact is an error, never a[skip]. Origin: LFHeli HH-60G, plan v15 Fase 1.1/1.2; measurement inLFHeli_dev/plans/2026-07-28-hh60g-v15-censo-y-tabla-normativa.md. - A model that passes every offline gate and still refuses to spawn is almost always over the RESOLVED-VERTEX ceiling — and
binarizetells you offline, in 90 seconds (LFHeli HH-60G, 2026-07-29).PHYSICS (E): Won't simulate, it has no geometryis emitted when the engine aborts while loading the MLOD, before it ever builds physics — so a perfect Geometry LOD does not exonerate the model, and chasing it as a collision problem costs days. Do this first, not last: drop the.p3ds into a dir underP:\with amodel.cfgdeclaring every basename, runbinarize.exe -always <src> <out>, and read the verdict —Too many verticesnames the culprit by filename. Validated 25/25 against in-game verdicts on 28 variants (zero false greens, zero false reds), which turns model bisection from ~6 min per variant into ~90 s per batch. That validation ran without-addon. [DESIGN] Give the call a dedicated-addonfolder all the same, since without one a later import's call, its source outsideP:\, stalled before its first model; with one onlyPASShas been run so far, noCAPACITY_FAIL, and a source underP:\was not timed (references/binarize-vertex-budget.mditem 2). Three rules that make it a real gate: (a) the verdict is three-valued —PASSneeds a new non-empty ODOL (a residual one is a false green),CAPACITY_FAILneeds theToo many verticesline attributed to that MLOD, and anything else isOTHER_FAILwhich blocks and does not authorize touching geometry; (b) the verdict is reproducible but the ODOL bytes are not (same input, 1.725.025 and 1.726.689 b in two clean runs) — never gate on the ODOL SHA; (c) the ceiling in(point, normal, uv)units is not 65535 and not portable — measured 46.133 on the HH-60G across four bases, because the engine's cap is on POST-SPLIT vertices and your counter under-counts by a model-dependent factor (1,4205 here, 1,46 on the OH-1). An assembler withRESOLVED_LIMIT = 65535hardcoded closed its own gate in green for weeks on a model the engine refuses. Re-measure per model, and know your headroom — the deployed HH-60G sat 114 triples under the cliff, one normal tweak from death. Full detail, including why reversing corner order is free while minting a normal is not: SP-122 at the bottom of this skill. Tool:<vehicle-import>\scripts\p3d_vertex_gate.py(its HH-60G copy calls binarize without-addon:references/binarize-vertex-budget.mditem 2). SP-216 rider: py3d count gates (resolved<65535, normal pool, digest, identical Geometry) do not authorize spawn. The first oracle forWon't simulate, it has no geometryisbinarize(invariant 27):CAPACITY_FAIL→ do not go in-game;OTHER_FAIL→ do not touch geometry;PASSand the game still says no-geometry → then yes, serialization / another axis, and in-game spawn is the judge. The V8 of SP-216 is the negative capacity control (CTL_CAPACITY), not a refutation ofbinarize. - A 4-seat car numbers ONE
crew_cargomodel 1/2/3 — the index is the instance suffix, and two rear seats both at index 1 collide (added 2026-08-16, SUB_WRXSTI T8.2). There is nocrew_cargo1.p3d:P:\DZ\vehicles\wheeled\proxies\ships exactlycrew_cargo.p3d+crew_driver.p3d, and the seat identity comes from the INDEX. The mapping the consumer actually applies isbone = "crewcodriver" if index == 1 else "crewcargo%d" % (index - 1), socrew_cargo.001→crewcodriver,.002→crewcargo1,.003→crewcargo2(<vehicle-import>\tools\verify_forza_car.py:983-986; corroborated by the sealed AC02 control inventory,profiles\contracts\brz_ac02_v1.json:347-364, and by CivilianSedan's own Crew Cargo1/Cargo2 block atP:\DZ\vehicles\wheeled\config.cpp:5206-5218). Consequences: the front pair legitimately BOTH sit at index 1 because their PATHS differ (crew_driver.001andcrew_cargo.001) — that is not the bug; adding two REAR seats at index 1 on the sharedcrew_cargopath is, and it fails as a duplicate-selectionValueErrorat authoring time if your builder checks, or as silently missing rear get-in if it does not. Extending a 2-seat rig: continue thecrew_cargoindex, never restart it, and pin the mapping with a test whose RED case is exactly the duplicate pair. - A rig threshold on a MEASURED axis must have its source declared for EVERY member of a symmetric pair — declaring it on one side leaves the other on raw measurement, and the build refuses months later (added 2026-08-16, SUB_WRXSTI T8.3 blocker). The detachable-door rig requires
dir_y > 0.999(rip_p3_structural.py), and a rip's placement tilt lands a door hinge just under it. The WRX profile fixed that for ONE door by declaringhinge.axis_source.declared = assembly.alignment_build.doorRF, an axis projected to vertical with its XZ midpoint kept — and the other door, declaring noaxis_source, kept riding its raw PCA axis. Measured when the whole structural build refused: codriver|dir_y| = 1.0000, driver|dir_y| = 0.998575= 3.059°, i.e. one door 3 degrees from a threshold the other met exactly. This isDZ-R7(trace the invariant to all call-sites) applied to profile DATA rather than code, and it is invisible until the gate fires, because both doors look declared and only one is. Two riders paid the same day: (a) project each side keeping its own XZ midpoint, never mirror the other door's — the two sides of a rip are not symmetric, and mirroring here would have moved the hinge 3.7 cm (driver x +0.849278 vs codriver x −0.811827); (b) relaxing the threshold to admit the one bad case is the tempting fix and the wrong one — the threshold exists so a door does not open tilted, and lowering it for one car lowers it for all. - A seat box is a GET-IN VOLUME, not the cushion — vanilla leads it ~0.15 m toward the nose of the crew position, and collapsing the two onto one point makes rear seats unenterable (added 2026-08-16, SUB_WRXSTI). The engine resolves the get-in cursor by raycast against the ViewGeometry, so the seat box has to sit inside the DOOR APERTURE; the body that occupies the seat sits further back, against the backrest. Vanilla keeps them apart deliberately — measured on the sealed control
civiliansedan_mlod.p3d: crew proxy z −0.173 vs seat box z −0.358 in front, +0.675 vs +0.531 in the rear, i.e. the box leads the crew proxy by 0.185 and 0.144. A rig that places both at the same declared point looks correct on a 2-seat car — there is open flank ahead of the front seat either way — and puts the box of a 4-seat car inside the C-pillar: the WRX measured 43% reach on BOTH rear seats (21/49 rays; the 28 blocked were exactly those with z ≥ 0.876, against a pillar occluder starting at +0.881) while the front pair read 100%. Three riders. (a) The pillar occluder is CORRECT — do not carve the body or refine the occlusion grid to make a seat reachable; the tempting diagnosis "residual door skin left in the shell" was refuted by measurement (the rip'sdoorhandlelr_acarries 1970carpaintfaces of the rear PANEL — the rip names the skin after the handle — and it was already owned by the rear door roles). (b) Move the BOX, never the crew proxy: the passenger must stay on the cushion. (c) Calibrate the offset against a sealed vanilla 4-seater and cross-check it by sweeping the box along z against the car's own occluder — on the WRX the vanilla-derived 0.740 landed inside the independently swept 100%-clear window [0.700, 0.800], and the sweep reproduced the measured 43% at the shipped z, which is what makes the simulation admissible. The matching detector is invariant 31. - Seat/crew checks written as a literal front pair go blind the day a car grows a rear row, and report
2/2 100%while doing it (added 2026-08-16, SUB_WRXSTI).verify_forza_car.pylisted("seat_driver", "seat_codriver")in the reach ray fan, the componentNN overlap targets and the ViewGeo presence check, plus a hardcoded/2denominator in two report strings. On a 2-seat car that IS the whole roster, so the blindness never shows; on a 4-seat car it printed green over two seats it never scanned, one of which was 43% reachable. Derive the roster from the crew bones (crewdriver→seat_driver,crewcargo1→seat_cargo1, …), not from the seat boxes present: deriving from the boxes is blind in the other direction — a car that LOSES a seat box simply has one fewer thing to check and stays green. Same derivation for thepos_*(_dir)memory points. Calibrate against the sealed vanilla 4-seater (it measures 4/4 at 100%), which is what turns a 43% into a defect rather than a strict gate. - Two halves of a car that a name-existence gate cannot see: the open-door action reads the DOOR PROXY, and vanilla parity is not a behaviour spec (added 2026-08-16, SUB_WRXSTI). (a)
ActionCarDoorsOutside.ActionConditiondoesCarDoor.Cast(target.GetObject())and thencarDoor.GetActionComponentNameList(...)— the selection names come from the attached door item's own.p3d, not from the shell (4_world\classes\useractionscomponent\actions\interact\vehicles\actioncardoorsoutside.c:30-58, re-read 2026-08-16). If the proxy does not carrydoors_<role>,GetAnimSourceFromSelectionreturns""for every one of its components and the open/close action never appears from outside — no error, no log. Measured placement on both the WRX and the shipped BRZ: the name lives in the proxy's Geometry and ViewGeometry LODs; FireGeo carries onlyComponent01/02,dmgzone_*andglass_<role>. Every gate in this project measured the SHELL; nobody had ever opened the six door proxies. When verifying "the doors open", the evidence is the proxy — the shell only proves the model references it and that the animation axis exists. (b) Three behaviours are correct vanilla ports and still wrong for a mod:OnDebugSpawndrops the parts loose in cargo (civiliansedan.c:408-430) so a freshly spawned car needs ten manual attachments before it moves;CarScript.EEOnCECreatefillsMath.RandomFloat(0.0, capacity*0.35)(carscript.c:2967-2974) so a CE-spawned car can arrive dry;CanDisplayCargoopens the cargo on the boot alone (civiliansedan.c:162-171). All three ship a car that loads, drives and looks right, and every structural gate passes them by construction because the criterion is "does it match vanilla". Declare behaviour PER CAR in the profile (vehicle_script_policy {cargo_access, debug_spawn, ce_spawn_fuel}) and measure the script against that declaration; derive the attachable roster from the config's owninventorySlot[]blocks so a renamed class reds by itself. Rider on the silent one:CrewCanGetThroughmust compare againstDOORS_CLOSED, because a torn-off door reportsDOORS_MISSING— writing the same intent as!= DOORS_OPENreads correctly, passes every structural check, and makes a doorless car unenterable. See LL-279. - A gate lane measuring only numbers is blind to proxy color and orientation, and the only instrument seeing them is an eye in front of an ASSEMBLED render (added 2026-08-16, SUB_WRXSTI). Existing names, preserved faces, matching hashes, and complete seat→door→slot matrices: all of that passes green on a monochrome car with reversed wheels. Measured: WRX arrived in PBO with 64% of its faces in ONE material and entire interior in another (import recorded
material_map: nulland sent 37 named parts — badges, mirrors, exhaust, jambs, skirts, underbody, arms — into the paint bucket), with four wheel proxies written with IDENTITY matrix where vanilla writes TWO mirrored ones, one per side, and with 176 mm vanilla sedan wheel instead of its own. All four defects were found by user within minutes upon first seeing assembled car; none was visible to any gate. Three riders. (a) Render must be ASSEMBLED and with materials: loose grey parts reveal neither wrong color nor inverted rim — avisual_sheetdrawing grey geometry per part already existed and caught nothing. (b) Human verdict is tied to sha256 of what was reviewed, or it rots: after a rebuild no one can tell whether THIS car or previous was approved, and gate must turn red from stale, not remain green. (c) Calibrate chirality with BRAND TEXT, not intuition: DayZ is left-handed and viewer engines are typically right-handed, so passing coordinates as-is is a reflection, and a mirrored car looks normal until a badge reads backwards. Honest trade-off: this DOES NOT replace in-game testing, it merely makes finding what is seen cheaper. - An absent key in a config class is NOT a value — it is a question about the parent (added
2026-08-21, from a refutation that did not survive). Reading
config.cppby grepping for a key and treating "no hit" as "the default" gets the answer backwards whenever the class extends another that sets it. Measured case:CivSedanDoors_BackLeftdeclares norotationFlagsat all — four lines of body (DZ\vehicles\wheeled\config.cpp:4923-4932) — and extendsCivSedanDoors_Driver, which sets8(:4798). A note published on 2026-08-19 read the absence as4and concluded the sedan REFUTES the left-8/right-4 rule; it does not, it is the rule's own prediction. The other five do declare4(CoDriver:4921, BackRight:4942, Hood:4957, Trunk:5034), which is exactly what made the sixth look like an outlier instead of an inheritance. Resolve the chain before quoting a value, and say which link you read it from. The same trap applies to every inherited config key, not justrotationFlags. - Missing DamageZone
memoryPoints[]/ Memorydmgzone_*points do NOT explain collision jerks or driving stutters (added 2026-08-30, LFQuad2).[MECHANISM VERIFIED]The script contact path never readsmemoryPoints[].CarScript.OnContactcaches the first PhysX impulse of the tick (carscript.c:1454-1478);CheckContactCacheappliesProcessDirectDamage(..., zoneName, "EnviroDmg", "0 0 0", dmg)(:1497,:1572).DamageSystem.GetDamageZoneMaponlyConfigGetTextArray(... componentNames)(damagesystem.c:63-68). ZeromemoryPointshits inscripts/**/*.c; the same grep finds them in vehicle configs. Vanilla OffroadHatchbackclass ChassishascomponentNames[]and nomemoryPoints[](DZ/vehicles/wheeled/config.cpp:1570-1579).GetMemoryPointPos("dmgZone_*")in the ctor (carscript.c:393-406) feedsGetEnginePointPosWS/GetFrontPointPosWS/ … (:496-522) which have zero callers. Geometry is the contact mesh; a working referent can carrydmgzone_*only in FireGeometry (+ Memory for config parity). Do not spend an in-game cycle adding Memorydmgzone_*as a jerk fix. DISTINCT from #9 (missingclass Engine/FuelTankstalls a started engine). NativeOnContact.zoneName/GetDamageZonePosremain C++-opaque — even if they used memory points, that would change which zone loses HP, not the impulse.
NEW CAR — DAY-1 (run BEFORE the first in-game cycle; retro 2026-07-03)
Retro of LFQuad→SUB_BRZ→MercedesAMGLF (~90-110 session-equivalents, ~180-220 in-game cycles across the three): the dominant cost was known-CLASS defects attacked one-per-rebuild AFTER the car was in-game. Front-load them on day 1, offline, before any PBO exists — promoted knowledge applied up front is nearly free (the SUB_BRZ spawn-blocker check ran before Mercedes Fase 3 and cost minutes; the un-promoted get-in cost "decenas de iteraciones" on three cars):
- Parity-first (METHOD #1): debinarize
civiliansedanONCE → structural gap checklist. Preflight #1-#9 items fall out of this diff instead of error-by-error in-game. - Measure, never assume — on the rip AND on a working control. Cite-then-verify extends to DATA PROVENANCE and empirical constants, not just API names (the BRZ winding saga ran ~15 sessions on an oracle nobody had verified; the 32768 "ceiling" and the Izz>=300 gate were both artifacts): nose=-Z markers sim vs visual (#11), EACH body proxy's anchor + content (#11 trap), axis memory-point directions, per-LOD point counts via py3d (Blender estimates read ~x1.24 optimistic), the rip's own authored-LOD inventory BEFORE building a decimation pipeline (decimation is user-gated).
- One command wraps the day-1 measurements:
python <vehicle-import>\tools\import_preflight.py --car <p3d> [--proxy-dir <dir>]— parse-health, sim/visual orientation, wheel-proxy yaw-180 MIRROR signature, body-proxy anchor+content report, steering-axis direction class, LOD point counts; prints the follow-up gate-ladder commands (rungs 1/2/4 stay in their dedicated tools). CALIBRATED 2026-07-04: FAILs x3 on the pre-fix Mercedes (B1/B2/B3), 0 FAIL on the vanilla sedan. Note from that calibration: anchor→hub PROXIMITY is NOT a discriminator (correct sedan = 0.20-0.22 m, mirrored Mercedes = 0.147 m — the broken car sat closer); the EXACT (-X,-Z) mirror signature is. - Acceptance spec from the CONSUMER (R8-extended): list the exact names/frames/values that
model.cfg + config.cpp + the engine will read and assert each exists in the
.p3d— producer-side self-consistency passed 58 asserts while the forward contract was broken (LL-025), and offline suites passed while the car could not spawn. - Headless harness with a vanilla control in the same run (sedan/Croco): "the control delta does
the diagnosis" (LFQuad bounce: wc=0000 vs control wc=1111 isolated the bug in one run).
The control delta is only valid if BOTH cars get the IDENTICAL kit flow — kit the mod car via
its own
OnDebugSpawn()(or an explicitly declared superset), never a hand-rolled partial kit. MercedesAMGLF s16 spent a user drive-test with the mod car on a hand-writtenKitManual(FUEL+COOLANT only → OIL=0/BRAKE=0) while the control sedan got full vanillaOnDebugSpawn(init.c:145-146, found 2026-07-07 AFTER the cycle was spent): non-equivalent kits silently poison the mod-vs-control discriminator and add a confounded state variable to every symptom. - Fluids / DamageZones / vitals config from the proven shape (preflight #8/#9) — copied, never invented.
- Reference-frame MARKER pass, before anything is built on top (SP-368, added 2026-08-15,
MercedesAMGLF G1). Two blind spots compound: a face with an empty
texturerenders WHITE, and white masks every frame/UV defect underneath it; and a mirrored or 180-rotated parametrization is invisible on any surface without legible text. Together they hid the same defect here from June to August, and it was CLOSED ONCE with the wrong diagnosis ("aliasing", measured 172-180 texels/m, 4 px/cycle) because no one had a surface that could show handedness. Inject the markers on the FIRST import and judge them in the engine - not the viewer, not the DCC: the engine defines the convention.- Texture marker: one image, asymmetric under every symmetry of the square - an
F, a labelled +U arrow, a labelled +V arrow, four distinctly coloured corners, and a grid for scale. Point EVERY visual face at it; this is the same single-field pass that repairs an emptyface.texture, so it costs nothing to build once. Redirect thehiddenSelectionsTexturesslots too, or the config stamping paints over the marker on the host and you read a false pass. Delivers in one look: mirror, rotation, per-island rotation, tiling and scale. - Geometry marker: a small tri-axis gizmo, three differently-SHAPED arms on +X/+Y/+Z, welded at
the origin. Catches axis permutation and a reflected transform with no textures involved. This is
the one that would have flagged a frozen
(x, y, -z)on day 0 - and note that compensating a reflection by flipping winding fixes lighting and culling but NOT texture handedness. - Caveat that cost a cycle: the texture marker is USELESS on heavily tiled surfaces. A seat net at u in [-14.9, 25.0] repeats ~40x and shows only fragments of the glyph, which reads as a defect and is not one. Measure the per-material UV range first and annotate those surfaces before looking, or you will chase your own marker.
- Texture marker: one image, asymmetric under every symmetry of the square - an
LEGACY DIAGNOSTIC GATE LADDER -> references/legacy-gate-ladder.md
Superseded diagnostic order, kept for vehicles already in flight; no rung fires automatically for a new family B asset. Moved out of the body 2026-08-15. The current findings that used to live under that heading now have their own sections, immediately below.
Calibration + scope (so a gate is trustworthy, not a false green)
- Draw the window from the defect, not the fix (LL-271). Derive the measurement window from where the defect is visible, the viewing angles and the user's referent, never from the area the repair touched. A red dismissed as a window artifact needs an independent control proving the instrument lies.
- Classify new faces before choosing a comparator (LL-272). Invented points/UVs -> independent calibrated reference (material median); geometry derived from one face (twin, displaced panel) -> that exact source face — stricter, here
0 of 7,205 deviate >5%; exact twin -> exact equality only. A failure inherited unchanged from the source does not prove the edit introduced it. A twin cannot fail a UV gate — it inherits UVs literally. - Half the model failing is structure, not defect (LL-276). If a detector rejects ~50% of entities, presume a structural property such as double-sided construction until refuted. Here
13,260vertices inbrz_black/12,986inbrz_paintflagged as "normal opposite face" = the design. Prefer invariants internal to one entity — e.g. the three normals of the same triangle >90° apart (112 of 618 glass faces) — then remeasure the whole residual after the repair: negating opposite normals fixed ~59%, the remaining ~41% needed flattening against the face normal. - Every rung must catch at least one KNOWN in-game bug, not just a synthetic self-test. The
self-test proves the MECHANICS; a real known-bad case proves COVERAGE. Keep a short per-car list of
known in-game defects and confirm the ladder catches each: BRZ backlight mixed-winding -> caught by
rung 2 (
winding_consistency: glass 18.2% of area); MercedesAMGLF left tail-lamp inverted -> caught by rung 4 (gate_carsee-through, L-vs-R 310/0). (both 2026-07-01) - A PASS states its SCOPE — never a bare "clean". "clean of see-through in
color/glass" is NOT "clean of winding".gate_carreporting "BRZ limpio" while rung 2 (per-piece uniformity) was unchecked was a FALSE GREEN the user caught in-game (2026-07-01). Name what each gate covered and did not. - Freshness: the analysis prints model SHA+mtime and flags changes vs the last run
(
model_freshness.stamp) — the deployed.p3dcan change under you between sessions (SUB_BRZ s16 de-dup removed 20320 faces mid-analysis; a stale golden then misleads). Owner of a shared artifact (a car's.p3d, or THIS skill) = the session working it; edit append-only + verify (this skill was being edited by two sessions at once, 2026-07-01).
A rip-imported PANEL is two parallel skins with an OPEN rim; a vanilla panel is a closed volume (SP-247, added 2026-08-15; SUB_BRZ doors E-1 — CORRECTED the same day, see the refutation below).
Any door, hood, tailgate or flap coming out of a game rip is a pair of surfaces — outer skin and inner card — that are never joined at the edge. The source game never shows that edge, so it was never modelled; every DayZ door OPENS, so every imported door shows it. This is the same finding as SP-198, measured a week earlier on the same car (leading-edge cap 25 cm² against the ~710 cm² a real cap needs, 77 mm of thickness available) — and the fact that SP-247 was written without reading it is itself the lesson: before authoring a fix, grep this file for the defect, not just for the car.
- Count a free edge over ALL faces of the LOD, not within the material. An edge the skin shares with glass or trim is covered, not open; skirting it pokes through the neighbour. Counting within-material inflates the census and aims the fix at edges that were never exposed.
- ⚠ REFUTED — do NOT close the rim with an extruded skirt, and do NOT calibrate on the edge-on
silhouette. The first version of this entry canonized both, on the strength of an edge-on render
going 15.0% → 25.4% of frame against a vanilla control at 26.8%. The user's eye falsified it the
same day: the edge was still see-through in game, plus two new defects. Two mechanisms, both
measured afterwards, and either one alone is fatal:
- An extrusion is a flange, not a cap. Welding a band to the rim closes that edge and opens a new one at the band's inner boundary. Measured on the shipped door: true free edge went 4455 → 5036 mm. The operation moved the hole inward; it never closed it.
- The extruded vertices inherited the rim vertex's UV, so every quad has 3D area and ZERO UV area — 235 of 235 quads measured at 0.00000 UV/3D against 0.18 for the skin beside them. The faces draw (same pixel count cull ON and OFF), which is exactly why a solid-colour offline render scored them as closed, while the engine samples a degenerate mapping and the player reads smeared garbage. A skirt that lands anywhere near glass reads as an artifact ON the glass: 39 quads within 5 cm of a pane, closest 6.1 mm. The silhouette percentage was measuring the patch, not the defect — the exact failure the corollary below warns about, committed in the bullet above it.
- The rim has to be AUTHORED as a cap, on the vertical leading and trailing edges only. Weld skin edge to card edge with its own UV strip (or a dedicated opaque edge material); do not sweep the whole free perimeter, because most of it is not exposed — measured on SUB_BRZ, only 31.5% of an automatic perimeter skirt landed on the two edges that were actually the complaint, while 17.4% (663 cm²) landed on the window top and became a new texture defect.
- RE-IMPORTING DOES NOT FIX THIS — measured twice. SP-193 found all six visual factory groups
already present face-for-face in the shipped proxy (100% centroid match ≤1 mm), and the pipeline's
own
build_detachable_doorsoutput measures 684 mm of free leading edge against the live door's 743 and vanilla's 0. The open sandwich is in the source. Re-assembly reproduces it. - Gate: a TEXTURED render plus an in-game capture with the panel OPEN, at the angle of the complaint. Never the silhouette percentage, and never a solid-colour render — both are blind to the UV failure that decides whether the fix is visible at all.
- Corollary on gates (same family as SP-202). "56 of 63 quads face the edge", "cap 25→40 cm²" and "silhouette 15.0→25.4%" all improved while the defect was untouched. A metric that improves without the symptom moving is measuring the fix, not the defect — go back to the artifact of the complaint.
- Stale reference, not threshold (LL-275). If three visible improvements make the metric monotonically worse (here
5.2->3.4->0.5 mmtoward negative), the reference is stale — do not negotiate the threshold. Review what it measures against: the exterior rail must sit on the sheet edge, the interior rail correctly rises to the glass; qualifying the interior against the sheet demands it not do its job.
Building an OFFLINE viewer/render for the user to judge? Two traps that make it lie (SP-248, added 2026-08-15; SUB_BRZ 1PP).
Both were caught by the user, not by any gate.
- What 1PP draws is the 1100 of each part PLUS every proxy that 1100 REFERENCES. "Does this
chunk file have a 1100?" is the wrong question: a proxy that ships only
res=0is still instanced by the parent's ViewPilot and falls back to its own LOD0. The SUB_BRZ shell'sres=1100lists 11 proxies including all four body chunks; excluding them hid the roll cage from the very viewer built to have the user point at it. Read the parent's 1100 proxy list, then per part pick1100if present else the finest visual — the same rule the engine uses — and never hardcode the resolution, because a part grows a 1100 the moment you cut something from it. - DayZ model space is left-handed; Three.js and a naive numpy raycaster are right-handed. Feed
raw coordinates and the car renders MIRRORED — driver on the wrong side — which invalidates every
left/right judgement the user is about to make. Negate display X and swap one pair of triangle
vertices so the winding survives; keep ids, bboxes and any ray metric in true model space, since
the
.p3dedit reads those. Gate it with an asymmetric fact that must hold on screen (here: the centreline must project to the RIGHT of the driver's eye,side > 0, or the build aborts) — per the reference-frame rule, an orientation you did not assert is an orientation you got wrong.
The ViewPilot (res-1100) is a CURATED view — not a copy, not a budget, and never trimmed against a partial car (SP-249, added 2026-08-15; SUB_BRZ s52, root-caused).
One car shipped both halves of this hole at once, and the user found both by eye: a roll-cage bar and a headliner crossing the road view in first person, and 73 faces of body sheet silently absent from 1PP while present in the very same model's LOD0. Three lessons, each with the measurement that proves it:
- What the engine draws in 1PP = the main's res-1100 PLUS every proxy that 1100 references, each
contributing its own res-1100 when it has one and its finest visual LOD otherwise. "Does this chunk
file have a 1100?" is the wrong question — SUB_BRZ's shell 1100 lists 11 proxies including all four
body chunks, which ship only
res=0and are therefore drawn whole. Corollary for hiding something from first person only: give that part its own res-1100 minus the geometry. A part with only a visual LOD is drawn entire the moment the parent's ViewPilot references it. - A driver-visibility TRIM must run against the complete assembled car — attachments included.
The 73 missing faces were deleted by an occlusion classifier whose rule was "drop every face no
seated eye sees front-facing", run over a scene built from shell + interior only
(
c2_export_pilot_json.py:11-12, verified). With the doors absent, the door-aperture sheet was reachable from the seat and read as back-facing, so it was trimmed — the exact strip the user later reported as "missing where the body should meet the door, same on both sides". That file's own comment records the same bug caught once before ("without it the classifier flags rear lights as removable") and fixed by adding ONE more occluder. One occluder short is the whole failure mode. Same trap bites offline viewers: a 1PP viewer built without the chunks hid the roll cage from the very screen built to have the user point at it. - Cabin materials inside BODY chunks are an import misclassification, and that is what puts junk in
front of the driver. 269
brz_cab_metalfaces inbrz_chunk_01and 18brz_cab_headinbrz_chunk_02; the ViewPilot only made them visible. Confine material families to their artifacts and check on PRESENCE, not on visibility — a leak outside the driver's cone today is a leak in his cone after the next pose change.
The gate this produced (rip_viewpilot_content_gate.py, schema rip_artifact_gate.v1) runs
three checks — STRUCTURAL (faces of a declared structural selection in the visual LOD but not in the
1100, keyed by rounded coordinates so it survives AddonBuilder reordering), OCCLUSION (driver-eye ray
fan over the truly-drawn 1PP scene against a per-car deny list) and CONTAINMENT (material family →
allowed artifacts, longest prefix wins so one deliberate exception does not weaken its family).
Calibrated red-then-green on the real artifact: FAIL with 73/269/18 on the pre-fix build, PASS on the
shipped one. Two habits it enforced on its own author, both worth copying: the first deny list was
too broad (it banned a material the driver legitimately sees overhead, and the gate said so at
10.9% of sight lines), and the deliberate shell-side console screen had to be DECLARED rather than
have the rule loosened.
Why nothing caught it for weeks: every gate in that suite treated the 1100 as a budget or as
presence — perf_budget counts its faces (and already encoded the proxy-LOD rule at
rip_perf_budget_gate.py:230-243, used only for counting), coverage is presence-only ("Nothing
here has an opinion about geometry"), lod_semantics classifies the SOURCE, and the one driver-eye ray
gate was scoped to a single defect. No gate compared a model's 1100 against its own LOD0. When a LOD
is hand-edited outside the builder — this one is proxy-only in rip_p3_structural.py and was
cloned by hand — it has no owner and no gate, which is exactly where silent cuts live.
A decimated LOD can keep every texture and material path and still render wrong: collapsed UVs. [EXACT] On LFQuad3 the far LODs lost the body texture and the quad looked different up close, while the texture/material path census was clean; only a per-texture UV-AREA census found it - LODs 2-4 carried body faces with UV area 0 across the board (LOD2: body1 1.827/1.827, body3 1.706/1.706, body2 2.618/2.650, metal 880/880) and LODs 5-6 cited only metal_co (measured in game, DayZ 1.30.164014 Exp). A ViewPilot (res-1100) derived from a decimated LOD inherits the defect. Action: when a vehicle looks different from afar or seated, census UV area per texture on every visual LOD and on the 1100/1200 before touching textures or materials; fix by UV transfer from the intact LOD, then re-census.
METHOD — three habits that stop the re-derivation (apply BEFORE the per-invariant fixes)
Not invariants but the working method that would have saved most of the LFQuad→SUB_BRZ→MercedesAMGLF iterations. They are otherwise diffuse across the references; they live here at the front on purpose:
- Parity-first. Debinarize a known-good vanilla vehicle (
civiliansedan, ODOL v54) ONCE and diff your model against it — LODs, named selections, crew proxies, wheel hubs, componentNN, memory points — into a single master gap checklist. Fix every gap in one pass instead of discovering them error-by-error in-game. →vehicle-structural-parity.md"Parity-first method". - In-game is the ONLY gate; offline is a hint. Offline parity / frame / winding / render checks repeatedly
passed GREEN while the car still failed in-game (proxy frame, body winding, seat collidability). A metric that
is
0.000/100%by construction (winding-vs-its-own-normals, frame=py3d-identity) proves internal consistency, NOT that DayZ renders / spawns / seats correctly. Never close a phase on a tautological gate. - Crew-probe headless FIRST when a get-in / radial action will not appear for a seat. BEFORE any rebuild
cycle: spawn the car + a known-good control (LFQuad/Croco) headless, dump
CrewPositionIndex(0..79), and runRaycastRV(ObjIntersectView)per seat anchored atpos_driver/pos_codriver. This is what caught the inward-winding blocker (#4) without eyeballing and broke the blind-rebuild loop. →vehicle-structural-parity.mdcrew-probe.- (added 2026-07-28, LL-219) A probe you INHERIT carries the coordinates of the artifact of THEN —
re-derive its targets from source before believing a negative. The probe is not the thing that
rots; a hardcoded seat centre is. Re-running it verbatim yields a
hit=0that is aim, not mechanism, and it is indistinguishable from the real verdict — while being exactly the "interesting" answer nobody re-questions. Measured on SUB_BRZ: the s9 mission (brz_crew_probe_init.c:122-123) aims at engine(-0.40, 0.72, -0.12)/(0.54, 0.72, -0.12), while today's boxes come fromprofiles\<car>.json→rip_p3_structural.pyseat params at py3d(±0.357, 0.55, 0.12)with half-extents(0.22, 0.25, 0.25)— the codriver ray passes 3.7 cm from the box face in X. Still inside, zero margin. Procedure: (a) re-derive the aim point from the profile + generator that produced TODAY's artifact, never from the handoff that described it; (b) compute the ray's margin against the target box — margin below ~one half-extent means the negative is not evidence, fix the literal first; (c) log the aim point used next to the verdict, so a later reader can tell a realhit=0from a miss. Same family as METHOD #2 (measure, never assume) applied to the instrument instead of the artifact.
- (added 2026-07-28, LL-219) A probe you INHERIT carries the coordinates of the artifact of THEN —
re-derive its targets from source before believing a negative. The probe is not the thing that
rots; a hardcoded seat centre is. Re-running it verbatim yields a
- Two failed rebuilds on ONE symptom = HARD STOP — build the probe, do not fire cycle #3. The rule
predated the failures and still got skipped (retro 2026-07-03: copilot get-in ~7 days of serial wrong
hypotheses; handlebar ~2 sessions of parameter sweeps settled by ONE GetSteering log; BRZ get-in 3
bottom-up sessions when the gate was one script override) — while EVERY time the discipline ran
(both-sides log line, crew-probe, headless harness, LL-100 field-diff vs a working referent) the issue
closed in a single pass. Before the 3rd in-game cycle on the same symptom: enumerate candidate causes,
read the actual mechanism (engine source / wiki, not intuition), instrument BOTH client and server
(verify the hook actually fires — a silent log IS a signal,
OnUpdateis not an engine callback), then ONE directed fix. If two probes also fail, escalate to a multi-agent audit over the REAL files — that escalation resolved the LFQuad wheelsim stall (4 sessions serial) in one session.
PREFLIGHT
Gate real build work on /dayz-preflight (P:\ mounted, AddonBuilder present, P:\Mods junction), per
_shared/dayz-conventions.md. Authoring config / model.cfg offline does not need it; packing does.
THE THREE FILES (and what each owns)
| File | Owns | Does NOT own |
.p3d | geometry, LODs, named selections, memory points, proxies, mass + CoM (Geometry-LOD vertex weights) | physics params, animation values |
config.cpp | the CarScript class, drivetrain / engine / suspension, Crew, damage, lights, cargo, attachments, sounds, AnimationSources | mass, CoM, animation axes |
model.cfg | CfgSkeletons (bone hierarchy) + CfgModels.Animations (how each bone moves) | physics, which sources are user-driven |
Mass is not a config field — it is computed from Geometry-LOD vertex weights. Full detail in the references.
REFERENCES (read by task)
- Author a vehicle from scratch →
references/vehicle-config-and-modelcfg.md: the complete, source-verifiedconfig.cpp+model.cfgfor a drivable car (Tyson89 Landrover + Crocodoc QuadBike anchored), every block end-to-end with provenance labels. - Make an imported / procedural model actually work →
references/vehicle-structural-parity.md: the parity-first method — debinarizeciviliansedanONCE, diff your model against it, and build all missing LODs / selections / crew proxies / wheel hubs in one pass instead of discovering them error-by-error in-game. Crew proxies, wheel hubs, lights, ride-height. Derived from the LFQuad parity audit. Its Appendix "REGEN-FROM-glTF + GET-IN RADIAL / LOD ladder" owns the regen-from-glTF proxy-split traps and the proxy-body get-in radial + LOD ladder (sectioned from the core). - Imported-car VISUAL gates + winding session changelog →
references/visual-gates-and-winding.md: the full per-session history of preflight item #10 (sub-entries #10(a)-#10(o), s14->s23), including SUPERSEDED entries. The core keeps only #10 as a stub plus THE RULE; this file is the detail behind every#10(x)cite. - Ship it without it breaking on a server →
references/build-packaging-and-debug.md: binarize dropping config-only textures (white vehicle), ODOL-vs-MLODmodel.cfg, reversed wheel spin, harmless proxy drive paths, and offline PBO verification. The failures that pass filepatching and break on a dedicated box. The LFQuad shipping lessons. - Boats, trucks, and the ATV / motorbike gaps →
references/vehicle-types-boat-truck.md: theTransport→Car/Boat sibling hierarchy and the per-type deltas — boat propeller/buoyancy/ BoatFluid + always-free get-in, truck 3-axle DRIVE_642 + single-classname double wheels, ATV slots onChassis, and the honest motorbike gap (pre-1.30; for DayZ 1.30 motorbikes use thedayz-motorbikesskill). All source-verified against vanillaP:\scripts. - Get-in prompt, cursor actions and proxied-sub pose →
references/get-in-actions-and-proxy-pose.md: the four-link get-in condition chain, theActionConstructor+PlayerBaseregistration contract, and the measuredGeometry/autocenter=0gate for multi-LOD proxy submodels. - Let a rider FIRE their own weapon from a moving vehicle (free-gunner / walk-on-bed) →
references/gunner-shoot-from-vehicle.md: the SIBNIC "Gunner" pattern reverse-engineered — the one trick (IsInVehicle()→false unlocks the weapon) plusLinkToLocalSpaceOfattach, frozen physics,HumanGunnerCommandpose, edge-detection raycast walk controller, leash/tether, per-vehicle JSON of up to 10 slots, and the client-authoritative weakness to fix. The handheld free-gunner family; composes with the fixed-turret weapon recipe (vaultvehicle-weapon-system.md).
Imported-model pipeline (OBJ → DayZ .p3d)
When the body comes from an artist OBJ or another game (the LFQuad is an imported Yamaha Banshee),
these cover the import path end-to-end (recovered here because the plugin dayz-model-pipeline
dropped them in the 2026-06-05 migration):
references/external-obj-import.md— end-to-end OBJ → game-ready.p3dwalkthrough (parse, transform, decimate, assemble LODs, encode textures, write config). Start here.references/decimation-libraries.md— reducing a high-poly import to per-LOD face budgets.references/png-to-paa-encoding.md— PNG → PAA in pure Python, no external tools.references/emissive-leds-and-dynamic-lights.md— emissive materials + dynamic lights (headlights / brake / dashboard).references/placement-and-autocenter.md— origin / autocenter / placement gotchas.references/binarize-vertex-budget.md— evidence behind invariant 24: the three verdict states ofbinarize, the real ceiling in triple units, headroom accounting, and the noise that does NOT discriminate. Read it when a model passes every offline gate and still will not spawn.references/legacy-gate-ladder.md— superseded pre-CAMBIO-0 diagnostic order, kept for vehicles already in flight. Nothing here is current doctrine; the findings that used to sit under that heading and still are (SP-247/248/249) now have their own sections above.
REDIRECT CAMBIO-1: sole symptom→cookbook index for family B is in
../rip-vehicle-import/SKILL.md.
references/rip-geometry-and-winding.md- rip geometry: retopo, winding, and face surgery.references/proxies-and-get-in.md- proxies, get-in, and detachable parts: the contract.references/doors-and-panel-edges.md- doors: open edge, caps, and detachables that are not doors.references/network-physics-ownership.md- network, physics, and ownership: who governs the pose.references/visual-metrics-and-viewer.md- visual metrics: what can be stated from a capture.references/materials-and-selections.md- materials and selections: two misleading diagnoses.references/animation-sign-and-axis.md- animation sign is not judged without its axis.references/process-gates.md- process gates: negative fixtures and promoted rules.references/gauge-needles.md- dashboard needles and dials: frame anchored to model markers, rotation direction measured in vanilla, cluster approved in game (global verdict), needle = hub + blade without disc and in front of panel, content-based PAA cache, derivation instrument with guard, complete recipe, fuel gauge from scratch, new geometry in animated selection (trap 15), vertical handlebar axis (trap 16), two signs per new face copied from neighbors, winding and stored normal, and checking cluster at night (trap 17).
CITE-THEN-VERIFY
Vehicle config and model values are easy to half-remember. Before writing a class name, property, or
named selection, grep it in vanilla (P:\dz\vehicles\) or in the references' cited sources, and keep
the provenance labels the references already use ([Landrover ✓], [QuadBike], [LFQuad ✓],
[TBD-verify]). Anchor any new vehicle lesson to a real mod with path:line, never to memory.
LIGHTS — the five failures that look like "the material is broken" and are not (added 2026-08-17; SUB_BRZ B2-B6, all measured in-game)
- Light-selection preflight (LL-237). Before reassigning a rear lamp, print every light hidden selection's face count and bbox. A lateral selection whose bbox crosses
x=0owns centre geometry (e.g.x=-0.12to0.65= the tailgate bar); dozens of faces compressed into ~1 cm (here 44 faces in 0.01 m) are degenerate and draw nothing. Brake and tail must own disjoint faces; if they overlap the laterSetObjectMaterialwins and the result is indeterminate. - The
CarScriptlight-selection ABI is positional (SP-178, added 2026-08-31).carscript.c:293-301fixes indices 0/1 front, 2/3 brake, 4/5 reverse, 6/7 rear position and 8 dashboard. Names are free; list order is not. Indices>=9have no nativeCarScriptlight meaning and require the mod's own driver. Indices 2/3 and 6/7 must resolve to disjoint face sets, or the laterSetObjectMaterialwins. - With both headlight bulbs active,
*_dirdoes not steer the beam.CarHeadlightBulbsState.BOTHattaches one aggregated light at the midpoint oflight_leftandlight_right(carscript.c:2136-2141). Measure that midpoint against the centre-line body surface; a point inside the shell traps the beam. Apply the same check tolight_reverse(carscript.c:2182).
A car whose lights "do not work" almost never has a broken .rvmat. Five distinct causes were
separated in one session by measurement; each has a cheap discriminator. Run them in this order,
because the first one invalidates every test downstream.
1. A leading backslash in a material path is rejected in SILENCE
SetObjectMaterial accepts MOD\data\x.rvmat and rejects \MOD\data\x.rvmat. No error, no
log line: the readback simply still shows the old material. Proven with an A/B that held the car,
the selection, the index and the target file fixed and changed only the leading character:
asked \SUB_BRZ\data\brz_light_brake_on.rvmat -> got \sub_brz\data\brz_light_brake_off.rvmat REJECTED
asked SUB_BRZ\data\brz_light_brake_on.rvmat -> got SUB_BRZ\data\brz_light_brake_on.rvmat APPLIEDVanilla never writes the leading backslash on a material path — CivilianSedan has 25 of them,
all starting at dz\ (DZ\vehicles\wheeled\config.cpp:5165-5195). Neither does a working modded
glowing item. But model= paths keep their leading backslash and resolve fine, which is why
the inconsistency survives review: half the file uses one form, half the other, and only the
material half breaks.
Applies to hiddenSelectionsMaterials[] and to every *MatOn / *MatOff key.
Grep before blaming anything else: "\\MOD[^"]*\.rvmat".
2. A lamp whose faces are in NO hiddenSelection can never light
Rip-imported cars arrive with the lamp split in two: a large piece (tail, light_left_static,
light_right_static, light_dashboard_static) that is absent from hiddenSelections[], plus a
small switchable insert that is in it. On SUB_BRZ that was 19.310 faces that nothing can ever
switch against inserts of 42 and 318 faces.
The symptoms read as material bugs and are not:
- "the red piece is always faintly lit and has no glass" -> the big piece wears a crude constant emissive that no setter reaches.
- "only one bulb of the two lights up" -> only the insert is in the selection.
- "the position lamps do not light" -> they do; 42 lit faces against 1.555 unlit around them.
Discriminator, before touching any material: count faces per selection and cross them with
membership. A bounding box is NOT enough — tail spanned 3.788 mm of a 4.086 mm car and still was
only the two rear clusters plus two corner markers. Plot every face centroid; the distribution
answers what the box cannot.
3. Engine-off and lights-off on driver exit are TWO different bugs
Engine is script and is fixable. CarScript.OnDriverExit calls EngineStop() when the gear
is not neutral (carscript.c:1207-1215); Transport.OnDriverExit is a no-op
(3_game/vehicles/transport.c:161), so an override that does not call super loses nothing. Add
ShiftTo(GetNeutralGear()) (car.c:271, :262) or a running engine plus Brake.driverless
makes the car creep. Measured working: engine=ON on client and server, at the event and at
+500 ms and +2 s.
Lights are NOT fixable that way. The native forces LightIsOn() false on the CLIENT as soon
as the driver seat empties, and vanilla's m_Headlight is slaved to it
(carscript.c:2116-2156), so the beam is always destroyed and re-asserting server-side cannot
bring it back. The working pattern decouples the beam from LightIsOn():
- a synced
boolintent + an ownCarLightBasemember; RegisterNetSyncVariableBoolon the intent in the constructor;ToggleHeadlightsoverride that records the intent andSetSynchDirty()on server — the native exit-clear does NOT callToggleHeadlights, which is exactly why the intent survives it;- a
ManageExitLight()that creates/destroys the own beam edge-triggered, attached at the midpoint of the two headlight memory points, called from aUpdateLightsClientoverride; EEDeletecleanup so the beam cannot outlive the car.
To make the parked beam appear promptly, copy vanilla's own line rather than inventing one:
ForceUpdateLightsStart(); g_Game.GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ForceUpdateLightsEnd, 100, false);
(carscript.c:535-536; the pair is defined at :2981 and :2990).
Reference implementation in this tree: P:\LFQuad\scripts\4_world\entities\vehicles\inherited\LFQuad.c.
4. No bulbs mounted = brake and tail never light, and it reads as a failed fix
m_HeadlightsState stays NONE without HeadlightH7 in the reflector slots, and the rear beams
are gated on it (carscript.c:792-813, :2177). Testing brake lights on a car whose bulbs sit in
the cargo produces a confident false FAIL.
Related and cheap: OnDebugSpawn must use CreateAttachment, not CreateInInventory.
Vanilla's SpawnUniversalParts (carscript.c:3121-3158) drops everything into cargo, so every
test cycle starts with manual assembly and one forgotten bulb poisons the run.
5. Measure the setter against a VANILLA car spawned alongside
SetObjectMaterial / GetObjectMaterial are proto native (entityai.c:2896-2900): whether they
work is not decidable by reading source. Probing only the car under test cannot separate "our model
is wrong" from "this build does not apply overrides at all". Spawn a stock CivilianSedan next to
it and run the identical call, into the SAME log:
| vanilla switches | ours switches | conclusion |
| yes | yes | no override fault; it was the bake |
| yes | no | the fault is in OUR model |
| no | no | the environment does not apply overrides; the probe never measured the setter |
| no | yes | incoherent, repeat |
Resolve the selection by name with GetHiddenSelectionIndex (entityai.c:2792-2798) and print
the resolved index; never hardcode an index onto a vanilla car. If the resolve fails, SKIP — do not
fall back to 0, which is the front-left light of the CarScript ABI (carscript.c:293-301) and
corrupts both the car and the measurement.
And adjudicate by photo: the got= of a readback is the setter's echo, not proof that the engine
drew anything.
INVENTORY PREVIEW — two ways a part preview renders empty (split from LIGHTS 2026-10-01)
1. rotationFlags is per SIDE, and the wrong one renders an empty preview
Left-hand doors take 8, right-hand doors take 4. Vanilla applies this in every family —
Hatchback_02_Door_1_1/1_2 use 8 while 2_1/2_2 use 4 (DZ\vehicles\wheeled\config.cpp:9006,
:9130, :9141, :9265), and Truck_01_Door_2_1 overrides its left-hand parent purely to set 4
(:18301). A mirrored door that inherits the left-hand value shows a blank item-inspect preview
while its opposite renders normally, with everything else identical: same LOD table, same named
properties, same bounding box, same distance to origin, same selections, same winding. Symmetric
config plus symmetric geometry plus one side blank means look at rotationFlags before the model.
2. An inventory preview goes BLANK when invview sits at the bounding-box centre (SP-415, added 2026-10-01, SUB_BRZ s98)
A part mounts, renders in the world and keeps its tooltip, and its inventory preview is still empty.
ItemPreviewWidget view 0 is boundingbox_min + boundingbox_max + invView
[EXACT][CLAIM-INVVIEW-VIEW0-DEF] (scripts/3_game/gameplay.c:293, DayZ 1.30.164014 Exp), and the
icon forces SetModelOrientation("0 0 0") + SetView(GetViewIndex())
(containeditems/Icon.c:1385-1386): with invview at the box centre the eye-to-centre direction is
null and the render comes out empty. Measured on the two SUB_BRZ doors (s98, in game, DayZ
1.30.164014 Exp): both had invview within 3e-8 m of the bbox centre (and of ce_center / pos
center); vanilla puts it ~1.07 m from the centre, OUTSIDE the box, on the side to show, and reuses
the same point on both mirrored doors. Never write invview = (min+max)/2; offline gate:
|invview - bbox centre| > 0.3 m for every part with a preview. With invview2 present,
ATTACHMENT/GROUND render view 1 and CARGO/HANDS view 0 (scripts/3_game/entities/entityai.c:3746-3788) -
design both point sets together or not at all.
Get-out desync: one-shot bilateral OnDriverExit probe (SP-276, origen LFHeli LF-001)
- The ex-owner proxy is frozen until re-registered (LL-306). With
NetworkMoveStrategy.PHYSICS, on get-out the former owner falls back to the last proxy snapshot received before possession and receives no further server transform. A clientSetPosition/teleport or a server pose nudge cannot repair it (clientSetPositionhad no effect at 50 ms; 10 cm server lift had not arrived in 25 s). After a short server-side delay (~500 ms afterOnDriverExit) and only when every crew seat is empty, rebuild the remote representation withRemoteObjectTreeDelete/Create(scripts/3_game/global/game.c:707-709; guarded implLFHeliCore/scripts/4_world/LFHeli/LFHeli_Base.c:1167-1235):3/3re-entries,|Δ| 0.12/0.11 m. Schedule it from the surviving server object and cancel on deletion; aCallLaterowned by the destroyed client replica cannot complete safely.
For "invisible/desync on exit" bugs, a one-shot probe in OnDriverExit that prints on BOTH
sides playerPos, heliPos, crewEntryWS, dPH, dPC adjudicates in one flight what
hypotheses do not: good exit = client == server; bad exit = client with the vehicle at the
spawn pose (sunk) and the player underground, server correct. Align client/server clocks by
PAIRS of twin events (the two EXIT lines gave a stable 33,99 s offset) and correlate with
the owner's position series (FRAME p0) to date the divergence. Complements the Pawn ladder
in references/network-physics-ownership.md (SP-188).
LESSONS ARCHIVE — read by topic, not by date
Accumulated lessons live grouped by topic in references/. Each
entry states what is inside so opening can be decided without opening.
Everything below is CURRENT doctrine (superseded material is in
references/legacy-gate-ladder.md, and that alone).
- Rip geometry: retopo, winding, and face surgery ->
references/rip-geometry-and-winding.md(16,302 ch, 8 lessons) Everything touching the imported MESH: rip intake, auto-retopo, winding (gate and direction),autocenter, and how to delete/split faces without breaking selections.- RIPPED RACING-GAME IMPORT
- Auto-retopo of a dense rip = Quadriflow PER-PANEL + ASCENDING target sweep
- Rewriting a proxy triangle: corrected rule for proxies and visual faces
autocenter=0: corrected scope by LOD, host, and submodel- A winding gate must measure the WHOLE piece, ALL render LODs, and twin pairs
- Game winding is the INVERSE of MLOD geometric winding; fix winding per…
- Face surgery in a .p3d: a criterion along ONE axis splits the quads that…
- Deleting faces from a .p3d with py3d: mutate
lod.facesIN PLACE or you break the…
- Proxies, get-in, and detachable parts: the contract ->
references/proxies-and-get-in.md(17,979 ch, 7 lessons) Proxy placement measured on reference car, four-layer contract of detachable parts, two registrations vehicle action needs, and two failures blocking entire get-in.- REGEN-FROM-glTF BODY + PROXY-SPLIT / GET-IN RADIAL + LOD LADDER
- Proxy placement convention - measured on a working reference car
- Detachable parts (doors/hood/trunk): the FOUR-layer contract, and three rules…
- In-vehicle actions need TWO registrations, and a proxied part is your…
- A shared vehicle-core source turns "deploy ordering" gates into fiction
- Phantom vehicle command blocks ALL vanilla get-in after a client crash while…
- ViewPilot (1100) of a shell+proxy car MUST carry the body geometry, not only…
- Closing first-person gaps: membranes in the ViewPilot only, behind the steering…
- Doors: open edge, caps, and detachables that are not doors ->
references/doors-and-panel-edges.md(13,308 ch, 4 lessons) A ripped panel has no edge. How missing edge is MEASURED (free edge length against vanilla control), how closed via script, and how hood and trunk differ from a door.- An imported door has NO end caps, and a shut door cannot show you
- A missing door edge is measured by FREE EDGE LENGTH against…
- Door edge IS CLOSED VIA SCRIPT with a MEASURED depth band — and…
- Detachables that are NOT doors: hood and trunk (measured sub_wrxsti_04,…
- Network, physics, and ownership: who governs the pose ->
references/network-physics-ownership.md(11,034 ch, 3 lessons) Network ownership vs server-side forced seat, why writing pose fights owner reconciliation, and type ladder of custom Pawn frame with its hard rules.- Network ownership: server-side forced seat != client ownership; PHYSICS…
- PHYSICS = owner prediction with reconciliation: writing pose fights…
- Custom Pawn frame (Move/OwnerState): type ladder and its hard rules
- Visual metrics: what can be stated from a capture ->
references/visual-metrics-and-viewer.md(8,553 ch, 3 lessons) A pose complaint can be a PERSPECTIVE artifact. HUD ray anchor. And the general rule: diagnose before re-authoring, and validate every visual metric on something visible in-game.- An in-game GEOMETRIC POSE complaint can be a PERSPECTIVE artifact - measure…
- HUD reticle/marker: anchor the ray at the CAMERA, never at the vehicle
- Diagnose before re-authoring, and validate every visual metric on an…
- Materials and selections: two misleading diagnoses ->
references/materials-and-selections.md(3,959 ch, 2 lessons) Identical textures and server co-mismatch look like solid diagnoses and are not. And parent-driven selections (hiddenSelections / config anims) do NOT reach a proxy.- Two diagnoses that look solid and are not: identical textures, and server…
- Parent-driven selections (hiddenSelections / config anims) do NOT reach a…
- Animation SIGN is not judged without its AXIS ->
references/animation-sign-and-axis.md(4,368 ch, 1 lesson) How to fix axis before discussing whether animation is reversed, and why sign only means something relative to that axis.- An animation's SIGN is never judged without its AXIS - use the pseudovector…
- Process gates: negative fixtures and promoted rules ->
references/process-gates.md(4,161 ch, 2 lessons) An import gate rule is not deliverable without a NEGATIVE fixture. Plus rules promoted from lessons corpus.- An import gate rule is not deliverable without a negative fixture
- Rules promoted from lessons corpus
- A config gate is not a visual gate (SP-483)
Automated vehicle stand — measured invariants (added 2026-08-24)
Measured on dayz-mcp stand (DayZDiag 1.29.163709, CivilianSedan), ABBA G0 session
(origin: real frictions that aborted 3 attempts; evidence in
AI/10_Projects/DayZ_MCP/reviews/2026-08-24-g0-veredicto-nocturno.md):
- Get-out:
ActionGetOutTransport.ActionCondition=crewIndex >= 0 && CrewCanGetThrough && IsAreaAtDoorFree(actiongetouttransport.c:68-77;CCTNone— target does not participate). A car stopped AGAINST statics leaves door blocked and action incondition_failed; stand control surface has no reverse gear (throttle 0..1) — site becomes unrecoverable. Design site with >=150 m clear IN DRIVING DIRECTION and graceful cleanup: deleting fixture with player inside cleanly ejects (4/4 measured). - Spawn heading: 4th parameter of
CreateObjectExisRF_*flags, NOT yaw; vehicle inherits ground orientation at point (~11.6 degrees between two points 40 m apart). No orientation setter via tools: if experiment demands comparable headings, sites are selected empirically (trace samples give actual driven heading). - Positional drivability: a vehicle can end up FROZEN (2s delta ~0.12 m at throttle 1.0) due to SITE — same binary/config drove 91 m at one point and 0.12 m at another 40 m away. Before blaming config/ownership/code: ABBA of sites (LL-359).
Stand get-out: the real chain and sanctioned teardown (added 2026-08-24 afternoon)
Triangulated across 13 rounds on NWAF (evidence: session 2026-08-24c + ledger entry
fb-20260824-133301-ecf5). ActionGetOutTransport condition_failed on stand fixtures
DOES NOT depend on site (identical across 9 sites, 7 pristine):
- Driver door spawns CLOSED and
CrewCanGetThroughrequires it non-closed (civiliansedan.c:214-222; phase <=0.5 = CLOSED, carscript.c:2801-2811). - Opening via action DOES NOT work: server half of injected actions is inert
(
OnStartServer/SetAnimationPhase does not materialize, actioncardoors.c:92) - action STARTS on client (started=1) but phase remains 0.0. Cheap discriminator: ActionCloseCarDoors returns condition_failed after "opening" (its condition requires phase >0.5 and shares IsAreaAtDoorFree with get-out, which is exculpated). - SERVER replica of client-auth car does not leave spawn point even if owner-peer drives 70-100 m: object_anim/object_inspect (resolve by position on server, radius 25, MCPBridge.c:20) must be targeted AT SPAWN or not used.
Sanctioned teardown: object_delete of fixture with ejection verified via telemetry
(not_seated); 18/18 clean accumulated. Action-based get-out is NOT a site criterion.
A componentNN-tagged hub is useless if in wrong LOD (added 2026-08-29, LFQuad2 Arma 2 OA ATV port)
Preflight #4 of this file requires seats and hubs carry dual-tag componentNN. It states
WHAT tag, not WHERE. The other half --that selection must live in Geometry LOD-- is
what failed today, and what a port inherits from donor unknowingly.
Engine signature, identical to 2026-07-17:
ENTITY : Load entity type '<Clase>'
PHYSICS (E): Won't simulate, wheel wheel_1_1_damper_land has no proper selection in geometryConsequence: CreateObjectEx returns null. Vehicle fails to spawn; it is not merely looking wrong.
Where hubs go, measured on THREE working models, not just one:
| model | *_damper_land in Geometry | LandContact LOD |
| LFQuad (living reference) | 4, with faces and dual-tag componentNN | does not exist |
civiliansedan_mlod.p3d (vanilla) | 4 | does not exist |
| Crocodoc quadbike | 4 | does not exist |
| LFQuad2 (port, failed to spawn) | 0 | exists, and there all 4 were, with 1 point and 0 faces |
The port trap: an Arma 2 MLOD includes LandContact LOD, and porter leaves contact
points there because name matches. DayZ does not read hub from there: wheelHub in
config.cpp resolves against Geometry. Notice both symptoms are same error --
a selection with 1 point and 0 faces cannot carry componentNN either, because there are no faces
to tag.
5-second check before any in-game cycle: for each wheelHub named by
config, that name must exist in Geometry LOD, with faces, and dual-tagged. In
complementary non-enumerated form: derive it from model's own config, which names the
hubs, not from a hand-written list.
And process warning, which is expensive half of this entry: signature of this error was already
written, in history/cambio-1-superseded-family-b-rules.md:103, a file whose header says
HISTORY ONLY - NO AUTHORITY. Bit again six weeks later. When archiving a rule,
check that living half preserves BOTH questions: what, and where.
CONFIRMED IN-GAME same day. After moving the four selections to Geometry LOD --names on geometry that ALREADY existed, without adding a single face-- vehicle spawns:
[LFQ2-TEST] LFQuad2 type=LFQuad2 pos=<13282.5, 7.27, 6883.2> wheels=4 fuel=1CreateObjectEx returns object instead of null, with all four wheels attached. Meaning
Geometry gap was not only necessary: it was sufficient for engine to
build physics. Useful to know because engine stops at FIRST error, so prior to
testing it could not be ruled out that more blockers lay behind.
Measured trade-off, to watch when driving: in a port, sole geometry corresponding to each wheel is usually the entire wheel (here 0.198 x 0.599 x 0.599 m) vs reference hub box (0.20 x 0.22 x 0.22 m). It is the honest choice --inventing a small box alters Geometry LOD mass-- but leaves a 3x larger collision component on two axes. If after fix suspension acts strange or vehicle feels anchored, that size is first suspect, not config.
Port parity: three gaps no config reveals (added 2026-08-29, LFQuad2)
A port copies what it sees in reference config.cpp. These three do not live in any config,
so they survive intact through any review reading configs:
- The
refillanchor. Without arefillselection in Memory and in ViewGeometry, the refuel action never appears (actionfillfuel.c:13,61-70;transport.c:75-78,313-315). Copying theGetActionDistanceFuel()override from the reference is not enough: it adjusts the distance of an action that, without an anchor, does not exist. - Custom
CfgSoundSets. Inheriting the base class leaves the vehicle mute: zero engine soundsets insoundSetsFilter[]and none of its own. It emits no error in any log. modded classwith no trace in config. The reference hadActionGetInTransport(fixes the turn of more than 180 degrees when mounting from the opposite side) andModItemRegisterCallbacks(one-handed wheels). No config mentions them, so they only appear by comparing script trees.
Method that found them, which is the portable part: do not enumerate what to inspect, but derive the contract from running reference --"every selection family reference has, the port has"-- with a short list of authorized exceptions. Port had 17 gates, 15 green, and failed to spawn; complementary sweep extracted 22 gaps in a single pass.
Script visual state is NOT applied to a newly spawned vehicle, and config default looks like a result (SP-384, added 2026-09-10, SUB_BRZ s90)
Visual code running from OnVariablesSynchronized never executes on a
vehicle created with a bare spawn: has not synchronized anything yet. Car paints
hiddenSelectionsTextures[] and hiddenSelectionsMaterials[] entries from config.cpp and
nothing else, so all script SetObjectTexture / SetObjectMaterial calls are missing.
It is a silent trap because config default is usually a plausible version of what script would have painted: an almost black screen, unlit indicator light, blank panel. Reading those pixels as result of scripted branch gives a confident and false verdict.
What does synchronize, measured: mounting parts. A debug-spawn fixture attaching battery, spark plug, radiator, four wheels, and doors is a large attachment synchronization and triggers visual update. Seating a player and starting engine also works, with cost detailed below.
The discriminant: carry a control IN THE SAME FRAME. Choose a SECOND selection whose script state is distinguishable at a glance from its config default, and read it in same capture as subject:
- instrument cluster: config gives it OFF material, script gives it ON.
- any indicator light: OFF in config, ON only from script.
Thus frame answers two questions at once. Lit control => scripted branch ran, therefore subject appearance is the result. Unlit control => script did not run and subject says nothing, whatever it looks like.
Compare emmisive[] of the two materials before trusting the eye: scale difference of
0.3 vs 1.1 is unmistakable in broad daylight; 0.9 vs 1.0 is not.
Cost of alternative route (DayZ-MCP harness, measured 2026-09-10): starting the engine requires client-side vehicle ownership, and taking it leaves free camera unusable for the entire lifetime of client process. Framing dashboard and having engine running become mutually exclusive on the same client. Prefer synchronization via attachments.
Every axle must be listed in an Axles override: what the engine ENUMERATES is not inherited (added 2026-09-11, Arma2Quad)
A derived vehicle class that redeclares class Axles: Axles and lists only one axle loses the
other one entirely - not its values, the axle itself. Measured on three drivetrain variants that
declared only class Rear: Rear: in game all three reported wheel_count 2 against 4 on the
control, sat nose-down (pitch -7 to -10 deg against -0.3) and would not move. The user saw it
before the diagnosis did: "the front wheels are buried and they do not turn". The fix is one
line per variant: class Front: Front {};.
The discriminator, which is what makes this reusable: in the SAME override Clutch, Engine,
Gearbox and Steering were inherited without being declared, and those variants kept engine,
gearbox and steering. Inheritance works for children the engine looks up BY NAME; it does not
for children the engine ENUMERATES because their names are free - the axles under Axles.
Vanilla never exercises the hole: every vanilla car lists both Front and Rear in every
override (DZ\vehicles\wheeled\config.cpp:494-530, :1424-1489), so copying vanilla hides it.
Nothing offline catches it: CfgConvert parses the config without a complaint and the script
linter says nothing. The cheap tell is to count in the engine what should be there -
telemetry_read(mode="object_at").wheel_count on the prepared fixture - before handing a build
to a human. A bare fixture_not_ready from vehicle_prepare_fixture is usually exactly this.
config.cpp syntax has one offline gate, CfgConvert, and it only counts with canaries (added 2026-09-11, Arma2Quad)
-packonly does NOT rapify, so the config ships as text and the first parser that reads it is
the game's. The DayZ script validator does not check syntax either. What works:
"...\DayZ Tools\Bin\CfgConvert\CfgConvert.exe" -bin -dst <tmp>.bin <config.cpp>Gate it with two canaries that MUST fail, or a green proves nothing:
| canary | what CfgConvert answers |
remove the brace that closes CfgVehicles | line NNN: /CfgVehicles/: Missing '}' |
| misspell a base class inside the new block | Undefined base class 'ClutchTypo' |
The second is worth more than it looks: it proves the parser RESOLVES the nested class X: X
chains, which is the real risk when writing variant classes. And one thing that does NOT work as
a canary: dropping the ; after a closing } - CfgConvert accepts it, so it is not a syntax
error in that grammar and a gate built on it declares itself vacuous for the wrong reason.
"It slides" is almost never fixed by lowering grip: weigh wheel force against grip (added 2026-09-11, Arma2Quad)
A first-order Coulomb estimate is not a simulation, but it RANKS layouts, and the ranking is the
decision. First gear asks clutch cap x gear x final drive / tyre radius at the wheels; the
driven axle holds mass x 9.81 x its share of the weight x tyreGrip. On a 750 kg quad with a
180 Nm cap, 4.3 x 3.667 / 0.35 m, front/rear split 65.2/34.8:
| layout | asked | held | over | what the driver said |
| RWD | 8109 N | 2535 N | 3.2x | "they slide a lot" |
| AWD, open diffs | 8109 N | 7284 N, less in a corner: the inner wheel spins first | - | "they slide a lot" |
| AWD, locked rear | 8109 N | 7284 N | 1.11x | "least bad, but it cannot go straight at full throttle without skidding" |
| FWD (shipped) | 8109 N | 4749 N | 1.7x | stable, but "it turns less" |
That arithmetic predicted the exact order the driver reported. Lowering tyreGrip moves every
row the wrong way: at 0.85 the locked-rear AWD goes from 1.11x to 1.30x and the FWD from 1.7x to
2.0x. It agrees with invariant 18 from the other side - the SUB_BRZ accel-slide was fixed with
grip UP and throttle DOWN.
Vanilla tyreGrip census for healthy wheels, so no one calls a value "too high" again without
one: 0.80 (:875), 0.82 (:13402), 0.85 (:4774), 0.89 (:8984), 0.95 (:18076, :20508),
1.0 (:18104); ruined wheels sit at 0.2-0.5. A 0.99 is inside the vanilla band.
Corollary for a wheel-driven steering axle: on DRIVE_FWD the wheels that spin are the wheels
that steer, so excess torque reaches the driver as "it will not turn", never as "wheelspin".
Halving a vehicle's mass halves the grip budget while leaving the torque where it was, which on
its own turns a vehicle that steered into one that pushes - the trap behind 15g(a).
CivilianSedan on a straight: what torque, tyreGrip and the gear ratios did, measured (SP-424, added 2026-09-24)
Measured 2026-09-13 in DayZDiag: NWAF concrete, standing start at full throttle through the MCP
drive controller, one config field changed per variant on CivilianSedan / CivSedanWheel,
vehicle_trace at 19-20 Hz. The samples are small and the n is given per result: these are
measurements, not general law. The controller of that build skipped gears above ~33 km/h
(dayz-mcp-verify, SP-423), so the launch figures stop there.
- Torque drives the launch almost proportionally. Acceleration over the first 3 s scales as torque^0.93-0.95 (torque ×0.7 → ×0.72, n = 1; ×1.3 with clutch 520 → ×1.28, n = 3; baseline n = 5), and the 0-30 km/h time as torque^-1.17. First gear does not slip (rear slip p90 ≈ 0.001): the torque reaches the ground.
- tyreGrip between 0.70 and 1.00 changed neither the straight launch (≤ 2 %) nor braking (5.7-6.0 m/s²). At 0.30 the car braked 22-30 % less and launched 10 % faster with almost no slip (n = 1, unexplained). In this regime tyreGrip does not act as a simple longitudinal friction coefficient.
- First upshift at 9.20 m/s, against 9.33 m/s predicted by
0.8 × redline × 2πr / (60 · i_1 · i_final)(n = 5): the formula holds for wheel radius and gear ratios. - Steady arc at steering 0.3 (~20 m circles): drift angle falls from 8.3° at 14 km/h to 3.4° at 32 km/h, lateral acceleration reaches 7.2 m/s² (≈ 0.73 g), no slide (n = 1).
Rule of thumb: to change the launch, change the torque. Above ~0.7, tyreGrip does not change the vanilla sedan's straight launch or braking; look for its effect in cornering and drift, which is not measured yet.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

