Skip to content

ItemArrestStartAsset and ItemArrestEndAsset — Handcuffs and Arrest System

Overview

The arrest system is split across two paired asset types: ItemArrestStartAsset (handcuffs) and ItemArrestEndAsset (handcuff keys). Together they implement a player restraint mechanic where one player can arrest (restrain) another, restricting their movement, camera, and interactions. The restrained player can be freed by another player using a matching key, or can attempt to escape by mashing movement keys.

Both assets extend ItemAsset directly. They are not clothing, weapons, or barricades — they are specialized interaction items with dedicated Useable classes: UseableArrestStart and UseableArrestEnd.

Inheritance Chains

Asset
  └── ItemAsset
        └── ItemArrestStartAsset

Asset
  └── ItemAsset
        └── ItemArrestEndAsset

Both branch directly from ItemAsset. No intermediate base classes. The arrest pair is the only item system in the SDK that uses two separate asset types with a direct cross-reference (the key's _recover field points to the handcuff's item ID).

ItemArrestStartAsset — Handcuffs

Fields

Use Audio (_use)

csharp
protected AudioClip _use;
public AudioClip use => _use;

The sound played when handcuffs are applied to the target player. Loaded from the Unity asset bundle:

csharp
_use = p.bundle.load<AudioClip>("Use");

If the bundle doesn't contain a "Use" audio clip, _use is null. The UseableArrestStart should null-check before playing.

Strength (_strength)

csharp
protected ushort _strength;
public ushort strength => _strength;

The strength value determines how long the arrest lasts and/or how difficult it is for the arrested player to escape. The exact interpretation depends on the game mode config:

  • Duration model: arrestDuration = _strength * config.arrestDurationMultiplier. Each point of strength adds a fixed duration (e.g., 1 second per point).
  • Escape difficulty model: Each escape key press reduces remaining arrest time by baseEscapeAmount / _strength. Higher strength means each key press removes less time.
  • Hybrid model: Strength affects both duration AND escape speed.

Parsed from .dat key Strength:

csharp
_strength = p.data.ParseUInt16("Strength");

As a ushort, strength ranges from 0 to 65535. A strength of 0 would mean the arrest has zero duration (instant release) or infinite escape difficulty (division by zero) depending on the model.

shouldFriendlySentryTargetUser

csharp
public override bool shouldFriendlySentryTargetUser => true;

This is a hardcoded override from ItemAsset. When true, friendly sentry guns will target and shoot the player holding this item. The rationale: a player carrying handcuffs is considered hostile — they could be about to arrest a friendly player. The sentry preemptively targets them.

This is the only item type that unconditionally overrides shouldFriendlySentryTargetUser to true. Most items default to false (sentries ignore friendly players holding them). The override is hardcoded — modders cannot change it without code modifications.

PopulateAsset

csharp
public override void PopulateAsset(in PopulateAssetParameters p)
{
    base.PopulateAsset(in p);

    _use = p.bundle.load<AudioClip>("Use");
    _strength = p.data.ParseUInt16("Strength");
}

internal override void BuildCargoData(CargoBuilder builder)
{
    base.BuildCargoData(builder);

    CargoDeclaration data = builder.GetOrAddDeclaration("ArrestStart");
    data.Append("GUID", GUID);
    data.Append("Strength", strength);
}

The ArrestStart Cargo table exports GUID and Strength.

ItemArrestEndAsset — Handcuff Keys

Fields

Use Audio (_use)

csharp
protected AudioClip _use;
public AudioClip use => _use;

The sound played when handcuffs are removed from the target player. Loaded identically to the start asset:

csharp
_use = p.bundle.load<AudioClip>("Use");

Recover (_recover)

csharp
protected ushort _recover;
public ushort recover => _recover;

The item ID of the matching ItemArrestStartAsset that this key unlocks. The key only works on handcuffs with this specific ID. This creates a pairing system: a specific handcuff type requires a specific key type.

Parsed from .dat key Recover:

csharp
_recover = p.data.ParseUInt16("Recover");

If _recover is 0, the key matches no handcuff type and cannot unlock anything. The BuildDescription method guards against this:

csharp
if (_recover != 0)
{
    ItemArrestStartAsset arrestStartAsset = Assets.find(EAssetType.ITEM, _recover) as ItemArrestStartAsset;
    if (arrestStartAsset != null)
    {
        builder.Append(..., DescSort_Important);
    }
}

Description UI

csharp
public override void BuildDescription(ItemDescriptionBuilder builder, Item itemInstance)
{
    base.BuildDescription(builder, itemInstance);

    if (!builder.HasFlag(EItemDescriptionFlags.Uncategorized))
        return;

    if (_recover != 0)
    {
        ItemArrestStartAsset arrestStartAsset = Assets.find(EAssetType.ITEM, _recover) as ItemArrestStartAsset;
        if (arrestStartAsset != null)
        {
            builder.Append(
                PlayerDashboardInventoryUI.localization.format(
                    "ItemDescription_ArrestEnd_UnlocksItem",
                    "<color=" + Palette.hex(ItemTool.getRarityColorUI(arrestStartAsset.rarity)) + ">"
                    + arrestStartAsset.itemName + "</color>"
                ),
                DescSort_Important
            );
        }
    }
}

The tooltip displays:

Unlocks: [Handcuff Name]

The handcuff name is colorized using its rarity color. If the _recover ID is 0 or doesn't resolve to a valid ItemArrestStartAsset, no line is displayed — the key's tooltip gives no indication of what it unlocks, making it effectively useless.

The Assets.find(EAssetType.ITEM, _recover) call looks up the referenced handcuff asset in the registry. If the referenced asset doesn't exist (deleted item, missing mod), arrestStartAsset is null and the line is skipped.

PopulateAsset and Cargo

csharp
public override void PopulateAsset(in PopulateAssetParameters p)
{
    base.PopulateAsset(in p);

    _use = p.bundle.load<AudioClip>("Use");
    _recover = p.data.ParseUInt16("Recover");
}

internal override void BuildCargoData(CargoBuilder builder)
{
    base.BuildCargoData(builder);

    CargoDeclaration data = builder.GetOrAddDeclaration("ArrestEnd");
    data.Append("GUID", GUID);
    data.Append("Recover", recover);
}

The ArrestEnd Cargo table exports GUID and Recover.

Arrest State Machine

The arrest system manages a player's restrained state through the PlayerLife component:

Arrest State Properties

isArrested: bool
arrestStartAssetId: ushort (the handcuff item ID currently applied)
arrestDuration: float (remaining time until auto-release)

Applying Arrest (UseableArrestStart)

  1. Target validation: The arresting player must be facing a target player within a configurable range.
  2. Raycast check: A raycast from the arresting player's viewpoint confirms the target is valid.
  3. State check: The target must not already be arrested. Double-arrest is not possible.
  4. Handcuff application:
    • The target player's isArrested is set to true.
    • The target's arrestStartAssetId is set to the handcuff item's ID.
    • The target's movement speed is set to 0.
    • The target's camera is forced to first-person view.
    • The target's weapon/item switching is blocked.
    • The target's pause menu is limited to quit only (no respawn, no suicide).
    • The handcuff item is consumed (stack decremented or removed).
  5. Audio: The _use AudioClip plays.

Restrained Player State

While arrested, the player:

  • Cannot move (movement speed = 0).
  • Cannot look around freely (camera locked to first-person).
  • Cannot switch weapons or use items.
  • Cannot access most UI (inventory, crafting, map may be restricted).
  • Can still chat (text and voice).
  • Can attempt to escape by pressing movement keys.

Removing Arrest (UseableArrestEnd)

  1. Target validation: The releasing player must be facing an arrested player within range.
  2. Key matching:
    if (keyAsset.recover == targetPlayer.arrestStartAssetId)
        // Match! Remove handcuffs.
    else
        // No match. Key not consumed. Nothing happens.
  3. Handcuff removal:
    • The target player's isArrested is set to false.
    • Movement speed, camera, and UI restrictions are lifted.
    • The key item is consumed.
  4. Audio: The _use AudioClip plays.

Arrest Escape (Key Mashing)

The arrested player can attempt to escape by pressing movement keys (WASD). Each key press reduces the remaining arrest duration:

arrestDuration -= escapeAmount
if (arrestDuration <= 0)
    releaseArrest()

The escape amount per key press is typically baseEscapeAmount / handcuffAsset.strength. Higher strength handcuffs reduce the per-press escape amount, making escape harder.

The escape mechanic is entirely client-side prediction with server validation. The client counts key presses and sends the count to the server, which deducts the appropriate time based on the handcuff's strength and the server's escape rate config.

Auto-Release

If arrestDuration reaches 0 (whether from key mashing or time-based expiration), the arrest ends automatically. The handcuffs are not returned to the player — they "break" and are lost. The key mashing escape simulates breaking free from the handcuffs.

Pairing System

The key-handcuff pairing relies on direct ID matching:

HandcuffKeyPairs?
Handcuffs ID 58500Key with Recover 58500Yes
Handcuffs ID 58500Key with Recover 58501No
Handcuffs ID 58500Key with Recover 0No (key has no target)

Multiple Keys for One Handcuff

If multiple key types have the same _recover value, any of them can unlock that handcuff type. This allows:

  • "Universal handcuff key" items that unlock all handcuffs (by having multiple key assets all referencing the same handcuff ID).
  • Difficulty tiers: easy-to-obtain handcuffs with common keys, hard-to-obtain handcuffs with rare keys.

However, one key can only unlock one handcuff type (one _recover value). A key cannot match multiple handcuff IDs.

Partial Overlap

Two handcuff types can share a key:

  • Handcuff A (ID 58500), Handcuff B (ID 58501)
  • Key X (Recover 58500): unlocks A only.
  • Key Y (Recover 58501): unlocks B only.
  • Key Z (Recover 58500): also unlocks A. Now A has two key types that can unlock it.

This flexibility allows modders to create complex arrest economies where different factions or NPCs distribute different key types.

Sentry Gun Interaction

The shouldFriendlySentryTargetUser override has specific gameplay implications:

  • Friendly sentries: Turrets placed by allies will target a player holding handcuffs, treating them as hostile.
  • Preemptive targeting: The sentry doesn't wait for the player to actually arrest someone — carrying handcuffs is enough to trigger hostility.
  • Unequipped handcuffs: If the handcuffs are in the backpack (not held), the shouldFriendlySentryTargetUser check still applies because all inventory items are checked, not just the held item.

This behavior exists to prevent friendly-fire arrest abuse: a player joining a group, equipping handcuffs from their inventory, and arresting group members while friendly sentries ignore them. The override closes this loophole.

Network and Save Behavior

Arrest State Replication

A player's arrest state is replicated to all clients via the PlayerLife component. Changes to isArrested, arrestStartAssetId, and arrestDuration are sent via the player state update channel (typically every tick or every few ticks for time-sensitive state).

The arresting player's client sends an arrest request to the server. The server validates the request (range, target, no existing arrest) and applies the state change. The state is then broadcast to all clients.

Persistence Across Sessions

Arrest state is NOT persisted across server restarts. If a server restarts while a player is arrested:

  • The player respawns unrestrained (or at a spawn point).
  • The handcuffs and key items are lost (consumed during the arrest/release).
  • Partial escape progress is lost.

Death While Arrested

If an arrested player is killed (by another player, zombie, or environmental damage), the arrest ends:

  • The player dies and respawns unrestrained.
  • The handcuffs that were applied are lost.
  • The arresting player does not get the handcuffs back.

Modding Guide

Creating Handcuffs

ID 58500
ItemName "Handcuffs"
ItemDescription "Restrains a player."
Rarity Rare
Size_X 1
Size_Y 2
Slot Primary
Strength 10

Creating a Matching Key

ID 58501
ItemName "Handcuff Key"
ItemDescription "Unlocks standard handcuffs."
Rarity Uncommon
Size_X 1
Size_Y 1
Slot None
Recover 58500

The key's Recover value (58500) matches the handcuff's ID. This key ONLY unlocks handcuffs with ID 58500.

Creating High-Security Handcuffs

ID 58502
ItemName "Reinforced Handcuffs"
Rarity Epic
Size_X 1
Size_Y 2
Slot Primary
Strength 50

Higher strength means longer arrest duration and/or harder escape. The matching key:

ID 58503
ItemName "Reinforced Handcuff Key"
Rarity Rare
Size_X 1
Size_Y 1
Slot None
Recover 58502

Creating a Universal Key (Multiple Entries)

Since one key can only reference one handcuff ID, a "universal" key requires creating multiple key assets:

ID 58504
ItemName "Universal Handcuff Key"
Recover 58500  // Standard handcuffs

ID 58505
ItemName "Universal Handcuff Key"
Recover 58502  // Reinforced handcuffs

Both key items have the same name and appearance but different IDs and Recover values. The player would need to carry both key types to unlock both handcuff types. A mod or plugin could implement a true universal key that checks all handcuff types.

Common Pitfalls

  1. Zero recovery ID: A key with Recover 0 (the default if the key is omitted) cannot unlock any handcuffs. The tooltip shows no "Unlocks" line. Always set Recover to a valid handcuff ID.

  2. Recover pointing to non-handcuff item: If Recover is set to a weapon ID, food ID, or any non-ItemArrestStartAsset item, Assets.find(...) as ItemArrestStartAsset returns null. The key is non-functional. Validate that Recover targets an actual handcuff.

  3. Incorrect tooltip: The tooltip looks up the handcuff asset to display its name. If the handcuff asset is missing (deleted, unloaded mod), the tooltip shows nothing. The key still functions (the ID match happens at runtime, independent of the tooltip lookup) but the player has no indication of what the key does.

  4. Strength 0: A handcuff with Strength 0 may cause division-by-zero in escape calculations or result in instant release. Always set a positive strength.

  5. Infinite arrest: If the game mode config or escape calculation makes arrestDuration never reach 0 (e.g., strength is absurdly high and escape rate is zero), the player is permanently arrested. Include an auto-release timeout or ensure escape is always possible.

  6. Double-arrest not possible: A player already arrested cannot be arrested again. The arrest attempt fails silently. If you want re-arrest mechanics, the first arrest must end (via release, escape, or death) before a new arrest can be applied.

  7. Sentry hostility surprise: Players carrying handcuffs in a friendly base will be shot by their own sentries. Warn players in the item description or game rules that handcuffs trigger sentry hostility.

  8. Arrest range abuse: The arrest requires the player to be close to the target. If the range is configured too large, players can arrest from unrealistic distances. If too small, arrest is impossible against moving targets. Tune the range config for expected gameplay.

Escape Mechanics Deep Dive

Key Press Tracking

The escape system tracks individual key presses from the arrested player. Each press of a movement key (W, A, S, D) counts as one escape attempt. The system monitors raw input, not movement output (the player can't actually move while arrested — input goes to the escape counter instead).

Escape Progress Calculation

Each key press reduces the remaining arrest duration:

remainingDuration -= escapeAmount

Where:

escapeAmount = config.baseEscapeRate / handcuffAsset.strength

Example: baseEscapeRate = 1.0, strength = 10, each press removes 0.1 seconds of arrest time. An arrest duration of 30 seconds requires 300 key presses to escape.

Example: baseEscapeRate = 1.0, strength = 50, each press removes 0.02 seconds. An arrest of 30 seconds requires 1,500 key presses — effectively inescapable by mashing alone.

Input Rate Limiting

The escape input has rate limiting to prevent macro/autoclicker abuse:

  • Maximum key presses tracked per second: typically 10-15 (configurable).
  • Presses beyond the limit are discarded.
  • The limit prevents players from binding a turbo button to escape instantly.

Client-Side Prediction

The escape input is tracked client-side and sent to the server in batches. The client predicts escape progress for responsive UI feedback (progress bar, screen shake). The server validates the input rate and applies the actual escape progress.

If the client sends more key presses than the rate limit allows, the server caps the count. This prevents speed-hack style escape acceleration. The client-side escape bar may jump backward if the server rejects excess inputs.

Escape Failure and Re-Arrest

If a player escapes and is immediately re-arrested, the escape counter resets. There is no persistence of partial escape progress across arrests. Each arrest is a fresh timer.

Some game mode configs implement an "escape immunity" period after successful escape — the player cannot be re-arrested for 5-10 seconds. This prevents arrest-spam griefing where an arresting player re-applies handcuffs the moment the target breaks free.

Arrest in PvP and Roleplay Contexts

Law Enforcement Roleplay

In roleplay servers, the arrest system models law enforcement interactions:

  • Police officers carry handcuffs and keys.
  • Criminals are arrested rather than killed.
  • Arrested players are transported to a jail or processing area.
  • The arrest duration serves as the "sentence."
  • Keys are restricted to law enforcement roles via plugin permissions.

Raid and Capture Mechanics

In PvP raid servers, arrest enables non-lethal base defense:

  • Defenders arrest attackers instead of killing them.
  • Arrested attackers are moved outside the base and released.
  • This creates a "capture and release" dynamic rather than a deathmatch.

The arrest system's shouldFriendlySentryTargetUser override plays a role here: defenders placing sentries AND carrying handcuffs may be targeted by their own sentries. Base designs must account for this — sentry placement must avoid line-of-sight to defenders holding handcuffs.

Arrest Economy

Handcuffs and keys create an economic loop:

  • Handcuffs are consumed on use (one-time use).
  • Keys are consumed on use (one-time use).
  • Both must be crafted or purchased.
  • Arresting costs one handcuff per target.
  • Releasing costs one key per target.
  • Escape costs nothing (just time and effort).

This creates supply/demand dynamics: a faction that arrests many players needs a steady supply of handcuffs. A faction that frees arrested allies needs keys. Modders can tune the crafting costs of each to balance arrest frequency.

Comparison: Arrest vs. Other Restraint Systems

FeatureArrest SystemAdmin FreezeKill Bind
Who appliesAny player with handcuffsAdmin onlySelf only
Duration controlStrength field + game configAdmin-set timeInstant
Escape possibleYes (key mashing)NoN/A
Consumed on useHandcuffs consumedNo itemNo item
Key required for friend releaseYes (matching key)No (admin command)N/A
Death behaviorReleases on deathReleases on deathPlayer dies
Sentry hostilityYes (hardcoded)NoNo
Network syncVia PlayerLifeVia admin RPCClient-side
PersistenceSession onlySession onlyN/A

Historical Context: Why Two Separate Asset Types

The arrest system's split into ItemArrestStartAsset and ItemArrestEndAsset (rather than a single arrest item with a mode field) follows the SDK's pattern of one-asset-type-per-useable-class. Each Useable class is paired with exactly one asset type:

  • UseableArrestStartItemArrestStartAsset
  • UseableArrestEndItemArrestEndAsset

This is consistent with other item pairs in the SDK (e.g., ItemBarricadeAsset/ItemStructureAsset for placeables, ItemGunAsset/ItemMagazineAsset for firearms). The SDK avoids multi-mode items at the asset level, instead using separate asset types that reference each other.

The _recover cross-reference (key points to handcuff by item ID) is the same pattern used by ItemArrestEndAsset for pairing with its matching start asset. This ID-based pairing is simpler than GUID-based pairing (used by ItemCurrencyAsset.Entries) because arrest items are typically from the same mod and don't need cross-mod compatibility.