Skip to content
RustPluginGenerator

OnSamSiteTarget

OnSamSiteTarget is called once for each candidate entity that passes the visibility and validity checks during SamSite.TargetScan, right before it is selected as the turret's target.

Signature
object OnSamSiteTarget(SamSite samSite, SamSite.ISamSiteTarget item)

Parameters

Hook parameters
ParameterTypeDescription
samSiteSamSiteThe SamSite entity running the target scan. Use it for its position, OwnerID, flags or to compare against a known turret instance.
itemSamSite.ISamSiteTargetThe candidate target implementing SamSite.ISamSiteTarget. Its SAMTargetType tells you whether it is a missile or helicopter target, and its CenterPoint() gives the aim position.

Return value

Return null to let the SamSite accept the candidate as its target. Return any non-null value to reject that candidate; the scan moves on to the next entity and the turret may pick a different one or end up with no target at all.

How it works

SamSite.TargetScan iterates the entities in its scan range and, for each one that is not client-side, sits above the eye point, is visible and reports IsValidSAMTarget, calls Interface.CallHook("OnSamSiteTarget", this, item). The loop stops at the first candidate where the hook returns null, so the hook effectively filters the target selection. Returning a non-null value skips that candidate and the scan continues to the next one.

The item parameter is typed as SamSite.ISamSiteTarget, which covers both rockets and aircraft the site is set to track. Use its SAMTargetType (Missile or Helicopter) to tell them apart, and CenterPoint() for the target position. samSite is the turret itself, so you can branch on its owner, position or region.

Narrowing by parameter type does not help much here: the interface type is what the game passes, and you cannot substitute a concrete class for it. Keep the hook body cheap because it runs per candidate per scan tick.

Example plugin: Keep sam sites from locking onto safe-zone targets

SamSiteSafeZoneFilter.cs
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Sam Site Safe Zone Filter", "RustPluginGenerator", "1.0.0")]
    [Description("Filters SamSite targets by prefab short name, height and NPC ownership.")]
    public class SamSiteSafeZoneFilter : RustPlugin
    {
        private const string PermAdmin = "samsitesafezonefilter.admin";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Blocked prefab short names")]
            public List<string> BlockedPrefabs = new List<string> { "bradleyapc", "patrolhelicopter", "cargoship" };

            [JsonProperty("Minimum target height above terrain")]
            public float MinimumTargetHeight = 5f;

            [JsonProperty("Block NPC-owned targets")]
            public bool BlockNpcs = true;
        }

        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>
            {
                ["Blocked"] = "SAM site target blocked: {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(PermAdmin, this);
        }

        private object OnSamSiteTarget(SamSite samSite, SamSite.ISamSiteTarget target)
        {
            if (samSite == null || target == null) return null;

            BaseEntity entity = target as BaseEntity;
            if (entity == null) return null;
            if (entity.IsDestroyed) return null;

            float height = target.CenterPoint().y - TerrainMeta.HeightMap.GetHeight(target.CenterPoint());
            if (height < config.MinimumTargetHeight) return true;

            string prefab = entity.ShortPrefabName;
            if (!string.IsNullOrEmpty(prefab) && prefab != "unknown")
            {
                for (int i = 0; i < config.BlockedPrefabs.Count; i++)
                {
                    if (string.Equals(config.BlockedPrefabs[i], prefab, StringComparison.OrdinalIgnoreCase))
                        return true;
                }
            }

            if (config.BlockNpcs && entity.OwnerID != 0)
            {
                BasePlayer owner = BasePlayer.FindByID(entity.OwnerID);
                if (owner != null && owner.IsNpc) return true;
            }

            return null;
        }

        [ChatCommand("samfilter")]
        private void CmdSamFilter(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;
            if (!player.IsAdmin && !permission.UserHasPermission(player.UserIDString, PermAdmin))
            {
                player.ChatMessage(Msg("Blocked", player.UserIDString, "no permission"));
                return;
            }

            player.ChatMessage(string.Format("Blocked prefabs: {0}", string.Join(", ", config.BlockedPrefabs)));
            player.ChatMessage(string.Format("Min height: {0}m, block NPCs: {1}", config.MinimumTargetHeight, config.BlockNpcs));
        }
    }
}

The plugin reads a configurable list of prefab short names and blocks any SamSite target whose ShortPrefabName matches. It also checks the configurable minimum height and skips targets whose CenterPoint().y is below the turret's own eye height, mirroring the vanilla geometry rules. When a candidate is rejected, the hook returns true, so the scan ignores it and moves on. An optional blockNpcs switch rejects targets owned by non-player entities such as ScientistNPCs that might wander into range. Config values load on startup; warnings are printed only once when the config fails to parse.

Common use cases

  • Protect event or safe-zone entities

    Reject targets on a configured prefab list so SAM sites on a PvE server never fire at supply drops, bradley or event helicopters.

  • Restrict SAM tracking by height

    Reject targets below a height threshold to stop turrets wasting rockets on low-flying mini-copters near the ground.

  • Owner-based targeting policy

    Use samSite.OwnerID and the target owner to allow firing only at entities outside the turret owner's team.

Pitfalls

  • Do not assume the hook fires every tick

    It only fires for candidates that already passed visibility and validity checks. A target you expect to be filtered may never reach the hook because the game rejected it earlier.

  • Interface type limits narrowing

    The second parameter is SamSite.ISamSiteTarget, not a concrete class, so you cannot declare a more specific parameter type to filter calls. Inspect SAMTargetType and ShortPrefabName instead.

  • Cheap checks only

    This runs for every candidate on every scan, which can happen at a short interval with many entities in range. Avoid allocations, string building or iteration over large collections inside the hook.

Build a plugin with OnSamSiteTarget

Describe what should happen and get a compiled plugin that uses this hook.

Prompt: Protect event or safe-zone entities using the OnSamSiteTarget hook, with a config file and a permission
Generate this plugin

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 OnSamSiteTarget

Generate