OnReactiveTargetReset
OnReactiveTargetReset is called after a ReactiveTarget (the shootable target used in shooting ranges) finishes its reset routine and is switched back on. It fires once per reset, at the end of ReactiveTarget.ResetTarget.
void OnReactiveTargetReset(ReactiveTarget reactiveTarget)Parameters
| Parameter | Type | Description |
|---|---|---|
reactiveTarget | ReactiveTarget | The ReactiveTarget entity that just finished resetting. It has already been switched back on with full knockdown health, so you can read its position, owner and net ID to react to the reset. |
Return value
The return value is ignored. ResetTarget returns void and the hook is called as a plain notification, so returning null or any other value changes nothing about the reset.
How it works
The hook fires inside ReactiveTarget.ResetTarget, after the target has been set back to the On flag, cleared of its knockdown state and had its knockdown health restored to 100. This means the call happens when a knocked-down target goes back up, either through the game's own reset flow on that entity. Because it runs at the end of the method, the entity's flags and health are already in their post-reset state when your plugin sees it.
The single parameter is the target entity itself. Use net.ID.Value as a stable key, transform.position for location-based logic, and OwnerID when targets are placed on player-owned bases. A common narrowing trick is to declare the parameter as ReactiveTarget (as given) — Oxide only dispatches when the argument really is a ReactiveTarget, so there is no need to cast, though a null check is still good practice.
The hook is a notification only; it does not run often, so light bookkeeping such as scoring, counters or effects is fine here. Avoid heavy per-target scans if a server has many targets on a shooting range.
Example plugin: Track shooting range target resets per player
using UnityEngine;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("Reactive Target Reset Tracker", "RustPluginGenerator", "1.0.0")]
[Description("Tracks how many times a player's reactive targets reset, with a cooldown and chat command.")]
public class ReactiveTargetResetTracker : RustPlugin
{
private const string PermView = "reactivetargetresettracker.view";
private PluginConfig config;
private StoredData data;
private readonly Dictionary<ulong, float> lastCount = new Dictionary<ulong, float>();
private class PluginConfig
{
[JsonProperty("Enable tracking")]
public bool Enabled = true;
[JsonProperty("Cooldown per target in seconds")]
public float CooldownSeconds = 5f;
[JsonProperty("Broadcast to owner on reset")]
public bool NotifyOwner = false;
}
private class StoredData
{
[JsonProperty("Reset counts by player ID")]
public Dictionary<ulong, int> Counts = new Dictionary<ulong, 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>
{
["ResetCount"] = "{0}'s targets have reset {1} times.",
["NoStats"] = "No reset statistics recorded for {0}.",
["OwnerNote"] = "One of your targets is back up. Total resets: {0}."
}, 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);
data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name) ?? new StoredData();
}
private void Unload()
{
Interface.Oxide.DataFileSystem.WriteObject(Name, data);
}
private void OnReactiveTargetReset(ReactiveTarget reactiveTarget)
{
if (!config.Enabled || reactiveTarget == null || reactiveTarget.IsDestroyed) return;
ulong owner = reactiveTarget.OwnerID;
if (owner == 0) return;
if (lastCount.TryGetValue(reactiveTarget.net.ID.Value, out float last) &&
UnityEngine.Time.realtimeSinceStartup - last < config.CooldownSeconds)
return;
lastCount[reactiveTarget.net.ID.Value] = UnityEngine.Time.realtimeSinceStartup;
int total = GetCount(owner) + 1;
data.Counts[owner] = total;
if (config.NotifyOwner)
{
BasePlayer player = BasePlayer.FindByID(owner);
if (player != null && player.IsConnected)
player.ChatMessage(Msg("OwnerNote", owner.ToString(), total));
}
}
private int GetCount(ulong userId)
{
return data.Counts.TryGetValue(userId, out int count) ? count : 0;
}
[ChatCommand("targetstats")]
private void CmdTargetStats(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!player.IsAdmin && !permission.UserHasPermission(player.UserIDString, PermView))
{
player.ChatMessage("You do not have permission to use this command.");
return;
}
if (args == null || args.Length < 1)
{
player.ChatMessage($"Your targets have reset {GetCount(player.userID.Get())} times.");
return;
}
BasePlayer target = BasePlayer.FindAwakeOrSleeping(args[0]);
if (target == null)
{
player.ChatMessage($"Player '{args[0]}' not found.");
return;
}
int count = GetCount(target.userID.Get());
if (count == 0)
player.ChatMessage(Msg("NoStats", player.UserIDString, target.displayName));
else
player.ChatMessage(Msg("ResetCount", player.UserIDString, target.displayName, count));
}
}
}The plugin listens for every target reset and records a per-owner counter of how many times the owner's shooting-range targets have gone back up. It keeps a small in-memory dictionary plus a data file so the count survives restarts, and it applies a configurable cooldown so a single target cannot inflate the total when it is hit repeatedly. Admins can read the current count with the targetstats chat command, which resolves a player by name or Steam ID and prints the stored total. The plugin demonstrates the hook's real value: knowing when a target returns to duty, not just when it is knocked down.
Common use cases
Shooting range scoring
Count how many times targets on a player's range reset to award range points, tickets or a message in chat when a target is back up.
Target maintenance monitoring
Log or count resets per target net ID to detect targets that are being hammered non-stop and may need to be moved or replaced.
Trigger effects on reset
Play a short sound or effect at the target's position when it comes back up, useful for scripted minigames built on ReactiveTarget entities.
Pitfalls
The target is already active
Because the hook fires after the flags and knockdown health are restored, checking
HasFlag(Flags.On)to decide whether the target needs a manual reset will always be false. Treat this as a post-reset event.Entity may be destroyed shortly after
A target can be killed or despawned around a reset, so always null-check the parameter and guard against
IsDestroyedbefore storing references or effects.Not a per-hit hook
This fires on reset only, not on every shot. Pair it with
OnEntityTakeDamageif you need to know when a target was knocked down.
Build a plugin with OnReactiveTargetReset
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 OnReactiveTargetReset
Generate