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.
object OnBradleyApcInitialize(BradleyAPC bradleyAPC)Parameters
| Parameter | Type | Description |
|---|---|---|
bradleyAPC | BradleyAPC | The 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
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.positionto 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.serverEntitiesevery 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,destinationandfinalDestination. 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
destinationor the rigidbody center of mass already reflect the spawn position inside the hook. Usetransform.positioninstead, and if you need the finalized state, defer withNextTick.No type filtering helps
The parameter is always a BradleyAPC, so declaring it as
BaseEntitygives no extra filtering. ValidatebradleyAPC == null || bradleyAPC.IsDestroyedbefore touching it.
Build a plugin with OnBradleyApcInitialize
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 OnBradleyApcInitialize
Generate