Skip to content
RustPluginGenerator

How to make a Rust no vehicle decay plugin (per-type scaling)

Scales or disables decay damage on minicopters, boats, horses and modular cars per vehicle type, optionally only while inside a tool cupboard radius. It uses OnEntityTakeDamage(BaseCombatEntity, HitInfo).

Vehicles decay when nobody is around, and on a busy server that means admin-spawned minicopters, patrol boats and cars quietly rot away between wipes or get farmed by whoever finds them first. Turning decay off globally in the server's decay settings is all-or-nothing and also affects bases, so the clean approach is a damage hook that only touches decay hits on the vehicle types you name.

This plugin listens to OnEntityTakeDamage for BaseCombatEntity arguments, checks whether the hit is decay damage, and looks up the victim in a map of prefab and class rules. Each rule has a multiplier from 0 (never decays) to 1 (normal) to above 1. An optional tool cupboard rule only protects vehicles that sit inside a building privilege range, so abandoned vehicles in the open still rot. Multipliers are applied by scaling the damage types on the hit, and zero-damage hits are cancelled outright.

The plugin code

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

namespace Oxide.Plugins
{
    [Info("No Vehicle Decay", "RustPluginGenerator", "1.0.0")]
    [Description("Scales or disables decay damage on vehicles per type, optionally only within a tool cupboard range")]
    public class NoVehicleDecay : RustPlugin
    {
        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Require tool cupboard to protect")]
            public bool RequireBuildingPrivilege { get; set; } = false;

            [JsonProperty("Vehicle rules")]
            public Dictionary<string, VehicleRule> Vehicles { get; set; } = new Dictionary<string, VehicleRule>
            {
                ["minicopter.entity"] = new VehicleRule { Multiplier = 0.0f },
                ["boat.entity"] = new VehicleRule { Multiplier = 0.0f },
                ["ridablehorse.entity"] = new VehicleRule { Multiplier = 0.0f },
                ["modularcar.entity"] = new VehicleRule { Multiplier = 0.0f }
            };

            [JsonProperty("Ignored prefabs")]
            public List<string> IgnoredPrefabs { get; set; } = new List<string>();
        }

        private class VehicleRule
        {
            [JsonProperty("Decay multiplier (0 = never)")]
            public float Multiplier { get; set; } = 0.0f;
        }

        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);

        private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo)
        {
            if (entity == null || hitInfo == null || hitInfo.damageTypes == null) return null;
            if (entity is BasePlayer || entity.IsNpc) return null;

            if (hitInfo.damageTypes.GetMajorityDamageType() != Rust.DamageType.Decay) return null;
            if (hitInfo.InitiatorPlayer != null) return null;

            string prefab = entity.ShortPrefabName;
            if (string.IsNullOrEmpty(prefab)) return null;
            if (config.IgnoredPrefabs != null && config.IgnoredPrefabs.Contains(prefab)) return null;

            VehicleRule rule = ResolveRule(entity, prefab);
            if (rule == null) return null;

            if (config.RequireBuildingPrivilege && entity.GetBuildingPrivilege() == null) return null;

            float multiplier = Mathf.Clamp(rule.Multiplier, 0f, 10f);
            if (multiplier <= 0f)
            {
                hitInfo.damageTypes.ScaleAll(0f);
                return true;
            }

            hitInfo.damageTypes.ScaleAll(multiplier);
            return null;
        }

        private VehicleRule ResolveRule(BaseCombatEntity entity, string prefab)
        {
            if (config.Vehicles != null)
            {
                VehicleRule direct;
                if (config.Vehicles.TryGetValue(prefab, out direct)) return direct;

                foreach (var pair in config.Vehicles)
                {
                    if (string.Equals(pair.Key, prefab, StringComparison.OrdinalIgnoreCase)) return pair.Value;
                }
            }

            string fallback = ClassKey(entity);
            if (fallback == null) return null;

            if (config.Vehicles != null && config.Vehicles.TryGetValue(fallback, out var found)) return found;

            return new VehicleRule { Multiplier = 0.0f };
        }

        private string ClassKey(BaseEntity entity)
        {
            if (entity == null) return null;
            string prefab = entity.ShortPrefabName;
            if (string.IsNullOrEmpty(prefab)) return null;
            if (prefab.Contains("minicopter")) return "minicopter.entity";
            if (prefab.Contains("boat")) return "boat.entity";
            if (prefab.Contains("ridablehorse")) return "ridablehorse.entity";
            if (prefab.Contains("modularcar")) return "modularcar.entity";
            return null;
        }
    }
}

Hooks used: OnEntityTakeDamage

Config file

Created automatically on first load at oxide/config/NoVehicleDecay.json.

NoVehicleDecay.json
{
  "Require tool cupboard to protect": false,
  "Vehicle rules": {
    "minicopter.entity": {
      "Decay multiplier (0 = never)": 0
    },
    "boat.entity": {
      "Decay multiplier (0 = never)": 0
    },
    "ridablehorse.entity": {
      "Decay multiplier (0 = never)": 0
    },
    "modularcar.entity": {
      "Decay multiplier (0 = never)": 0
    }
  },
  "Ignored prefabs": []
}

How the code works

  1. Intercepting decay hits

    object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo) fires for every hit on a combat entity. The plugin ignores players, NPCs, null info and null damageTypes, then checks hitInfo.damageTypes.GetMajorityDamageType() == Rust.DamageType.Decay. Only decay hits continue; everything else returns null so vanilla handling is untouched. A multiplier of 0 calls ScaleAll(0f) and returns true to stop the hit.

  2. Matching the vehicle

    ResolveRule first checks the entity's ShortPrefabName against the Vehicles config keys, first exact and then case-insensitive, so you can add prefabs like minicopter.entity without recompiling. If no prefab key matches, ClassKey inspects the same ShortPrefabName for the substrings minicopter, boat, ridablehorse and modularcar and returns the matching key such as minicopter.entity. If that fallback key is not present in Vehicles, the plugin returns a default rule with multiplier 0.0, so the decay is still disabled. If ClassKey returns null (no substring matches), the decay is left untouched.

  3. Applying the multiplier

    When a rule matches, hitInfo.damageTypes.ScaleAll multiplies the whole damage list by the configured factor, clamped to between 0 and 10. A factor of 0 calls ScaleAll(0f) and returns true so the hit is not processed further. Values between 0 and 1 slow decay, and values above 1 accelerate it, which is useful for cleaning up abandoned vehicles faster than vanilla.

  4. Tool cupboard requirement

    When RequireBuildingPrivilege is enabled (it defaults to false), the plugin skips any vehicle whose GetBuildingPrivilege() returns null. That means only vehicles parked within a cupboard's radius are protected, and a spawn-zone or roadside vehicle still decays normally. Enable it on PvE and RP servers where players park vehicles next to their bases rather than in the open.

  5. Ignore list and player contact

    The IgnoredPrefabs list holds exact ShortPrefabName values that bypass the hook completely, useful for siege vehicles or event entities. Separately, if hitInfo.InitiatorPlayer is set the plugin returns early, so a player deliberately damaging a vehicle never benefits from your multiplier. Both checks happen before the rule lookup, so they win over any config entry.

  6. Config loading and reload

    LoadDefaultConfig builds a PluginConfig with default rules for minicopter.entity, boat.entity, ridablehorse.entity and modularcar.entity, all with multiplier 0. LoadConfig re-reads the file, catches malformed JSON with a warning, and writes the file back through SaveConfig. Because rules are read on every hit through the cached config field, editing the config on disk and reloading the plugin takes effect immediately with no restart.

Install it on your server

  1. Save NoVehicleDecay.cs

    Keep the file name exactly as the class name. Oxide matches them.
  2. Upload it to the plugins folder

    Oxide: oxide/plugins/. Carbon: carbon/plugins/. The server compiles and loads it within a few seconds, no restart needed.
  3. Edit the config if you want

    On first load the default config is written to oxide/config/NoVehicleDecay.json (Carbon: carbon/configs/NoVehicleDecay.json). After editing, reload it:oxide.reload NoVehicleDecay c.reload NoVehicleDecay # Carbon

Variations

Each one opens the generator with a ready prompt you can tweak.

  • Only protect vehicles owned by players

    Skip the multiplier when the vehicle has no OwnerID, so admin-spawned free vehicles still decay.

    Generate this version
  • Chat command to list vehicle rules

    Useful when debugging a config where a prefab is matched by the wrong rule.

    Generate this version
  • Announce decay to the owner

    Warn the owner when a vehicle is about to decay so they can repair it.

    Generate this version

FAQ

Does this stop vehicle decay when nobody is online?

Yes. Decay damage is applied by the server whenever the vehicle is unparked, and the hook runs regardless of who is connected. With a multiplier of 0 the damage is cancelled, so the vehicle stays at full health between wipes. With RequireBuildingPrivilege enabled, only vehicles inside a tool cupboard's range are protected.

Why does my modular car still decay?

Check IgnoredPrefabs first, then confirm the prefab name. The plugin first looks up Vehicles by exact ShortPrefabName, then case-insensitively, then falls back to substring matching on ShortPrefabName for minicopter, boat, ridablehorse and modularcar. If the fallback key (e.g. modularcar.entity) is absent from Vehicles, the default rule of 0.0 still applies, so a car usually stops decaying unless its prefab is on the ignore list.

Can I scale decay above 1?

Yes. Any value above 1 multiplies the decay damage, so abandoned vehicles destroy themselves faster. This is useful on PvP servers that want fewer free vehicles lying around while still protecting vehicles parked at bases through the tool cupboard rule. Values are clamped between 0 and 10.

How do I install it on Oxide or Carbon?

Drop the compiled .cs file into oxide/plugins, restart or reload the server, then edit oxide/config/NoVehicleDecay.json and run oxide.reload NoVehicleDecay. Carbon loads the same Oxide file, so no conversion is needed.

Customize it for your server

Change the reward, add a cooldown, restrict it to VIPs. Start from this prompt and edit anything.

Prompt: Create a Rust plugin that scales decay damage per vehicle type for minicopters, boats, horses and modular cars, with a tool cupboard requirement and ignored prefab list.
Customize for my server

Edit the prompt on the next screen. 2 free plugins a month, no card.

Last updated Oct 26, 2026.

Customize no vehicle decay

Customize