OnBonusItemDropped
OnBonusItemDropped is called after LootContainer has spawned the bonus scrap item and dropped it into the world, during the loot container's bonus roll.
void OnBonusItemDropped(Item item, BasePlayer player, ItemContainer container)Parameters
| Parameter | Type | Description |
|---|---|---|
item | Item | The scrap Item instance that LootContainer.DropBonusItems just created and dropped. Never null at this point; its amount is the bonus quantity that was rolled. |
player | BasePlayer | The BasePlayer who triggered the bonus drop (the looter). Can be null on edge paths, so always null-check before using it. |
container | ItemContainer | The ItemContainer of the loot container that was opened. Useful to identify which crate type produced the bonus, but may be null. |
Return value
The hook's return value is ignored; the original method is void and the drop has already occurred before the call. Returning null or any value changes nothing.
How it works
The hook fires inside LootContainer.DropBonusItems, which runs when a lootable container hands out its extra scrap roll. In the game code the scrap item is created, OnBonusItemDrop is consulted, and only if that hook returns null is the item actually dropped and OnBonusItemDropped called. So this hook marks a completed bonus drop: the DroppedItem entity already exists in the world with DropReason set to Loot, moving away from the container's drop position with the container's inherited velocity.
item is the server-side scrap Item you can inspect for amount or skin. player is the looter who opened the container. container is the loot container's ItemContainer, which helps distinguish crate types if you track them. Because the drop already happened, you cannot cancel it here — use OnBonusItemDrop for that.
All three parameters can be treated as optional context; guard against nulls. There is no way to narrow dispatch by declaring a more specific type here since the arguments are already Item, BasePlayer and ItemContainer. The hook fires once per bonus roll, so it is safe for light bookkeeping but avoid expensive work per call on busy servers.
Example plugin: Track and reward bonus scrap luck
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("BonusScrapAnnouncer", "RustPluginGenerator", "1.0.0")]
[Description("Tracks and announces bonus scrap rolls from loot containers.")]
public class BonusScrapAnnouncer : RustPlugin
{
private const string PermAdmin = "bonusscrapannouncer.admin";
private PluginConfig config;
private StoredData data;
private const string DataFile = "BonusScrapAnnouncer/totals";
private class PluginConfig
{
[JsonProperty("Announce big rolls in chat")]
public bool AnnounceBigRolls { get; set; } = true;
[JsonProperty("Big roll threshold (scrap)")]
public int BigRollThreshold { get; set; } = 50;
[JsonProperty("Thank the player privately")]
public bool ThankPlayer { get; set; } = true;
}
private class StoredData
{
[JsonProperty("Totals")]
public Dictionary<string, int> Totals { get; set; } = new Dictionary<string, 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>
{
["Thanks"] = "You rolled {0} bonus scrap.",
["BigRoll"] = "{0} just rolled {1} bonus scrap!",
["NoData"] = "No bonus scrap recorded yet."
}, 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);
data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(DataFile) ?? new StoredData();
}
private void Unload()
{
Interface.Oxide.DataFileSystem.WriteObject(DataFile, data);
}
private void OnBonusItemDropped(Item item, BasePlayer player, ItemContainer container)
{
if (item == null || player == null || !player.IsConnected) return;
if (item.info == null || item.info.shortname != "scrap") return;
int amount = item.amount;
if (amount <= 0) return;
string id = player.UserIDString;
int total;
data.Totals.TryGetValue(id, out total);
data.Totals[id] = total + amount;
if (config.ThankPlayer)
player.ChatMessage(Msg("Thanks", id, amount));
if (config.AnnounceBigRolls && amount >= config.BigRollThreshold)
Server.Broadcast(Msg("BigRoll", null, player.displayName, amount));
}
[ChatCommand("bonusscrap")]
private void CmdBonusScrap(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!permission.UserHasPermission(player.UserIDString, PermAdmin))
{
player.ChatMessage(Msg("NoData", player.UserIDString));
return;
}
if (data.Totals.Count == 0)
{
player.ChatMessage(Msg("NoData", player.UserIDString));
return;
}
foreach (var entry in data.Totals)
player.ChatMessage($"{entry.Key}: {entry.Value}");
}
}
}The plugin listens to OnBonusItemDropped, counts the scrap amount in the rolled item, and either thanks the player privately or broadcasts when the roll is unusually large. It tracks a per-player running total in oxide/data so admins can see who is getting the most bonus scrap. Config controls the broadcast threshold and whether announcements are enabled, and all player text goes through the language file. Note that because the hook only fires after the drop, the plugin reports the reward rather than changing it.
Common use cases
Broadcast rare bonus rolls
Announce to the server when a player's bonus scrap roll exceeds a configurable amount so lucky pulls feel rewarding.
Track loot statistics
Accumulate per-player bonus scrap totals in a data file for leaderboards or admin review of container farming.
Hook into an economy plugin
Call into Economics to deposit a small bonus when a player rolls scrap, using the item amount as the multiplication factor.
Pitfalls
Cannot cancel the drop
By the time this hook runs the DroppedItem already exists and is moving. Use OnBonusItemDrop if you need to block or replace the reward.
Null player or container
Not every code path guarantees a looter, so null-check player and container before touching their members to avoid exceptions.
Do not re-give items here
Giving the player a second copy of the scrap doubles the loot and confuses the drop bookkeeping. Report or reward separately with your own item creation.
Build a plugin with OnBonusItemDropped
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 OnBonusItemDropped
Generate