C#:
using Newtonsoft.Json;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("BallisticArmorSlots", "emp77", "1.1.0")]
[Description("Gives ballistic armor a random number of insert slots (by % chance) when it spawns inside loot crates")]
public class BallisticArmorSlots : RustPlugin
{
// ballistic.legarmor / ballistic.vest
private const int LegArmorId = 1983541158;
private const int VestId = -1780402255;
#region Configuration
private Configuration _config = new();
private class Configuration
{
[JsonProperty("Chance (%) - 0 slots")]
public int Chance0;
[JsonProperty("Chance (%) - 1 slot")]
public int Chance1;
[JsonProperty("Chance (%) - 2 slots")]
public int Chance2;
[JsonProperty("Chance (%) - 3 slots")]
public int Chance3;
}
private static Configuration GetDefaultConfig()
{
return new Configuration
{
Chance0 = 50,
Chance1 = 30,
Chance2 = 10,
Chance3 = 10
};
}
protected override void LoadConfig()
{
base.LoadConfig();
Configuration config;
try
{
config = Config.ReadObject<Configuration>() ?? GetDefaultConfig();
}
catch
{
LoadDefaultConfig();
return;
}
_config = config;
SaveConfig();
// The four chances are meant to add up to 100%. They still work if they
// don't (each value is applied proportionally), but warn admins so they
// can fix obvious typos instead of getting unexpected odds.
int sum = Mathf.Max(0, config.Chance0) + Mathf.Max(0, config.Chance1)
+ Mathf.Max(0, config.Chance2) + Mathf.Max(0, config.Chance3);
if (sum != 100)
PrintWarning($"Slot chances add up to {sum}%, not 100%. They will be applied proportionally - set them so they total 100% for exact percentages.");
}
protected override void LoadDefaultConfig() => _config = GetDefaultConfig();
protected override void SaveConfig() => Config.WriteObject(_config);
#endregion
private void OnItemAddedToContainer(ItemContainer container, Item item)
{
// Only ballistic armor that spawns inside a loot crate (airdrop / military
// crate / etc.). Players can't put items back into these, so the
// roll happens exactly once and can never be re-rolled (a 0 stays 0).
if (container?.entityOwner is not LootContainer) return;
if (item?.info == null) return;
if (item.info.itemid != LegArmorId && item.info.itemid != VestId) return;
var mod = item.info.GetComponent<ItemModContainerArmorSlot>();
if (mod == null) return;
// Skip armor that already has slots (vanilla/admin pre-slotted).
if (item.contents != null) return;
int amount = RollSlotCount(mod.MaxSlots);
if (amount > 0) mod.SetSlotAmount(item, amount);
}
// Picks a slot count in [0, maxSlots] using the configured % chances. If the
// chances don't sum to 100 they are treated proportionally, so nothing breaks.
private int RollSlotCount(int maxSlots)
{
// index = slot count, value = chance (%)
int[] chances =
{
Mathf.Max(0, _config.Chance0),
Mathf.Max(0, _config.Chance1),
Mathf.Max(0, _config.Chance2),
Mathf.Max(0, _config.Chance3),
};
int top = Mathf.Clamp(maxSlots, 0, chances.Length - 1);
int total = 0;
for (int i = 0; i <= top; i++) total += chances[i];
// All chances are zero -> add no slots.
if (total <= 0) return 0;
int roll = UnityEngine.Random.Range(0, total); // [0, total)
for (int i = 0; i <= top; i++)
{
if (roll < chances[i]) return i;
roll -= chances[i];
}
return top;
}
}
}