Skip to content
RustPluginGenerator

OnCrateSpawned

OnCrateSpawned is called after a PatrolHelicopter or BradleyAPC dies and the game has spawned the loot crate at the wreck site.

Signature
void OnCrateSpawned(PatrolHelicopter patrolHelicopter, BaseEntity crateEntity)

Parameters

Hook parameters
ParameterTypeDescription
patrolHelicopterPatrolHelicopterThe PatrolHelicopter that just died and dropped its crash crate. When the hook fires from BradleyAPC, declaring this as PatrolHelicopter makes Oxide skip the call entirely; use a BaseEntity parameter to receive both.
crateEntityBaseEntityThe crate entity that was spawned at the wreck, typically a HackableLockedCrate-style loot crate created by the helicopter or Bradley death handler.

Return value

The return value is ignored at both injection sites, so returning null or any value has no effect on the game's crate spawning code. Use the hook to observe or modify the spawned crate entity rather than to cancel the spawn.

How it works

This hook fires inside PatrolHelicopter.OnDied and BradleyAPC.OnDied, after the gibs and crate entity have been created and the collision ignores set up. It runs once per kill, not per crate inside the crate inventory. The first parameter is the dying vehicle and the second is the spawned crate entity, so you can reach both the killer vehicle and the loot container in one callback.

Oxide dispatches by parameter type: a plugin declaring PatrolHelicopter only receives helicopter kills, BradleyAPC only Bradley kills. Declaring BaseEntity for the first parameter receives both, which is the usual choice for a shared crate-handling plugin. Because the third-party signature variant uses the same hook name, keep the second parameter typed as BaseEntity in both overloads so dispatch never fails on the crate argument.

The call happens after the crate has been spawned and positioned, so calling Kill(), moving it, or changing flags here takes effect immediately on the live entity. It fires off the death path, not on a timer, so keep the handler cheap.

Other signatures

Oxide also calls OnCrateSpawned from other places in the game with these parameter types. Declare the overload you need, or several:

  • void OnCrateSpawned(BradleyAPC bradleyAPC, BaseEntity baseEntity2)

Example plugin: Replace heli and Bradley crash crates with configurable loot

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

namespace Oxide.Plugins
{
    [Info("Heli Crate Replacer", "RustPluginGenerator", "1.0.0")]
    [Description("Replaces the crash crate dropped by a PatrolHelicopter or BradleyAPC with configurable loot.")]
    public class HeliCrateReplacer : RustPlugin
    {
        private const string PermNotify = "helicratereplacer.notify";

        private class PluginConfig
        {
            [JsonProperty("Enable for PatrolHelicopter")]
            public bool EnableHeli = true;

            [JsonProperty("Enable for BradleyAPC")]
            public bool EnableBradley = true;

            [JsonProperty("Replacement crate shortname")]
            public string CrateShortname = "codelockedhackablecrate";

            [JsonProperty("Replacement crate skin")]
            public ulong CrateSkin = 0UL;

            [JsonProperty("Spawn effect prefab")]
            public string SpawnEffect = "assets/prefabs/npc/patrol helicopter/effects/rocket_explosion.prefab";
        }

        private PluginConfig config;

        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>
            {
                ["CrateReplaced"] = "A {0} kill crate has been replaced with custom loot.",
                ["Heli"] = "Patrol Helicopter",
                ["Bradley"] = "Bradley APC"
            }, 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(PermNotify, this);
        }

        private void OnCrateSpawned(BaseEntity source, BaseEntity crateEntity)
        {
            if (source == null || crateEntity == null || crateEntity.IsDestroyed) return;

            string sourceName = null;
            if (source is PatrolHelicopter && config.EnableHeli) sourceName = "Heli";
            else if (source is BradleyAPC && config.EnableBradley) sourceName = "Bradley";
            if (sourceName == null) return;

            Vector3 position = crateEntity.transform.position;
            crateEntity.Kill();

            ItemDefinition def = ItemManager.FindItemDefinition(config.CrateShortname);
            if (def == null)
            {
                PrintWarning($"Unknown replacement shortname: {config.CrateShortname}");
                return;
            }

            Item item = ItemManager.CreateByItemID(def.itemid, 1, config.CrateSkin);
            if (item == null) return;

            item.MoveToContainer(null);
            BaseEntity replacement = item.Drop(position, Vector3.zero) as BaseEntity;
            if (replacement == null) return;

            replacement.SendNetworkUpdate();

            if (!string.IsNullOrEmpty(config.SpawnEffect))
                Effect.server.Run(config.SpawnEffect, position);

            NotifyPermitted(Msg("CrateReplaced", null, Msg(sourceName)));
        }

        private void NotifyPermitted(string message)
        {
            foreach (BasePlayer player in BasePlayer.activePlayerList)
            {
                if (player != null && player.IsConnected && permission.UserHasPermission(player.UserIDString, PermNotify))
                    player.ChatMessage(message);
            }
        }

        [ConsoleCommand("crate.reload")]
        private void CmdReload(ConsoleSystem.Arg arg)
        {
            if (arg != null && arg.Player() != null && arg.Player().IsAdmin == false)
            {
                arg.ReplyWith("Admin only");
                return;
            }
            LoadConfig();
            LoadDefaultMessages();
            arg?.ReplyWith("HeliCrateReplacer reloaded");
        }
    }
}

The plugin declares OnCrateSpawned with a BaseEntity first parameter so it catches both PatrolHelicopter and BradleyAPC deaths. On each call it checks the config toggle for the source type, destroys the original crate with Kill(), and spawns a replacement at the same position. The replacement shortname, amount, skin and a broadcast message are all configurable. A crate.reload console command rereads config and messages, and Unload clears the change. Players with the helicratereplacer.notify permission get a chat line via the language file when a crate is swapped.

Common use cases

  • Custom loot for heli crashes

    Kill the default crate and spawn a custom SupplyDrop or HackableLockedCrate with your own loot table, or refill the crate's inventory container in place.

  • Announce heli and Bradley kills

    Send a server-wide chat message or audio effect when the crash crate appears so players know a fight just ended and loot is on the ground.

  • Limit crate farming on small servers

    Track kills per player or per time window and remove the crate entirely when a cooldown is active, forcing players to spread out their heli farming.

Pitfalls

  • Wrong first parameter hides Bradley

    If you declare the first parameter as PatrolHelicopter, Oxide skips the call from BradleyAPC.OnDied. Use BaseEntity to handle both, then cast with as when you need vehicle specifics.

  • Crate already positioned and parented

    The crate entity is spawned before the hook fires, so calling Spawn() again or moving it after Kill() won't help. Destroy the old entity, then create and spawn the replacement at the old position.

  • Multiple plugins may fight over the crate

    Another plugin can kill or replace the same crate in its own handler. Null-check and call IsDestroyed before touching the entity, and avoid assuming your replacement is the only entity at that position.

Build a plugin with OnCrateSpawned

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

Prompt: Custom loot for heli crashes using the OnCrateSpawned 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 OnCrateSpawned

Generate