Interactable Trap — Trigger Damage System
Designing effective trap defense for your Unturned base starts with understanding how InteractableTrap validates triggers through its five-layer protection chain, calculates per-entity-type damage, and resolves kill credit to the barricade owner. InteractableTrap extends InteractablePower and handles triggered area damage — both explosive (landmines) and contact-based (spike traps, barbed wire). It uses a child InteractableTrapTrigger collider for trigger detection, validates through a multi-layer protection system (setup delay, power requirement, self-exclusion, cooldown), and applies per-entity-type damage with kill credit resolved to the barricade owner.
Source code location: Unturned/Interactable/InteractableTrap.cs
Inheritance Chain
Interactable → InteractablePower → InteractableTrapTrigger Detection
InteractableTrapTrigger
csharp
public class InteractableTrapTrigger : MonoBehaviour
{
public InteractableTrap parentTrap;
private void OnTriggerEnter(Collider other)
{
if (parentTrap != null)
parentTrap.NotifyTrapEntered(other);
}
}A child trigger collider attached to the trap prefab. On any collider entering, it notifies the parent trap.
NotifyTrapEntered — Validation Chain
csharp
internal void NotifyTrapEntered(Collider other)
{
if (other.isTrigger) return; // Layer 1: Trigger guards
if (Time.realtimeSinceStartup - lastActive < setupDelay) return; // Layer 2: Setup delay
if (requiresPower && !isWired) return; // Layer 3: Power
if (other.transform.IsChildOf(transform)) return; // Layer 4: Self-exclusion
if (Time.realtimeSinceStartup - lastTriggered < cooldown) return;// Layer 5: Cooldown
// ... damage application
}Validation Layers
| Layer | Check | Purpose |
|---|---|---|
| 1 | other.isTrigger | Ignore trigger colliders that shouldn't activate |
| 2 | Setup delay | Prevents immediate triggering after placement |
| 3 | Power | Unpowered traps don't trigger |
| 4 | Self-exclusion | Trap cannot trigger itself |
| 5 | Cooldown | Minimum interval between triggers |
Timer Values
setupDelay(default 0.25s): Prevents the trap from damaging the player who placed it.cooldown(default 0.0s): Minimum interval between triggers — configurable per trap asset.
Explosive Path
When isExplosive is true:
csharp
if (isExplosive)
{
// Self-damage
BarricadeManager.damage(transform, 5.0f, 1f, false, damageOrigin: EDamageOrigin.Trap_Wear_And_Tear);
// Explosion
ExplosionParameters explosionParameters = new ExplosionParameters(
transform.position, range2, EDeathCause.LANDMINE, GetKillerId());
explosionParameters.playerDamage = playerDamage;
// ... set all damage types
DamageTool.explode(explosionParameters, out kills);
// Detonation effect
EffectAsset detonationEffect = Assets.FindEffectAssetByGuidOrLegacyId(
trapDetonationEffectGuid, explosion2);
EffectManager.triggerEffect(parameters);
}Killer ID Resolution
csharp
private CSteamID GetKillerId()
{
Transform barricadeRoot = DamageTool.getBarricadeRootTransform(transform);
if (barricadeRoot != null)
{
BarricadeDrop barricade = BarricadeDrop.FindByRootFast(barricadeRoot);
if (barricade != null)
{
BarricadeData data = barricade.GetServersideData();
if (data != null)
return new CSteamID(data.owner);
}
}
return CSteamID.Nil;
}Kill credit goes to the barricade owner as resolved from serverside data. This ensures correct attribution even when the owner is offline.
Non-Explosive Path — Per-Entity Damage
| Entity | Damage Path | Special |
|---|---|---|
| Player | DamageTool.damage(player, SHRED, SPINE, ..., playerDamage) | Breaks legs if isBroken |
| Zombie | DamageZombieParameters(zombie, direction, zombieDamage) | Hyper zombies deal 10 self-damage |
| Animal | DamageAnimalParameters(animal, direction, animalDamage) | Standard animal damage |
Leg Breaking
csharp
if (isBroken)
player.life.breakLegs();The isBroken flag on the trap asset causes leg bones to be broken, immobilizing the player.
Damage Tires
csharp
if (trapAsset.damageTires)
transform.GetOrAddComponent[InteractableTrapDamageTires]();A marker component that other systems (particularly VehicleManager) check to determine whether the trap can damage vehicle tires.
Power Requirement
As an InteractablePower subclass, traps require power from a nearby generator:
requiresPowerreads from the trap asset.isWiredis set by the power system when a generator is in range.- When unpowered,
NotifyTrapEnteredreturns at layer 3 without triggering.
Key Design Insights
- Five-layer protection — Setup delay, power, self-exclusion, cooldown, and trigger guard prevent accidental or exploit activation.
- Killer ID via barricade owner — Uses
BarricadeDrop.FindByRootFastfor O(1) ownership lookup. - Per-entity damage paths — Different damage methods per entity type allow precise balance.
- Trap self-damage — Explosive traps take 5 damage per trigger, limiting their lifespan.
Worked Code Example: Custom Trap System
Proximity Warning Trap
csharp
using SDG.Unturned;
using UnityEngine;
public class ProximityWarningTrap : MonoBehaviour
{
public InteractableTrap parentTrap;
public float warningRadius = 15f;
public float warningBeepInterval = 2f;
private float _lastBeepTime;
private void OnTriggerStay(Collider other)
{
if (parentTrap == null || !parentTrap.isWired)
return;
Player player = DamageTool.getPlayer(other.transform);
if (player == null)
return;
float now = Time.realtimeSinceStartup;
if (now - _lastBeepTime < warningBeepInterval)
return;
_lastBeepTime = now;
ChatManager.serverSendMessage(
"You are near an active trap zone.",
Color.yellow,
toPlayer: player,
iconURL: null,
useRichTextFormatting: true
);
EffectAsset warningEffect = Assets.FindEffectAssetByGuidOrLegacyId(
default, 1 // pulse effect
);
if (warningEffect != null)
{
EffectManager.triggerEffect(
new TriggerEffectParameters(warningEffect)
{
position = transform.position,
relevantDistance = EffectManager.MEDIUM,
wasInstigatedByPlayer = false
}
);
}
}
}Trap Network Manager
csharp
using SDG.Unturned;
using System.Collections.Generic;
using UnityEngine;
public class TrapNetworkManager
{
private Dictionary[Transform, float] _lastTriggerTime = new Dictionary[Transform, float]();
/// [summary]
/// Registers a trap trigger event, enforcing a configurable global
/// cooldown to prevent trap spam exploits.
/// Returns true if the trap was allowed to trigger.
/// [/summary]
public bool TryTriggerTrap(Transform trapTransform, float globalCooldown)
{
float now = Time.realtimeSinceStartup;
if (_lastTriggerTime.TryGetValue(trapTransform, out float lastTrigger))
{
if (now - lastTrigger < globalCooldown)
return false;
}
_lastTriggerTime[trapTransform] = now;
return true;
}
/// [summary]
/// Calculates the effective damage a trap would deal to a specific
/// entity type, accounting for the trap asset's multipliers and
/// the entity's armor/resistance.
/// [/summary]
public static float GetEffectiveTrapDamage(
ItemTrapAsset trapAsset,
Player player,
EDamageOrigin origin
)
{
if (trapAsset == null || player == null)
return 0f;
float baseDamage = trapAsset.playerDamageMultiplier.damage;
// Account for armor
float armorReduction = 1f;
if (player.clothing != null)
{
// Armor reduces damage proportionally
int armorTier = 0;
byte armorQuality = 0;
if (player.clothing.hatAsset != null)
armorTier = Mathf.Max(armorTier, (int)player.clothing.hatAsset.armorTier);
if (player.clothing.vestAsset != null)
{
armorTier = Mathf.Max(armorTier, (int)player.clothing.vestAsset.armorTier);
armorQuality = Mathf.Max(armorQuality, player.clothing.vestQuality);
}
armorReduction = 1f - (armorTier * 0.1f + armorQuality * 0.001f);
}
return baseDamage * Mathf.Max(armorReduction, 0.05f);
}
}Mermaid Diagram: Trap Trigger Validation Chain
Comparison: Trap Types and Damage Potential
| Feature | Explosive Trap | Spike/Barbed Wire | Barbed Wire Fence | Caltrop |
|---|---|---|---|---|
| Base damage type | Radial explosion | Contact DOT | Contact DOT | Contact + tire |
| Self-damage | 5 per trigger | None | None | None |
| Ammo/consumable | No (unlimited triggers until destroyed) | No | No | No |
| Power required | Optional (asset flag) | Optional | Optional | No |
| Entity targets | All (explosion AoE) | Player, zombie, animal | Player, zombie | Player, vehicle tires |
| Leg breaking | No | Yes (if isBroken) | Yes (if isBroken) | No |
| Cooldown | Configurable | Configurable | Configurable | Configurable |
| Kill attribution | Barricade owner | Barricade owner | Barricade owner | Barricade owner |
| Visual effect | Explosion prefab | None (contact-based) | None | None |
| Range | range2 (explosion radius) | Trigger collider radius | Trigger collider radius | Very small collider |
Failure Modes and Common Mistakes
Hyper zombie self-destruction chains — Hyper zombies deal 10 self-damage to the trap. If multiple traps are stacked, a hyper zombie entering the trigger zone sets off a chain reaction: Trap A damages the zombie (which is hyper), zombie deals 10 self-damage to Trap A, Trap B triggers on the same zombie, zombie deals 10 more self-damage. Five stacked traps results in 50 self-damage per zombie step — traps self-destruct faster than expected.
Setup delay evaded by re-placement — The
setupDelay(0.25s) useslastActivetimestamp, which resets when the barricade is placed again. Moving a trap (pick up + re-place) resets the setup delay, allowing the placer to re-enter the trigger zone immediately after placement without triggering. This can be exploited to bypass trap defense by rapidly repositioning.Explosive damage to friendlies — Traps do not differentiate between owner/group members and enemies. The
DamageTool.explodecall applies damage to ALL entities in range. An explosive trap placed in a shared base will damage teammates who walk over it. There is no friendly-fire exclusion in the trap code.Killer resolution failure on unowned traps — If a trap's barricade data is corrupted and the owner field is
CSteamID.Nil,GetKillerId()returnsCSteamID.Nil. Kill messages display as "[no name] killed [player]" and kill statistics are not attributed. This is common on servers that import barricade databases from external sources.Cooldown short-circuiting on rapid re-entry — A player can trigger a trap, take damage, exit the trigger zone, and re-enter within the cooldown window without triggering. This allows "trap running" — skilled players can dance through trap corridors by timing their entry/exit with the cooldown cycle.
How This Field Behaves Differently from the SDG Docs
SDG docs describe traps as "one-time use." The community wiki often characterizes traps like landmines as single-use items. In the SDK, explosive traps take 5 self-damage per trigger but survive multiple triggers if they have sufficient health. A landmine with 15 health (boosted via asset) can trigger 3 times before destruction.
SDG docs claim traps can be disarmed. Some documentation mentions a disarming interaction. In the SDK, traps have no
disarmmethod. They can only be destroyed by damage (from explosions, zombies, orBarricadeManager.damage) or picked up by the owner if the barricade asset allows pickup.SDG docs state trap damage is "per tick" for contact traps. The official documentation sometimes implies continuous damage over time for spike traps. In the SDK,
NotifyTrapEnteredfires once per trigger entry + cooldown cycle. Spike traps do not deal damage per frame — they damage once per valid trigger event.SDG docs mention "pressure plate" trap variants. The community wiki discussion references pressure-sensitive traps that trigger under weight. In the SDK, all traps use the same
OnTriggerEnterpattern. There is no weight threshold or mass-sensitive trigger. Any collider (player, zombie, animal, vehicle) triggers the trap.
Performance Considerations
Trigger Volume Overhead
Each trap with an active InteractableTrapTrigger adds a OnTriggerEnter Unity callback that fires on every collider entering its volume:
- 1-50 traps: Negligible — Unity's physics system handles the overlap efficiently.
- 50-200 traps: Each trigger sends an
OnTriggerEnterevent to theInteractableTrapTriggercomponent. The validation chain runs in O(1) per trigger. Acceptable. - 200+ traps in a dense area: Physics overlap checks may become expensive on the Unity physics thread. Consider reducing trigger collider sizes or consolidating multiple traps into fewer, larger traps.
Explosion Performance
Each explosive trap trigger calls DamageTool.explode(...), which performs:
- A
Physics.OverlapSphereto find all damageable entities in the blast radius. - Per-entity damage calculation and application.
- Physics impulse (
explosionLaunchSpeed) to nearbyRigidbodycomponents.
A rapid sequence of trap explosions (e.g., a group of 10 zombies triggering 5 traps each) can generate 50 OverlapSphere calls in a single frame. This is acceptable on modern hardware but may cause stuttering on lower-end servers.
Memory
Trap state is minimal: a few floats (setupDelay, cooldown timer, lastTriggered timestamp) and a flag (isExplosive). Each trap uses approximately 32 bytes of agent data beyond its barricade state.
Deeper FAQ
Q: Can traps damage vehicles with passengers inside?
Yes. If a vehicle's collider enters the trap trigger, the trap checks for a DamageTires component. If present, tire damage is applied. If the trap is explosive, the explosion damages the vehicle (and by extension, passengers inside the vehicle). DamageTool.explode applies vehicle damage through vehicleDamageMultiplier in the standard explosion path.
Q: How is kill attribution handled for trap kills?
Kill credit goes to the CSteamID stored in the trap's barricade data owner field. If the owner is offline or has left the server, the kill is still attributed to their SteamID. If the barricade owner is CSteamID.Nil (0), the kill appears as from "an unknown source" in kill messages.
Q: Do traps respect safezone restrictions?
InteractableTrap does not check safezone flags in its trigger method. Traps in safezones will still deal damage. However, the BarricadeManager checks safezone placement rules when the trap is initially placed. A trap placed in a weapon-restricted safezone (safezone.noWeapons = true) is prevented at placement time, but if the safezone configuration changes after placement, existing traps are not retroactively removed.
Q: Can I set a trap to only trigger on zombies?
Not through vanilla configuration. The NotifyTrapEntered method does not filter by entity type. A Harmony patch on NotifyTrapEntered or on InteractableTrapTrigger.OnTriggerEnter is required to add entity-type filtering. Some server plugins implement "zombie-only" or "player-only" trap modes by checking the collider's parent component before calling the trap logic.
Q: What happens if two traps' triggers overlap?
Each trap fires independently. A single zombie entering the overlap of two traps will trigger both traps in the same frame (or on sequential physics updates). Both traps apply their full damage, and each trap receives its own self-damage. This can be intentionally exploited for "double mine" kill zones but creates unexpected damage spikes for players who assume one trigger = one trap.
Cross-References
- Interactable Generator — Power Supply — Traps require generator power via
InteractablePowerwhenrequiresPoweris true. - ItemTrapAsset — Trap Asset Definitions — Asset-level damage multipliers, explosive flag, broken/bone damage setting, cooldown, and setup delay.
- Barricade Manager — Barricade placement, damage, and state management that underpins trap behavior.
- Interactable Sentry System — Sentries are another power-dependent defensive system with similar wiring requirements.
