OnTrapSnapped
OnTrapSnapped is called from BaseTrapTrigger.OnObjectAdded the moment an object enters a trap's trigger volume, before the trap entity receives ObjectEntered and before the snap check completes.
void OnTrapSnapped(BaseTrapTrigger trapTrigger, UnityEngine.GameObject obj, UnityEngine.Collider col)Parameters
| Parameter | Type | Description |
|---|---|---|
trapTrigger | BaseTrapTrigger | The BaseTrapTrigger whose trigger volume has just flagged an object. Use it to reach the parent trap entity with trapTrigger.trap, get its OwnerID, OwnerID, position and ShortPrefabName. |
obj | UnityEngine.GameObject | The GameObject that entered the trigger volume, typically the root object of a player, NPC or dropped item. Check for a BasePlayer with obj.ToBaseEntity() as BasePlayer. |
col | UnityEngine.Collider | The Collider that produced the overlap, useful when you need the exact body part or a component on the hit object via col.GetComponent<T>() or col.gameObject. |
Return value
The return value is ignored. The original OnObjectAdded body runs regardless of what you return, so the trap proceeds to notify its entity and evaluate the object. Return nothing from this hook; use it only for observation or side effects.
How it works
The hook fires whenever a BaseTrapTrigger (the trigger component on bear traps, landmines, snap traps and similar BaseTrap prefabs) registers a new overlapping object. Because the call sits at the very start of BaseTrapTrigger.OnObjectAdded, it runs before the trap entity's own ObjectEntered handler and before base.OnObjectAdded, so at this point the trap has not yet been armed at the object or reacted to it.
trapTrigger is the trigger component itself; trapTrigger.trap gives you the owning BaseTrap entity for ownership, prefab and position checks. obj is the entering GameObject and col is the producing collider, so you can distinguish a player body from an NPC, an animal or a dropped item and read the body part when needed.
Declaring trapTrigger as a more specific type is not helpful here, but you can safely take obj/col as derived types only if you are sure every overlap is that type — Oxide skips calls where the argument is not an instance of your parameter type, so the hook would silently miss other entries. Most plugins use the base types and cast manually.
The hook is informational only: it cannot cancel the entry, and the trap still processes the object afterwards.
Example plugin: Notify owners when someone steps on their trap
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Trap Trigger Notifier", "RustPluginGenerator", "1.0.0")]
[Description("Alerts trap owners when a player steps on one of their traps")]
public class TrapTriggerNotifier : RustPlugin
{
private const string PermAlerts = "traptriggernotifier.alerts";
private PluginConfig config;
private readonly Dictionary<string, double> cooldowns = new Dictionary<string, double>();
private class PluginConfig
{
[JsonProperty("Notify trap owners")]
public bool NotifyOwners = true;
[JsonProperty("Seconds between alerts per trap")]
public float CooldownSeconds = 20f;
}
protected override void LoadDefaultConfig() => config = new PluginConfig();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<PluginConfig>();
if (config == null) throw new Exception();
}
catch
{
PrintWarning("Configuration file is invalid, using defaults");
LoadDefaultConfig();
}
SaveConfig();
}
protected override void SaveConfig() => Config.WriteObject(config);
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["Alert"] = "{0} stepped on your {1} at {2}.",
["NoPermission"] = "You do not have permission to receive trap alerts.",
["Enabled"] = "Trap alerts are enabled for you."
}, this);
}
private string Msg(string key, string userId = null, params object[] args) => string.Format(lang.GetMessage(key, this, userId), args);
private void Init()
{
permission.RegisterPermission(PermAlerts, this);
cooldowns.Clear();
}
private void OnTrapSnapped(BaseTrapTrigger trapTrigger, GameObject obj, Collider col)
{
if (!config.NotifyOwners || trapTrigger == null || obj == null) return;
// BaseTrapTrigger is not a BaseEntity; the hosting trap is found in its parents.
var trap = trapTrigger.GetComponentInParent<BaseEntity>();
if (trap == null || trap.IsDestroyed || trap.OwnerID == 0) return;
var player = obj.ToBaseEntity() as BasePlayer;
if (player == null || player.IsNpc || !player.IsConnected || player.IsDead()) return;
var owner = BasePlayer.FindByID(trap.OwnerID);
if (owner == null || !owner.IsConnected || owner == player) return;
if (!permission.UserHasPermission(owner.UserIDString, PermAlerts)) return;
string cooldownKey = $"{owner.UserIDString}:{trap.net.ID.Value}";
double now = Time.realtimeSinceStartup;
double last;
if (cooldowns.TryGetValue(cooldownKey, out last) && now - last < config.CooldownSeconds) return;
cooldowns[cooldownKey] = now;
string trapName = trap.ShortPrefabName ?? "trap";
Vector3 pos = trap.transform.position;
owner.ChatMessage(Msg("Alert", owner.UserIDString, player.displayName, trapName, $"({Mathf.RoundToInt(pos.x)}, {Mathf.RoundToInt(pos.z)})"));
}
[ChatCommand("trapalerts")]
private void CmdTrapAlerts(BasePlayer player, string command, string[] args)
{
if (player == null) return;
player.ChatMessage(permission.UserHasPermission(player.UserIDString, PermAlerts)
? Msg("Enabled", player.UserIDString)
: Msg("NoPermission", player.UserIDString));
}
private void Unload()
{
cooldowns.Clear();
}
}
}The plugin listens on OnTrapSnapped, checks that the entering collider belongs to a real, connected, non-NPC player, and then resolves the trap's owner through trapTrigger.trap.OwnerID. If the owner is online and not the victim, they get a chat message with the victim's name and the trap prefab so they know which of their traps was stepped on. Each player is rate-limited per trap with a short cooldown stored in a dictionary, which keeps mine fields from spamming chat when several people walk through at once. A config toggle turns the notifications off entirely, and language keys keep the text localizable.
Common use cases
Owner alerts for landmines and bear traps
Message the placing player when any of their traps is triggered, including the victim name and trap type, without needing to poll trap state.
Anti-team-kill warnings
Compare the entering player's
currentTeamwith the trap owner's team and warn them that a teammate is standing on a friendly trap.Audit logging for trap events
Record a timestamped line in a data file each time a trap snaps, so admins can review disputes about mines placed around bases.
Pitfalls
Fires for every overlap, not just players
Animals, NPCs, dropped items and even other entities can enter the trigger volume. Always cast
obj.ToBaseEntity()and bail out when it is not aBasePlayer.Trap may already be spent or owned by nobody
trapTrigger.trapcan be null on a destroyed or unspawned trap, andOwnerIDis 0 for traps placed by admin commands or spawned by plugins. Null-check both before messaging anyone.It is not a dead-player hook
The hook reports entry into the trigger, not the resulting death. Pair it with
OnEntityDeathorOnPlayerDeathif you need to know whether the trap actually killed the victim.
Build a plugin with OnTrapSnapped
Describe what should happen and get a compiled plugin that uses this hook.
Edit the prompt on the next screen. 2 free plugins a month, no card.
Last updated Oct 26, 2026. Found a mistake? Email support@rustplugingenerator.com.
Build a plugin with OnTrapSnapped
Generate