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
└── ItemArrestEndAssetBoth 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)
- Target validation: The arresting player must be facing a target player within a configurable range.
- Raycast check: A raycast from the arresting player's viewpoint confirms the target is valid.
- State check: The target must not already be arrested. Double-arrest is not possible.
- Handcuff application:
- The target player's
isArrestedis set to true. - The target's
arrestStartAssetIdis 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).
- The target player's
- Audio: The
_useAudioClip 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)
- Target validation: The releasing player must be facing an arrested player within range.
- Key matching:
if (keyAsset.recover == targetPlayer.arrestStartAssetId) // Match! Remove handcuffs. else // No match. Key not consumed. Nothing happens. - Handcuff removal:
- The target player's
isArrestedis set to false. - Movement speed, camera, and UI restrictions are lifted.
- The key item is consumed.
- The target player's
- Audio: The
_useAudioClip 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:
| Handcuff | Key | Pairs? |
|---|---|---|
| Handcuffs ID 58500 | Key with Recover 58500 | Yes |
| Handcuffs ID 58500 | Key with Recover 58501 | No |
| Handcuffs ID 58500 | Key with Recover 0 | No (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
shouldFriendlySentryTargetUsercheck 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 10Creating a Matching Key
ID 58501
ItemName "Handcuff Key"
ItemDescription "Unlocks standard handcuffs."
Rarity Uncommon
Size_X 1
Size_Y 1
Slot None
Recover 58500The 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 50Higher 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 58502Creating 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 handcuffsBoth 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
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 setRecoverto a valid handcuff ID.Recover pointing to non-handcuff item: If
Recoveris set to a weapon ID, food ID, or any non-ItemArrestStartAssetitem,Assets.find(...) as ItemArrestStartAssetreturns null. The key is non-functional. Validate thatRecovertargets an actual handcuff.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.
Strength 0: A handcuff with
Strength 0may cause division-by-zero in escape calculations or result in instant release. Always set a positive strength.Infinite arrest: If the game mode config or escape calculation makes
arrestDurationnever 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.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.
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.
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 -= escapeAmountWhere:
escapeAmount = config.baseEscapeRate / handcuffAsset.strengthExample: 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
| Feature | Arrest System | Admin Freeze | Kill Bind |
|---|---|---|---|
| Who applies | Any player with handcuffs | Admin only | Self only |
| Duration control | Strength field + game config | Admin-set time | Instant |
| Escape possible | Yes (key mashing) | No | N/A |
| Consumed on use | Handcuffs consumed | No item | No item |
| Key required for friend release | Yes (matching key) | No (admin command) | N/A |
| Death behavior | Releases on death | Releases on death | Player dies |
| Sentry hostility | Yes (hardcoded) | No | No |
| Network sync | Via PlayerLife | Via admin RPC | Client-side |
| Persistence | Session only | Session only | N/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:
UseableArrestStart→ItemArrestStartAssetUseableArrestEnd→ItemArrestEndAsset
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.
