Components reference
Every component key — lockable, resistance, gagging, shock, gps, choking,
adjustable, blinding, plus the two often-forgotten ones: ownership and
built_in_lock. Includes which fields are inert.
A bondage item is a single JSON file at data/<namespace>/tiedup_items/<item>.json. The whole
schema is plain text — no Java, no special tooling beyond the .glb mesh it points at. This
page documents every field the game actually reads.
The smallest valid definition — required fields only:
{ "type": "tiedup:bondage_item", "display_name": "Ropes", "model": "tiedup:models/gltf/binds/ropes.glb", "regions": ["ARMS"]}| Field | Type | Default | Notes |
|---|---|---|---|
type | string | — | Required. Must be exactly tiedup:bondage_item. Any other value rejects the whole definition. |
display_name | string | — | Required, non-empty. Used as the fallback name when translation_key is absent. |
model | RL | — | Required. Resource location of the .glb mesh. Convention: tiedup:models/gltf/<category>/<item>.glb. |
slim_model | RL | none | Optional Alex-arm mesh variant. Invalid RL → ignored (warning in the log). |
regions | string[] | — | Required. One or more body regions this item occupies (uppercased; case-insensitive). |
blocked_regions | string[] | = regions | Regions this item prevents others from using. Omit it and it defaults to the occupied regions — not to “nothing”. |
translation_key | string | none | Lang key for the name. If set, the name is translatable; otherwise display_name is shown literally. |
pose_priority | int | 0 | On joints two items both animate, the higher priority wins. Author binds usually use ≥ 1 (shipped binds use 30, collars 5). |
creator | string | none | Author name, shown in the tooltip. |
The mesh’s baked texture renders. A tinted mesh renders texture × tint.
| Field | Type | Default | Notes |
|---|---|---|---|
supports_color | bool | false | Marks the item as dye-able / tintable. |
tint_channels | object | {} | Map of GLB material name → hex colour (#RRGGBB, with or without #). Bad hex → that channel skipped (warning in the log). |
.glb when you export from Blender.tintable_* and list them in tint_channels.| Field | Type | Default | Notes |
|---|---|---|---|
icon | RL | tiedup:item/data_driven_item | Inventory-sprite model. |
"pose_type": "STANDARD"| Value | Effect |
|---|---|
STANDARD | Default. Normal upright biped render. |
DOG | Lowers the camera and leash for a floor/dog pose — prefer the explicit modern fields camera_offset + leash_anchor. ⚠️ Partial The camera/leash drop applies, but the third-person dog/quadruped pose is not rendered yet — the figure still stands upright to onlookers. |
These replace the special-cased pose_type branches with explicit, composable controls.
| Field | Type | Default | Notes |
|---|---|---|---|
hides_arms | bool | false | ⚠️ Partial Drives full NPC immobilization, and is the modern way to author sacks/wraps. The third-person arm-hiding is not active yet (the wearer’s arms still show under a full wrap/latex enclosure to onlookers); the immobilization and first-person mitten behaviour apply, the visual arm removal does not. |
camera_offset | {x,y,z} | 0,0,0 | First-person camera offset. Missing axes default to 0.0. Lower y for floor/dog poses. |
leash_anchor | double | 1.3 | Leash attach height (Y). Lower it for floor poses (the dogbinder uses 0.35). |
"movement_style": "CRAWL"A worn item can change how the player physically moves. Server-authoritative and synced to clients. When several styled items are worn, the highest severity wins.
| Value | Severity | Speed × | Jump |
|---|---|---|---|
WADDLE | 1 | 0.6 | allowed |
SHUFFLE | 2 | 0.4 | disabled |
HOP | 3 | 0.35 | disabled (auto-hop) |
CRAWL | 4 | 0.2 | disabled (swim-like hitbox) |
Values are case-insensitive; an unknown value is ignored (with a warning in the log).
| Field | Type | Default | Notes |
|---|---|---|---|
movement_modifier | {speed_multiplier, jump_disabled} | style defaults | Overrides the style’s defaults. Ignored unless movement_style is also set, and only the winning item’s modifier applies. |
"movement_style": "WADDLE","movement_modifier": { "speed_multiplier": 0.5, "jump_disabled": true }| Field | Type | Default | Notes |
|---|---|---|---|
can_attach_padlock | bool | true | The real per-item padlock gate. Defaults true unconditionally (not “true if lockable”). Organic items set it false. |
lockable | bool | true | ⚠️ Partial For worn items this is not consumed — only furniture reads it. Worn-item locking = can_attach_padlock + the lockable component. |
escape_difficulty | int | 0 | ⚠️ Partial Fallback resistance, used only when the item has no resistance component. Most shipped items carry components.resistance (which wins), but a few rely on this field — e.g. leather_mittens (20) and test_cloth (50) have no resistance component, so their top-level escape_difficulty is live. |
Usable in both regions and blocked_regions (uppercased, case-insensitive). Unknown values are
skipped individually (with a warning in the log); if all are invalid the item is rejected.
HEAD EYES EARS MOUTH NECK TORSO ARMSHANDS FINGERS WAIST LEGS FEET TAIL WINGScomponents blockcomponents{} holds the gameplay behaviours (gag, lock, shock, GPS…). Unknown component keys are
ignored (with a warning in the log). The full field-by-field reference lives on its own page:
Components reference
Every component key — lockable, resistance, gagging, shock, gps, choking,
adjustable, blinding, plus the two often-forgotten ones: ownership and
built_in_lock. Includes which fields are inert.
Quick orientation on the two most-missed components:
ownership — owner registration, removal alerts, owner/nickname tooltips. Used by the 5 shipped collars.built_in_lock — “permanently locked, no padlock” mechanic; blocks unequip and denies padlocks. Used by the 8 organic items (slime/vine/web/tape).animations blockanimations{} binds Epic Fight clips to the item — idle/walk/struggle overlays, equip/unequip
one-shots, and equip/unequip sounds. Full reference on its own page:
Animations reference
living_motions (FULL_BODY vs OVERLAY + joints), on_equip / on_unequip one-shots, and the
properties unlocks (pose_modifier, speed/time modifiers, events).
cloth block 🖥️ Client / singleplayer onlyTop-level cloth declares one or more verlet cloth strands (cape, dangling tail, pendant) that
physically simulate on top of the item. The value is a single object (one strand) or an array
of objects (several). It is client-side cosmetic render only — no gameplay effect, no server sync.
| Field | Type | Default | Notes |
|---|---|---|---|
mesh | RL | — | Required. Cloth-mesh asset id (rig soft-body JSON, not a .glb); must carry a cloth_info block. Unregistered/missing → strand skipped (warning in the log, no crash). |
parent_joint | string | — | Required. Biped joint the strand anchors to (case-sensitive, e.g. Torso). Unknown → skipped (warning in the log). |
texture | RL | — | Required, full namespace:path. The cloth render texture. A strand with no valid texture is dropped (no skin fallback). |
collider_preset | string | BIPED | BIPED or BIPED_SLIM only. Unknown → falls back to BIPED (warning in the log). |
gravity | float | — | ⚠️ Partial Parsed but ignored — recorded for forward-compat; no per-strand gravity hook exists yet. |
These look authorable but do nothing. Don’t ship them expecting an effect.
| Key | Status | What actually happens |
|---|---|---|
resistance_id (top-level) | ❌ Planned | Never read. A copy-trap from stale examples. Use components.resistance.id. |
color_variants (top-level) | ❌ Planned | Never read. Also from stale examples. |
animation_bones (top-level) | ❌ Planned | Ignored (warning in the log) — a PlayerAnimator-era dead key. Drop it. |
animation_source | ❌ Planned | Read and stored but has no effect (GLB-embedded clips aren’t played; animation runs via EF-JSON). Treat as legacy. |
A complete, copy-pasteable bind that exercises the common fields. Real shipped values
(adapted from armbinder.json and dogbinder.json):
{ "type": "tiedup:bondage_item", "display_name": "Armbinder", "translation_key": "item.tiedup.armbinder", "model": "tiedup:models/gltf/binds/armbinder.glb", "regions": ["ARMS"],
"pose_type": "STANDARD", "pose_priority": 30,
"can_attach_padlock": true, "supports_color": false,
"animations": { "living_motions": { "IDLE": { "animation": "tiedup:armbinder_idle", "mode": "OVERLAY", "joints": ["Shoulder_R", "Arm_R", "Elbow_R", "Hand_R", "Shoulder_L", "Arm_L", "Elbow_L", "Hand_L"] }, "WALK": { "animation": "tiedup:armbinder_walk", "mode": "OVERLAY", "joints": ["Shoulder_R", "Arm_R", "Elbow_R", "Hand_R", "Shoulder_L", "Arm_L", "Elbow_L", "Hand_L"] }, "STRUGGLE": { "animation": "tiedup:armbinder_struggle", "mode": "OVERLAY", "joints": ["Shoulder_R", "Arm_R", "Elbow_R", "Hand_R", "Shoulder_L", "Arm_L", "Elbow_L", "Hand_L"] } }, "on_equip": "tiedup:armbinder_equip_oneshot" },
"components": { "lockable": {}, "resistance": { "id": "armbinder" } }}And a floor/dog-pose variant using the modern movement & camera fields:
{ "type": "tiedup:bondage_item", "display_name": "Dogbinder", "translation_key": "item.tiedup.dogbinder", "model": "tiedup:models/gltf/binds/dogbinder.glb", "regions": ["ARMS"],
"pose_type": "DOG", "camera_offset": { "y": -0.6 }, "leash_anchor": 0.35, "movement_style": "CRAWL",
"pose_priority": 30, "can_attach_padlock": true,
"components": { "lockable": {}, "resistance": { "id": "armbinder" } }}