Skip to content
RustPluginGenerator

OnBradleyApcInitialize

OnBradleyApcInitialize is called from BradleyAPC.Initialize, right before the APC's rigidbody center of mass, destination and finalDestination are set to its spawn position. Returning a non-null value skips that vanilla setup block.

Signature
object OnBradleyApcInitialize(BradleyAPC bradleyAPC)

Parameters

Hook parameters
ParameterTypeDescription
bradleyAPCBradleyAPCThe BradleyAPC that is running its Initialize method, i.e. the APC entity that just spawned or finished its spawn setup. Useful for reading its position, net ID or entity reference before the vanilla initialization completes.

Return value

Return null (or nothing) to let the vanilla initialization assignments run. Returning any non-null object stops that block, so myRigidBody.centerOfMass, destination and finalDestination are not set from the transform position.

How it works

The hook fires once per BradleyAPC when its Initialize method runs, which happens as the APC finishes spawning on the server. The single argument is the APC instance itself, so you can inspect bradleyAPC.net.ID.Value, bradleyAPC.transform.position, bradleyAPC.OwnerID or check whether it is already dead. Because the call happens before the APC's movement fields are assigned, this is the earliest point where you can track a freshly spawned APC (a monument tank, a spawned one, or one restored from a save) and register it in your own list or data store.

The hook has no subtype filtering value: the parameter is always a BradleyAPC, so declaring it as a more general type such as BaseEntity does not narrow anything further. It also does not fire again when the APC takes damage or respawns internally; a new entity means a new call.

Returning null lets the game run its normal setup. Returning any object stops the assignment of myRigidBody.centerOfMass, destination and finalDestination, which leaves the APC's movement state at whatever the field initializers gave it. Only do this when you are deliberately overriding APC movement or replacing the APC with your own entity.

Example plugin: Announce and track newly spawned Bradley APCs

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

namespace Oxide.Plugins
{
    [Info("Bradley Spawn Announcer", "RustPluginGenerator", "1.0.0")]
    [Description("Announces and tracks newly initialized Bradley APCs on the server.")]
    public class BradleySpawnAnnouncer : RustPlugin
    {
        private const string PermShow = "bradleyspawnannouncer.show";

        private PluginConfig config;
        private readonly Dictionary<ulong, ApcRecord> tracked = new Dictionary<ulong, ApcRecord>();

        private class ApcRecord
        {
            public ulong NetId;
            public Vector3 Position;
            public int SpawnedAt;
        }

        private class PluginConfig
        {
            [JsonProperty("Announce spawns in chat")]
            public bool AnnounceSpawns = true;

            [JsonProperty("Spawn announcement message")]
            public string Announcement = "A Bradley APC has spawned near {0}";
        }

        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>
            {
                ["Announce"] = "A Bradley APC has spawned near {0}",
                ["NoPermission"] = "You do not have permission to use this command.",
                ["NoApcs"] = "No Bradley APCs are currently tracked.",
                ["Tracked"] = "Tracking {0} APC(s). Newest at {1}",
                ["Location"] = "{0}, {1}, {2}"
            }, 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(PermShow, this);
        }

        private object OnBradleyApcInitialize(BradleyAPC bradleyAPC)
        {
            if (bradleyAPC == null || bradleyAPC.net == null || bradleyAPC.IsDestroyed) return null;

            ulong id = bradleyAPC.net.ID.Value;
            Vector3 pos = bradleyAPC.transform.position;

            tracked[id] = new ApcRecord
            {
                NetId = id,
                Position = pos,
                SpawnedAt = Facepunch.Math.Epoch.Current
            };

            if (config.AnnounceSpawns)
            {
                string location = Msg("Location", null, Mathf.RoundToInt(pos.x), Mathf.RoundToInt(pos.y), Mathf.RoundToInt(pos.z));
                string template = string.IsNullOrEmpty(config.Announcement) ? Msg("Announce", null, location) : string.Format(config.Announcement, location);
                Server.Broadcast(template);
            }

            return null;
        }

        private void OnEntityDeath(BaseCombatEntity entity, HitInfo info)
        {
            if (entity == null) return;
            var apc = entity as BradleyAPC;
            if (apc != null && apc.net != null) tracked.Remove(apc.net.ID.Value);
        }

        private void OnEntityKill(BaseNetworkable entity)
        {
            if (entity == null) return;
            tracked.Remove(entity.net.ID.Value);
        }

        [ChatCommand("bradley")]
        private void CmdBradley(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;
            if (!permission.UserHasPermission(player.UserIDString, PermShow))
            {
                player.ChatMessage(Msg("NoPermission", player.UserIDString));
                return;
            }

            if (tracked.Count == 0)
            {
                player.ChatMessage(Msg("NoApcs", player.UserIDString));
                return;
            }

            ulong newestId = 0;
            int newestTime = 0;
            foreach (var record in tracked.Values)
            {
                if (record.SpawnedAt >= newestTime)
                {
                    newestTime = record.SpawnedAt;
                    newestId = record.NetId;
                }
            }

            ApcRecord newest;
            string position = tracked.TryGetValue(newestId, out newest)
                ? Msg("Location", player.UserIDString, Mathf.RoundToInt(newest.Position.x), Mathf.RoundToInt(newest.Position.y), Mathf.RoundToInt(newest.Position.z))
                : "unknown";

            player.ChatMessage(Msg("Tracked", player.UserIDString, tracked.Count, position));
        }
    }
}

The plugin listens to OnBradleyApcInitialize, records every APC in a dictionary keyed by its net ID with the spawn position and a timestamp, and sends a configurable chat broadcast to all players. OnEntityDeath removes the entry when the APC is destroyed so the tracking list does not grow forever. A short chat command bradley lives behind the bradleyspawnannouncer.show permission and prints the number of tracked APCs plus the position of the newest one. All player-facing strings go through the language file, and the config controls whether broadcasting is enabled and what the message says.

Common use cases

  • Alert players to a Bradley spawn

    Broadcast a chat message the moment a new APC initializes so the server population knows a monument tank is active. Reads bradleyAPC.transform.position to include a rough location.

  • Maintain a live APC registry

    Store the net ID, spawn time and position of each APC so other features (loot tables, kill rewards, map markers) can look up active Bradleys without scanning BaseNetworkable.serverEntities every tick.

  • Gate or replace default APC behavior

    Return a non-null value to skip the vanilla center-of-mass and destination setup when you are fully driving the APC yourself, for example on an event map where you want it to stay parked.

Pitfalls

  • Stopping movement setup has consequences

    Returning a non-null value skips the assignment of myRigidBody.centerOfMass, destination and finalDestination. Unless you set those fields yourself, the APC may behave oddly or stay put, so only override if you know what you are replacing.

  • It fires before movement state is ready

    Do not assume destination or the rigidbody center of mass already reflect the spawn position inside the hook. Use transform.position instead, and if you need the finalized state, defer with NextTick.

  • No type filtering helps

    The parameter is always a BradleyAPC, so declaring it as BaseEntity gives no extra filtering. Validate bradleyAPC == null || bradleyAPC.IsDestroyed before touching it.

Build a plugin with OnBradleyApcInitialize

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

Prompt: Alert players to a Bradley spawn using the OnBradleyApcInitialize 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 OnBradleyApcInitialize

Generate