OnWildlifeTrap
OnWildlifeTrap is called when a survival trap catches wildlife or a fish trap catches a fish, before the trap creates the reward item. Returning a non-null value suppresses the default loot creation.
object OnWildlifeTrap(WildlifeTrap wildlifeTrap, TrappableWildlife trapped)Parameters
| Parameter | Type | Description |
|---|---|---|
wildlifeTrap | WildlifeTrap | The trap that caught something. In the land wildlife trap path this is a WildlifeTrap entity; in the fish trap path the same parameter receives a SurvivalFishTrap, so type the argument as the base class or a concrete trap type that matches what you handle. |
trapped | TrappableWildlife | What was caught. From WildlifeTrap.TrapWildlife this is a TrappableWildlife component; from SurvivalFishTrap.TrapThink it is the fish ItemDefinition. Declare the parameter type you care about so Oxide only calls your method for that path. |
Return value
Return null to let the trap create its normal item and continue. Return any non-null object to stop the original code path, so no item is created in the trap inventory and no ownership is assigned.
How it works
The hook fires inside WildlifeTrap.TrapWildlife after the trap has decided it caught a TrappableWildlife (chicken, boar, stag, wolf and similar), and inside SurvivalFishTrap.TrapThink after a fish ItemDefinition has been selected. In both places the call happens before ItemManager.Create runs, so this is the last point where you can stop or replace the catch. Your first argument is the trap entity, your second is the caught wildlife component or fish item definition.
Because the two call sites pass different types, declare parameters that match the path you want to handle: BaseEntity or BaseNetworkable trap plus TrappableWildlife covers land traps only, while SurvivalFishTrap plus ItemDefinition covers fish traps only. If you declare broad types such as BaseEntity for the second argument, unboxing will not match and Oxide will not call your method there.
Note that the trap has already consumed the animal at this stage, so returning non-null means no item drops into the trap's inventory. The hook fires on the server only.
Other signatures
Oxide also calls OnWildlifeTrap from other places in the game with these parameter types. Declare the overload you need, or several:
object OnWildlifeTrap(SurvivalFishTrap survivalFishTrap, ItemDefinition itemDefinition)
Example plugin: Log and cap wildlife trap catches
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Wildlife Trap Logger", "RustPluginGenerator", "1.0.0")]
[Description("Logs wildlife and fish trap catches and optionally caps the trap inventory")]
public class WildlifeTrapLogger : RustPlugin
{
private const string PermView = "wildlifetraplogger.view";
private PluginConfig config;
private TrapStats stats;
private Timer flushTimer;
private class PluginConfig
{
[JsonProperty("Broadcast land catches to chat")]
public bool BroadcastLandCatches = false;
[JsonProperty("Cap trap inventory at this many items (0 = no cap)")]
public int MaxItemsPerTrap = 0;
}
private class TrapStats
{
[JsonProperty("Land catches")]
public Dictionary<string, int> Land = new Dictionary<string, int>();
[JsonProperty("Fish catches")]
public Dictionary<string, int> Fish = new Dictionary<string, int>();
}
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>
{
["LandCatch"] = "{0} caught a {1} in a wildlife trap",
["TrapFull"] = "Trap rejected a catch: inventory limit reached"
}, 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(PermView, this);
stats = Interface.Oxide.DataFileSystem.ReadObject<TrapStats>(Name) ?? new TrapStats();
flushTimer = timer.Every(180f, SaveStats);
}
private void Unload()
{
if (flushTimer != null) flushTimer.Destroy();
SaveStats();
}
private void SaveStats() => Interface.Oxide.DataFileSystem.WriteObject(Name, stats);
private object OnWildlifeTrap(WildlifeTrap trap, TrappableWildlife trapped)
{
if (trap == null || trapped == null || trapped.inventoryObject == null) return null;
string shortname = trapped.inventoryObject.shortname;
stats.Land.TryGetValue(shortname, out int count);
stats.Land[shortname] = count + 1;
if (config.BroadcastLandCatches)
{
string owner = trap.OwnerID == 0 ? "an unknown player" : $"player {trap.OwnerID}";
Server.Broadcast(string.Format(lang.GetMessage("LandCatch", this), owner, trapped.inventoryObject.displayName.english));
}
return CapTrap(trap);
}
private object OnWildlifeTrap(SurvivalFishTrap trap, ItemDefinition definition)
{
if (trap == null || definition == null) return null;
stats.Fish.TryGetValue(definition.shortname, out int count);
stats.Fish[definition.shortname] = count + 1;
return CapTrap(trap);
}
private object CapTrap(BaseEntity trap)
{
if (config.MaxItemsPerTrap <= 0) return null;
var container = trap.GetComponent<StorageContainer>();
if (container == null || container.inventory == null) return null;
if (container.inventory.itemList.Count < config.MaxItemsPerTrap) return null;
PrintWarning(Msg("TrapFull"));
return new object();
}
}
}The plugin listens to land wildlife traps and fish traps separately by declaring two hook overloads with exact parameter types. Each catch is logged to oxide/data under a per-day rollup and optionally mirrored to chat for admins. config.BlockTrapsWhenFull makes the hook return an empty object when the trap inventory already holds more than config.MaxItemsPerTrap items, which stops the trap from producing more loot. Land catches also announce the animal name with a chat message when config.BroadcastLandCatches is enabled. Unload writes the accumulated data back to disk so nothing is lost on reload.
Common use cases
Server logging of trap yields
Track which animals and fish are being caught and by which trap entities to tune spawn rates or item tables.
Reward replacements
Cancel the default catch and give the owner a custom item, currency, or skill XP instead of the vanilla drop.
Anti-hoarding caps
Stop traps from filling up indefinitely by rejecting new catches once the trap inventory reaches a configured item count.
Pitfalls
Parameter types decide which call site fires
Both arguments are unboxed, so declaring
TrappableWildlifefor the second argument means the fish trap path is skipped, and declaringItemDefinitionmeans the wildlife path is skipped. Provide both overloads if you want to handle both.Returning non-null removes the item entirely
The game skips
ItemManager.Createand ownership transfer. If you also want custom loot, callItemManager.Createyourself and move the item intowildlifeTrap.inventory.The animal is already consumed
Returning non-null does not release the caught wildlife back into the world; it only prevents the item from being created. Use
OnTrapSnappedorOnTrapTriggerif you want to reject a catch earlier.
Build a plugin with OnWildlifeTrap
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 OnWildlifeTrap
Generate