Installs into .claude/skills of the current project.
Are you the author of Dayz Clothing?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/willy92wins-dayz-clothing)
---
name: dayz-clothing
description: "Use when: clothing mod, worn model, ClothingTypes, chaleco, armadura, la ropa flota/rígida/girada, slot Vest/Body/Armband, NBCHead/NBCTop/NBCPants/NBCBoots/NBCGloves, NBCBag, Waterskin, gas mask filter, headSelectionsToHide, wet clothing. Worn clothing (_m/_f). Not humanoids/rigging: dayz-characters; not proxy triangles: dayz-proxy-align."
---
# DayZ Worn Clothing — the verified pipeline
Distilled from ArmorHneck (2026-08-03): third-party body armor RAR → fully working
worn item, ~6 in-game cycles to isolate three independent SILENT failure modes.
Everything below is in-game verified on DayZ 1.29 diag unless labeled otherwise.
(until 1.29: the worn pipeline below is the 1.29-diag contract.) (since 1.30 Exp: killers #1–#3 and the worn p3d/model.cfg contract still hold. Config-side NBC layering, mask-filter protection, ItemBaseType wet/hide keys, shoe `itemSize`, Waterskin rename, and wash/wet actions: [dayz-1-30-clothing.md](references/dayz-1-30-clothing.md) and ## DayZ 1.30 Exp.)
## THE THREE SILENT KILLERS (check these FIRST, in this order)
A worn item can fail three independent ways, each with ZERO RPT errors. Any one
of them wastes cycles if you chase the others first.
1. **Config must be TEXT in the PBO.** A `config.bin` produced by CfgConvert in
this environment does NOT register (class absent: `CreateObjectEx` →
`unknown_type`, invisible to VPP). Pack `config.cpp` as text (packonly);
every working local mod ships text. Gate: live spawn of the classname —
never trust static PBO inspection for "it registered".
2. **Worn mesh must be in the canonical clothing frame** (see next section).
A mesh built facing +Z renders as EXPLODED rigid pieces floating ~1 m above
the player (each 0/1-weighted plate is transformed by its bone from a
flipped frame). Fix: rotate 180° about +Y (det=+1 — do NOT touch winding)
AND swap left*/right* selection pairs in every LOD. That fix keeps a mirror
(CANONICAL WORN FRAME → handedness); with the Blender source, re-export it
with `(x, z, y)` (Rule 12) instead.
3. **The clothing skeleton must be named `DayzTemporarySkeleton`.** ALL vanilla
worn clothing compiles with that name (verified via ODOL
`model_info.skeleton.name` on armbend_dynamic_m + chainmail_m): same 159
bones and hierarchy as OFP2_ManSkeleton, ONLY the name differs — it is the
placeholder the engine swaps for the wearer's skeleton at bind time. With
`skeletonName="OFP2_ManSkeleton"` the engine treats the p3d as having its
OWN skeleton and never re-binds: the item renders as a rigid block anchored
above the player, not animating. The community 114-bone OFP2 template fails
clothing TWICE (bone count and name). Template: `references/model.cfg.template`.
## CANONICAL WORN FRAME (measured on chainmail_m / hoodie_m)
- **-Z = chest/front** · **+X = anatomical LEFT** · **+Y = up** · origin at feet
(ground Y=0); mesh authored over the vanilla A-pose body.
- Measure orientation with ANATOMICAL COMPONENTS, never a global Z histogram
(both ±Z masses are ~55/45 on real clothing — the histogram false-negatives a
180° flip). Reliable signals: front detail components (hoodie drawstrings at
Z −0.14..−0.07), pelvis/spine weighted centroids (vanilla ≈ Z −0.04..−0.08),
`left*` selection centroids on +X.
- Torso clothing spans Y ≈ 0.61..1.66 m. Reference garment for overlay checks:
`dz\characters\tops\chainmail_m.p3d` (body armor, full-body bone set).
- **Handedness (added 2026-10-01).** This frame holds a non-mirrored body in
DayZ's left-handed space. A Blender garment (right-handed; front −Y and left
+X, like the official rig) lands on it with `(x, y, z) → (x, z, y)`: det −1,
faces in Blender order, normals negated (dayz-model-pipeline Rule 12). A
det +1 map mirrors it: `(x, z, −y)` faces +Z (killer #2), and `(−x, z, y)`
faces −Z with `left*` on −X. A mesh that needs "rotate 180° + swap L/R" was
exported with such a map. ArmorHneck's received p3d was the `(x, z, −y)` of
the artist's Blender file (`ArmorHneck_dev\CLAUDE.md`, 2026-08-04). Rotation
+ swap fixes the binding but keeps the mirror: text, logos, buckles and
one-sided straps read reversed. The properly handed fix is a re-export of
the Blender source with Rule 12, with no swap. With only the p3d, reflect it
on the axis its state calls for: a p3d as received from `(x, z, −y)` (facing
+Z, never rotated or swapped) takes `z → −z`; one that already had "rotate
180° + swap L/R", i.e. `(−x, z, y)` like the shipped ArmorHneck, takes
`x → −x` AND the L/R swap undone (`z → −z` on it would face +Z again).
Either reflection moves every point (memory points included), reverses
every face and reflects the normals the same way; `P3D.transform()` with
that diagonal matrix (det −1) does all three.
- **Chiral in-game check [run 2026-10-02, DayZDiag 1.29.163709].** The anatomical test above
rules out a det +1 export only while the L/R selections were never swapped by
hand; a swapped build needs an asymmetric feature. Put an "F" in relief on the
left chest of a test garment (weighted 100 % to `spine3`). Export it once with
Rule 12 and once with the old `(x, z, −y)` + rotate + swap as the control,
wear each with an item in hands and set the camera in front. Pass: the "F"
reads correctly and sits on the side opposite the item hand (items sit on
`RightHand_Dummy`). Expected on the control: the "F" mirrored, on the item
side. On ArmorHneck itself, compare one asymmetric detail against the
artist's Blender file.
Result, on a vest-like test garment worn in the `Armband` slot (binarized,
ODOL skeleton `DayzTemporarySkeleton`, driven by dayz-mcp): the Rule 12
build (`py3d.blender_to_dayz()`), worn by the player with a crowbar in the
right hand, shows the "F" reading correctly on the wearer's left chest,
opposite the crowbar. The control (`ROT_X_NEG90`, every face reversed and
every normal negated, then 180° about +Y) shows the "F" mirrored on the
wearer's right chest. The garment carries only `spine3`, so the control's
L/R swap renamed nothing. dayz-mcp cannot take a worn item off the player,
so the control was worn by a `SurvivorM_Francis` spawned beside the player,
facing the camera with empty hands; its right side was read from its facing.
(claim: CLAIM-CLOTHING-CHIRAL-INGAME) Not covered: ArmorHneck itself, a
garment with left/right-weighted parts, a female body.
## THE CLOTHING CONTRACT (structure of a working wearable)
config.cpp (text!):
- Class inherits a vanilla clothing base for the slot (`Armband_ColorBase`,
`Chainmail`, vest/top bases...). `model=` the GROUND p3d (`*_g.p3d`).
- `class ClothingTypes { male="..._m.p3d"; female="..._f.p3d"; };`
- `inventorySlot[]` names the worn slots. (until 1.29: NBC pieces listed only the primary slot — `"Headgear"`, `"Body"`, `"Legs"`, `"Feet"`, `"Gloves"`.) (since 1.30 Exp: those NBC bases also list secondary slots `"NBCHead"`, `"NBCTop"`, `"NBCPants"`, `"NBCBoots"`, `"NBCGloves"` so they can coexist with regular clothing.)
- `hiddenSelections[]` per model section; textures/materials arrays aligned.
- (since 1.30 Exp) Type metadata lives on `ItemBaseType`: `headSelectionsToHide[]`, `hideSelectionsByinventorySlot`, `varWetMax`, `temperaturePerQuantityWeight`, `EnvironmentWetnessIncrements`. The config keys existed in 1.29 (parsed on `ItemBase`); 1.30 centralizes them on the type. Dual-slot headgear (bandana/shemag) still uses `hideSelectionsByinventorySlot = 1` plus `simpleHiddenSelections[]`.
- Damage rvmats referenced only from healthLevels must be re-added at pack
time (AddonBuilder whitelist gap — see dayz-pbo-build SP-155).
Worn p3d (_m/_f):
- Visual LODs skinned by NAMED SELECTIONS in lowercase = bone names
(leftarm, rightarm, leftforearm, rightforearm, leftupleg, rightupleg,
neck, pelvis, spine, spine3 — more is better: vanilla also weights
shoulder/roll/extra transition bones for smooth joints).
- Per-vertex weight sums MUST be 1.0 (vanilla ODOL encodes raw bytes summing
255). 0/1 plate-rigid weights WORK but articulate coarsely.
- Camo sections: tops/vests use `camoMale` / `camoFemale` alone; ONLY armbands
carry the 4 complexion variants (camoMale_big_a/b + small_a/b). + `zbytek`.
- Empty Geometry LOD with `autocenter=0` = the vanilla worn pattern (keep it).
- Ground `_g`: normal item p3d (Geometry+mass+Component01, memory points,
ViewGeo, FireGeo with a penetration rvmat).
model.cfg:
- Copy `references/model.cfg.template` VERBATIM (CfgSkeletons
DayzTemporarySkeleton, 159 exact pairs, isDiscrete=0) and add one CfgModels
class per p3d (class name == p3d filename) with
`skeletonName="DayzTemporarySkeleton"` and its sections.
## BUILD (staged — see ArmorHneck_dev\tools\build.ps1 as template)
1. Stage the mod to %TEMP% (binarize with -addon=P:\ dies on any broken
third-party config in the tree — "Error 3", names the wrong culprit).
Stage beside it every file the p3d cite outside the mod's prefix, the
vanilla penetration rvmat of the Fire Geometry included, or binarize
embeds those materials EMPTY (dayz-pbo-build SP-155 rules 1 and 5,
corrected 2026-10-03: ArmorHneck's ODOL of 2026-08-04 carries
`dz\data\data\penetration\armor_5mm_plate.rvmat` empty).
2. Full AddonBuilder pass to binarize the p3d (model.cfg NEXT TO the p3d in
the staging; ODOL must come out with skeleton name DayzTemporarySkeleton —
verify with the external ODOL parser, `model_info.skeleton.name`).
3. Assemble pack tree: config.cpp TEXT + model.cfg + ODOL p3d + ALL paa/rvmat
(including damage/destruct) → AddonBuilder `-packonly` (separate -temp!).
4. Verify PBO entries (classname, NO config.bin, ODOL magic) → deploy by hash.
5. Gate = in-game: spawn (registers?) → equip (worn renders on body?) → move
(follows animations?). A worn item CANNOT be validated offline-only.
## DIAGNOSIS LADDER (when a worn item misbehaves)
| Symptom | Cause | Verify with |
|---|---|---|
| Item unknown/unspawnable, no RPT error | config.bin in PBO (killer #1) | spawn gate; PBO entry list |
| Worn = rigid block floating ~1 m, rotated, no anim | skeleton name != DayzTemporarySkeleton (killer #3) | ODOL `model_info.skeleton.name` vs vanilla |
| Worn = EXPLODED separate pieces above head | mesh frame flipped 180° (killer #2) | anatomical component Z-signs vs chainmail |
| Fits but plates misaligned (arms/legs) | rip proportions vs canonical bind | alignment viewer (below) or artist pass |
| Body/arms invisible when equipped | expected: torso-slot hide (Body/Vest classes hide skin) | wear it with the intended slot class |
| Coarse joints while moving | 0/1 plate weights, no transition bones | optional weight-smoothing pass |
| Gas mask worn, still takes bio/gas damage | (since 1.30 Exp) GP5/Airborne `Protection` no longer carries biological/dust_particle_breath; those come from attached `GasMask_Filter` | config `class Protection` vs slot `GasMaskFilter` |
| Custom backpack cannot attach NBC pack | (since 1.30 Exp) backpack `attachments[]` missing `"NBCBag"` (not every vanilla bag has it) | compare `TaloonBag_ColorBase` vs `SmershBag` / `ChildBag_ColorBase` |
**Config-vs-p3d bisection (one config-only rebuild, answers "which side is broken"):**
add two test classes — {your slot class + a VANILLA worn p3d (chainmail)} and
{a vanilla clothing class + YOUR p3d}. Vanilla-p3d-OK + custom-p3d-broken in
both classes = the p3d. Both OK = your main class fields. Both broken = pipeline.
**ODOL parity check vs a debinarized vanilla worn** (external ODOL parser):
skeleton name + 159 bones, per-vertex weight sums, sub_skeleton palette (unused
palette entries with 0 weight are normal in vanilla), sections, ModelInfo flags.
SP-034 caveat: vanilla CHARACTER BODY vertex positions decode corrupt, but
clothing worn p3ds decoded fine (armband, chainmail, hoodie all plausible).
## ALIGNMENT VIEWER (fit plates to the body interactively)
`references/alignment_viewer_extract.py` → JSON of mesh + per-region bone
weights + pivots (+ vanilla chainmail ghost as the placement reference);
`references/alignment_viewer_build.py` → self-contained HTML (embedded
three.js UMD): per-region rotation/offset sliders (weight-masked, around the
region pivot), L/R mirroring, front/back/side views, exports an adjustments
JSON. `references/apply_alignment_json.py` applies that JSON to the MLOD
sources with the EXACT same math (three.js Euler XYZ replicated; cross-check
a sample vertex against the live viewer before batch-applying — err ~1e-8).
Works for arbitrary region sets (arm/leg/abdomen masks, combined selections).
## ARTIST HANDOFF (when a human does the fitting — the usual best route)
`references/export_clothing_fbx.py` (Blender headless + py3d): MLOD worn →
FBX with geometry, UVs (V flipped for Blender), the diffuse as PNG
(ImageToPAA paa→png), and the bone weights carried by a 10-bone WEIGHT-CARRIER
armature (FBX drops loose vertex groups — verified: without an armature
deformer the groups vanish on reimport; with it, round-trip keeps all 10
groups at exact weight sums). Axis map DayZ→Blender: `(x, z, y)`, det −1 and
its own inverse, so the return trip is the same map with faces in the order
they come back and normals negated (dayz-model-pipeline Rule 12); with py3d
≥ 1.8.0 that is `py3d.blender_to_dayz()` on the model rebuilt from the FBX in
Blender coordinates. The artist sees the garment like the official rig: Z up,
front −Y, left +X, faces outward. [OFFLINE MEASURED 2026-10-01, ArmorHneck
`_m`, Blender 5.1.1 + py3d 1.7.0] FBX round trip back to DayZ exact to 2.7e-7 m
with the same face order; `leftarm` centroid x = +0.34, pelvis/spine centroid
y = −0.045; signed volume +0.026 in Blender, i.e. faces outward. Until
2026-10-01 the script used `(x, −z, y)` (det +1) with `py3d.BLENDER_TO_DAYZ`
for the return. The artist then saw a mirror image (front +Y, left +X) with
inward faces (signed volume −0.026), and only that exact inverse brought it
back. A package made by the old script returns through `(x, z, −y)`, i.e.
`transform(py3d.ROT_X_NEG90)` (the name py3d 1.8.0 gives that matrix; the
old `py3d.BLENDER_TO_DAYZ` still works but is deprecated and warns), never
through Rule 12 or `py3d.blender_to_dayz()`, which would land it facing +Z,
mirrored and inside-out. ALWAYS verify by reimporting the FBX in a clean
scene (counts + vgroups + weight sums) before shipping.
Package: FBX m/f + PNG + the MLOD p3ds + model.cfg + README from
`references/LEEME-ARTISTA.template.md` (states the three do-not-undo fixes,
the group-preservation rule, and the return flow). Scripts carry the
ArmorHneck paths — adapt per garment.
## PROVENANCE
ArmorHneck case (full trail): `ArmorHneck_dev\CLAUDE.md`, research workspace
`%TEMP%\armorhneck_research_ws\REPORT.md` (Codex dual research: canonical frame
measurement + transform prescription). Cross-refs: dayz-characters §2026-08-03
(same finding, humanoid context), dayz-pbo-build SP-155 (staged build + rvmat
gap), dayz-test-ingame (launch/lifecycle), external ODOL parsing (converter not distributed).
## DayZ 1.30 Exp (build 1.30.164014)
Worn-mesh killers #1–#3, the canonical frame, `DayzTemporarySkeleton`, and the
staged packonly build are unchanged. What 1.30 adds is **config/script contract**
around slots, protection, wetness, and one classname rename. Details and `[EXACT]`
blocks: [dayz-1-30-clothing.md](references/dayz-1-30-clothing.md).
### What changes
- **NBC secondary slots.** Vanilla NBC hood/jacket/pants/boots/gloves list both
the primary clothing slot and `NBCHead` / `NBCTop` / `NBCPants` / `NBCBoots` /
`NBCGloves` (`CfgSlots` in `exp\scripts\scripts\config.cpp:3124-3159`).
- **`NBCBag` on some backpacks, not all.** `TaloonBag_ColorBase`, `TortillaBag`,
`DryBag_ColorBase`, `HuntingBag`, `MountainBag_ColorBase`, `AssaultBag_ColorBase`,
`Attack2Bag_ColorBase`, `CoyoteBag_ColorBase`, `AliceBag_ColorBase` append
`"NBCBag"` next to `"Chemlight"`, `"WalkieTalkie"`, `"Backpack_1"`. `SmershBag`
and `ChildBag_ColorBase` do not. The pack item is scripted as `NBCPack_ColorBase`
(`Container_Base`); its `CfgVehicles` entry is not in this extract (`[UNVERIFIED]`).
- **GP5 / Airborne protection moved to the filter.** (until 1.29: `class Protection`
on both masks included `biological = 1` and `dust_particle_breath = 1`.) (since
1.30 Exp: those two keys are omitted; only `dust_particle_eyes = 1` remains.
`GetProtectionLevel` reads the attached `GasMask_Filter`. The sealed `GasMask`
class still has full passive Protection. There is no class named `FilterBase`.)
- **`ItemBaseType`** parses `headSelectionsToHide`, `hideSelectionsByinventorySlot`,
`varWetMax`, `temperaturePerQuantityWeight`, `EnvironmentWetnessIncrements`.
`ItemBase.GetHeadHidingSelection` / `HidesSelectionBySlot` / `GetWetMax` read
the type. Defaults for soaking/drying live on `Inventory_Base`.
- **Shoe `itemSize` width.** Most footwear bases went from width 4 to width 3
(height also shrank on several boots). `LeatherShoes_ColorBase` stays `{4,2}`.
- **`WaterPouch_*` → `Waterskin_*`** in `CfgVehicles`. Script stub
`WaterPouch_ColorBase: Bottle_Base{}` remains. Craft: 2 `TannedLeather` +
`LeatherSewingKit` (−10) → `Waterskin_Natural`. De-craft: empty
`Waterskin_ColorBase` + blade (−12 HP) → 1 `TannedLeather`.
- **`CraftArmbandFlag.CanDo`** no longer requires `IsEmpty()`; it only rejects a
flag that is already an attachment. Extra knives (`Jambiya`, `Scimitar`,
`DonerKnife`, `Akinaka`) are valid tools.
- **Wash head / wet clothing.** New `ActionWashHead*` / `ActionWetClothing*` apply
per-slot wetness and `GetThermalBiasHandler().Add(-THERMAL_BIAS_WASH_HEAD_DECREMENT)`.
`[CHANGELOG]` known issue: bottles can still Wash Head while frozen;
`ActionWashHeadItemContinuous.ActionCondition` does not call `GetIsFrozen()`.
- **Nasdara/Badlands garments** (Kameez, Shalwar, Pakol, DustGoggles, …) have
scripts under `ItemBase\Clothing\` but no `characters_*` config in this public
extract (`[UNVERIFIED]` heatIsolation / cargo).
### What breaks for a 1.29 clothing/gear mod
1. NBC clothing that lists only the primary slot cannot layer with regular
Headgear/Body/Legs/Feet/Gloves.
2. Custom backpacks without `"NBCBag"` cannot take the NBC pack attachment.
3. GP5/Airborne clones with hardcoded `biological` on the mask, or that skip
`attachments[] = {"GasMaskFilter"}`, will not match 1.30 filter-gated protection.
4. `CreateObject("WaterPouch_Natural")` / recipes naming `WaterPouch_*` fail:
the config class is `Waterskin_Natural`.
5. Shoe mods still at width 4 occupy more grid than vanilla 1.30 footwear.
6. Dual-slot headgear that omits `headSelectionsToHide[]` /
`hideSelectionsByinventorySlot` will clip or hide the wrong head selections.
### Migration checklist
- [ ] NBC pieces: `inventorySlot[]` includes the matching `NBC*` slot.
- [ ] Backpacks that should carry the NBC pack: `attachments[]` includes `"NBCBag"`.
- [ ] Filter masks: `attachments[] = {"GasMaskFilter"}`; biological protection
lives on `GasMask_Filter`, not on the mask `Protection` block.
- [ ] Declare `headSelectionsToHide[]` / `varWetMax` / optional
`EnvironmentWetnessIncrements` on the clothing class (read via `ItemBaseType`).
- [ ] Replace `WaterPouch_*` with `Waterskin_*` (optional 1.29 alias: empty
`WaterPouch_Natural: Waterskin_Natural { scope = 1; }` `[DESIGN]`).
- [ ] Recheck shoe `itemSize[]` if you balanced against vanilla width 4.
- [ ] Armband-from-flag: `CanDo` must not assume `IsEmpty()`.
- [ ] Gate remains in-game: spawn → equip → move (killers #1–#3 unchanged).