LeeFall

VL with tugboats

Jul 6th, 2023 (edited)
200
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 186.69 KB | None | 0 0
  1. // #define DEBUG
  2.  
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using Facepunch;
  8. using Network;
  9. using Newtonsoft.Json;
  10. using Newtonsoft.Json.Converters;
  11. using Newtonsoft.Json.Linq;
  12. using Oxide.Core;
  13. using Oxide.Core.Plugins;
  14. using Oxide.Game.Rust;
  15. using Rust;
  16. using Rust.Modular;
  17. using UnityEngine;
  18. using Random = UnityEngine.Random;
  19.  
  20. namespace Oxide.Plugins {
  21.   [Info("Vehicle Licence", "Sorrow/TheDoc/Arainrr", "1.7.43")]
  22.   [Description("Allows players to buy vehicles and then spawn or store it")]
  23.   public class VehicleLicence: RustPlugin {
  24.     #region Fields
  25.  
  26.       [PluginReference]
  27.     private readonly Plugin Economics, ServerRewards, Friends, Clans, NoEscape, LandOnCargoShip, RustTranslationAPI;
  28.  
  29.     private
  30.     const string PERMISSION_USE = "vehiclelicence.use";
  31.     private
  32.     const string PERMISSION_ALL = "vehiclelicence.all";
  33.     private
  34.     const string PERMISSION_ADMIN = "vehiclelicence.admin";
  35.  
  36.     private
  37.     const string PERMISSION_BYPASS_COST = "vehiclelicence.bypasscost";
  38.  
  39.     private
  40.     const int ITEMID_FUEL = -946369541;
  41.     private
  42.     const string PREFAB_ITEM_DROP = "assets/prefabs/misc/item drop/item_drop.prefab";
  43.  
  44.     private
  45.     const string PREFAB_TUGBOAT = "assets/content/vehicles/boats/tugboat/tugboat.prefab";
  46.     private
  47.     const string PREFAB_ROWBOAT = "assets/content/vehicles/boats/rowboat/rowboat.prefab";
  48.     private
  49.     const string PREFAB_RHIB = "assets/content/vehicles/boats/rhib/rhib.prefab";
  50.     private
  51.     const string PREFAB_SEDAN = "assets/content/vehicles/sedan_a/sedantest.entity.prefab";
  52.     private
  53.     const string PREFAB_HOTAIRBALLOON = "assets/prefabs/deployable/hot air balloon/hotairballoon.prefab";
  54.     private
  55.     const string PREFAB_MINICOPTER = "assets/content/vehicles/minicopter/minicopter.entity.prefab";
  56.     private
  57.     const string PREFAB_TRANSPORTCOPTER = "assets/content/vehicles/scrap heli carrier/scraptransporthelicopter.prefab";
  58.     private
  59.     const string PREFAB_CHINOOK = "assets/prefabs/npc/ch47/ch47.entity.prefab";
  60.     private
  61.     const string PREFAB_RIDABLEHORSE = "assets/rust.ai/nextai/testridablehorse.prefab";
  62.     private
  63.     const string PREFAB_WORKCART = "assets/content/vehicles/trains/workcart/workcart.entity.prefab";
  64.     private
  65.     const string PREFAB_SEDANRAIL = "assets/content/vehicles/sedan_a/sedanrail.entity.prefab";
  66.     private
  67.     const string PREFAB_MAGNET_CRANE = "assets/content/vehicles/crane_magnet/magnetcrane.entity.prefab";
  68.     private
  69.     const string PREFAB_SUBMARINE_DUO = "assets/content/vehicles/submarine/submarineduo.entity.prefab";
  70.     private
  71.     const string PREFAB_SUBMARINE_SOLO = "assets/content/vehicles/submarine/submarinesolo.entity.prefab";
  72.  
  73.     private
  74.     const string PREFAB_CHASSIS_SMALL = "assets/content/vehicles/modularcar/car_chassis_2module.entity.prefab";
  75.     private
  76.     const string PREFAB_CHASSIS_MEDIUM = "assets/content/vehicles/modularcar/car_chassis_3module.entity.prefab";
  77.     private
  78.     const string PREFAB_CHASSIS_LARGE = "assets/content/vehicles/modularcar/car_chassis_4module.entity.prefab";
  79.  
  80.     private
  81.     const string PREFAB_SNOWMOBILE = "assets/content/vehicles/snowmobiles/snowmobile.prefab";
  82.     private
  83.     const string PREFAB_SNOWMOBILE_TOMAHA = "assets/content/vehicles/snowmobiles/tomahasnowmobile.prefab";
  84.  
  85.     // Train Engine
  86.     private
  87.     const string PREFAB_TRAINENGINE = "assets/content/vehicles/trains/workcart/workcart_aboveground.entity.prefab";
  88.     private
  89.     const string PREFAB_TRAINENGINE_COVERED = "assets/content/vehicles/trains/workcart/workcart_aboveground2.entity.prefab";
  90.     private
  91.     const string PREFAB_TRAINENGINE_LOCOMOTIVE = "assets/content/vehicles/trains/locomotive/locomotive.entity.prefab";
  92.  
  93.     // Train Car
  94.     private
  95.     const string PREFAB_TRAINWAGON_A = "assets/content/vehicles/trains/wagons/trainwagona.entity.prefab";
  96.     private
  97.     const string PREFAB_TRAINWAGON_B = "assets/content/vehicles/trains/wagons/trainwagonb.entity.prefab";
  98.     private
  99.     const string PREFAB_TRAINWAGON_C = "assets/content/vehicles/trains/wagons/trainwagonc.entity.prefab";
  100.     private
  101.     const string PREFAB_TRAINWAGON_UNLOADABLE = "assets/content/vehicles/trains/wagons/trainwagonunloadable.entity.prefab";
  102.     private
  103.     const string PREFAB_TRAINWAGON_UNLOADABLE_FUEL = "assets/content/vehicles/trains/wagons/trainwagonunloadablefuel.entity.prefab";
  104.     private
  105.     const string PREFAB_TRAINWAGON_UNLOADABLE_LOOT = "assets/content/vehicles/trains/wagons/trainwagonunloadableloot.entity.prefab";
  106.     private
  107.     const string PREFAB_CABOOSE = "assets/content/vehicles/trains/caboose/traincaboose.entity.prefab";
  108.  
  109.     private
  110.     const int LAYER_GROUND = Layers.Solid | Layers.Mask.Water;
  111.  
  112.     private readonly object _false = false;
  113.  
  114.     public static VehicleLicence Instance {
  115.       get;
  116.       private set;
  117.     }
  118.  
  119.     public readonly Dictionary < BaseEntity, Vehicle > vehiclesCache = new Dictionary < BaseEntity, Vehicle > ();
  120.     public readonly Dictionary < string, BaseVehicleSettings > allVehicleSettings = new Dictionary < string, BaseVehicleSettings > ();
  121.     public readonly Dictionary < string, string > commandToVehicleType = new Dictionary < string, string > (StringComparer.OrdinalIgnoreCase);
  122.  
  123.     public enum NormalVehicleType {
  124.       Rowboat,
  125.       Tugboat,
  126.       RHIB,
  127.       Sedan,
  128.       HotAirBalloon,
  129.       MiniCopter,
  130.       TransportHelicopter,
  131.       Chinook,
  132.       RidableHorse,
  133.       WorkCart,
  134.       SedanRail,
  135.       MagnetCrane,
  136.       SubmarineSolo,
  137.       SubmarineDuo,
  138.       Snowmobile,
  139.       TomahaSnowmobile
  140.     }
  141.  
  142.     [JsonConverter(typeof (StringEnumConverter))]
  143.     public enum ChassisType {
  144.       Small,
  145.       Medium,
  146.       Large
  147.     }
  148.  
  149.     [JsonConverter(typeof (StringEnumConverter))]
  150.     public enum TrainComponentType {
  151.       Engine,
  152.       CoveredEngine,
  153.       Locomotive,
  154.       WagonA,
  155.       WagonB,
  156.       WagonC,
  157.       Unloadable,
  158.       UnloadableLoot,
  159.       UnloadableFuel,
  160.       Caboose,
  161.     }
  162.  
  163.     #endregion Fields
  164.  
  165.     #region Oxide Hooks
  166.  
  167.     private void Init() {
  168.       LoadData();
  169.       Instance = this;
  170.       permission.RegisterPermission(PERMISSION_USE, this);
  171.       permission.RegisterPermission(PERMISSION_ALL, this);
  172.       permission.RegisterPermission(PERMISSION_ADMIN, this);
  173.       permission.RegisterPermission(PERMISSION_BYPASS_COST, this);
  174.  
  175.       foreach(NormalVehicleType value in Enum.GetValues(typeof (NormalVehicleType))) {
  176.         allVehicleSettings.Add(value.ToString(), GetBaseVehicleSettings(value));
  177.       }
  178.       foreach(var entry in configData.modularVehicles) {
  179.         allVehicleSettings.Add(entry.Key, entry.Value);
  180.       }
  181.       foreach(var entry in configData.trainVehicles) {
  182.         allVehicleSettings.Add(entry.Key, entry.Value);
  183.       }
  184.       foreach(var entry in allVehicleSettings) {
  185.         var settings = entry.Value;
  186.         if (settings.UsePermission && !string.IsNullOrEmpty(settings.Permission)) {
  187.           if (!permission.PermissionExists(settings.Permission, this)) {
  188.             permission.RegisterPermission(settings.Permission, this);
  189.           }
  190.         }
  191.  
  192.         foreach(var perm in settings.CooldownPermissions.Keys) {
  193.           if (!permission.PermissionExists(perm, this)) {
  194.             permission.RegisterPermission(perm, this);
  195.           }
  196.         }
  197.  
  198.         foreach(var command in settings.Commands) {
  199.           if (string.IsNullOrEmpty(command)) {
  200.             continue;
  201.           }
  202.           if (!commandToVehicleType.ContainsKey(command)) {
  203.             commandToVehicleType.Add(command, entry.Key);
  204.           } else {
  205.             PrintError($"You have the same two commands({command}).");
  206.           }
  207.           if (configData.chat.useUniversalCommand) {
  208.             cmd.AddChatCommand(command, this, nameof(CmdUniversal));
  209.           }
  210.           if (!string.IsNullOrEmpty(configData.chat.customKillCommandPrefix)) {
  211.             cmd.AddChatCommand(configData.chat.customKillCommandPrefix + command, this, nameof(CmdCustomKill));
  212.           }
  213.         }
  214.       }
  215.  
  216.       cmd.AddChatCommand(configData.chat.helpCommand, this, nameof(CmdLicenseHelp));
  217.       cmd.AddChatCommand(configData.chat.buyCommand, this, nameof(CmdBuyVehicle));
  218.       cmd.AddChatCommand(configData.chat.spawnCommand, this, nameof(CmdSpawnVehicle));
  219.       cmd.AddChatCommand(configData.chat.recallCommand, this, nameof(CmdRecallVehicle));
  220.       cmd.AddChatCommand(configData.chat.killCommand, this, nameof(CmdKillVehicle));
  221.  
  222.       Unsubscribe(nameof(CanMountEntity));
  223.       Unsubscribe(nameof(OnEntityTakeDamage));
  224.       Unsubscribe(nameof(OnEntityDismounted));
  225.       Unsubscribe(nameof(OnEntityEnter));
  226.       Unsubscribe(nameof(CanLootEntity));
  227.       Unsubscribe(nameof(OnEntitySpawned));
  228.       Unsubscribe(nameof(OnRidableAnimalClaimed));
  229.     }
  230.  
  231.     private void OnServerInitialized() {
  232.       var currentTimestamp = TimeEx.currentTimestamp;
  233.       foreach(var playerData in storedData.playerData) {
  234.         foreach(var entry in playerData.Value) {
  235.           entry.Value.PlayerId = playerData.Key;
  236.           entry.Value.VehicleType = entry.Key;
  237.           if (configData.global.storeVehicle) {
  238.             entry.Value.LastRecall = entry.Value.LastDismount = currentTimestamp;
  239.             if (entry.Value.EntityId == 0) {
  240.               continue;
  241.             }
  242.             NetworkableId id = new NetworkableId(entry.Value.EntityId);
  243.             entry.Value.Entity = BaseNetworkable.serverEntities.Find(id) as BaseEntity;
  244.             if (entry.Value.Entity == null || entry.Value.Entity.IsDestroyed) {
  245.               entry.Value.EntityId = 0;
  246.             } else {
  247.               vehiclesCache.Add(entry.Value.Entity, entry.Value);
  248.             }
  249.           }
  250.         }
  251.       }
  252.       if (configData.global.preventMounting) {
  253.         Subscribe(nameof(CanMountEntity));
  254.       }
  255.       if (configData.global.noDecay) {
  256.         Subscribe(nameof(OnEntityTakeDamage));
  257.       }
  258.       if (configData.global.preventDamagePlayer) {
  259.         Subscribe(nameof(OnEntityEnter));
  260.       }
  261.       if (configData.global.preventLooting) {
  262.         Subscribe(nameof(CanLootEntity));
  263.       }
  264.       if (configData.global.autoClaimFromVendor) {
  265.         Subscribe(nameof(OnEntitySpawned));
  266.         Subscribe(nameof(OnRidableAnimalClaimed));
  267.       }
  268.       if (configData.global.checkVehiclesInterval > 0 && allVehicleSettings.Any(x => x.Value.WipeTime > 0)) {
  269.         Subscribe(nameof(OnEntityDismounted));
  270.         timer.Every(configData.global.checkVehiclesInterval, CheckVehicles);
  271.       }
  272.     }
  273.  
  274.     private void Unload() {
  275.       if (!configData.global.storeVehicle) {
  276.         foreach(var entry in vehiclesCache.ToArray()) {
  277.           if (entry.Key != null && !entry.Key.IsDestroyed) {
  278.             RefundVehicleItems(entry.Value, isUnload: true);
  279.             entry.Key.Kill(BaseNetworkable.DestroyMode.Gib);
  280.           }
  281.           entry.Value.EntityId = 0;
  282.         }
  283.       }
  284.       SaveData();
  285.       Instance = null;
  286.     }
  287.  
  288.     private void OnServerSave() {
  289.       timer.Once(Random.Range(0 f, 60 f), SaveData);
  290.     }
  291.  
  292.     private void OnPlayerConnected(BasePlayer player) {
  293.       if (player == null || !player.userID.IsSteamId()) {
  294.         return;
  295.       }
  296.       if (permission.UserHasPermission(player.UserIDString, PERMISSION_BYPASS_COST)) {
  297.         PurchaseAllVehicles(player.userID);
  298.       }
  299.     }
  300.  
  301.     private void OnEntityDismounted(BaseMountable entity, BasePlayer player) {
  302.       if (entity == null) {
  303.         return;
  304.       }
  305.       var vehicleParent = entity.VehicleParent();
  306.       if (vehicleParent == null || vehicleParent.IsDestroyed) {
  307.         return;
  308.       }
  309.       Vehicle vehicle;
  310.       if (!vehiclesCache.TryGetValue(vehicleParent, out vehicle)) {
  311.         return;
  312.       }
  313.       vehicle.OnDismount();
  314.     }
  315.  
  316.     #region Mount
  317.  
  318.     private object CanMountEntity(BasePlayer friend, BaseMountable entity) {
  319.       if (friend == null || entity == null) {
  320.         return null;
  321.       }
  322.       var vehicleParent = entity.VehicleParent();
  323.       if (vehicleParent == null || vehicleParent.IsDestroyed) {
  324.         return null;
  325.       }
  326.       Vehicle vehicle;
  327.       if (!vehiclesCache.TryGetValue(vehicleParent, out vehicle)) {
  328.         return null;
  329.       }
  330.       if (AreFriends(vehicle.PlayerId, friend.userID)) {
  331.         return null;
  332.       }
  333.       if (configData.global.preventDriverSeat && vehicleParent.HasMountPoints()) {
  334.         foreach(var mountPointInfo in vehicleParent.allMountPoints) {
  335.           if (mountPointInfo != null && mountPointInfo.mountable == entity) {
  336.             if (!mountPointInfo.isDriver) {
  337.               return null;
  338.             }
  339.             break;
  340.           }
  341.         }
  342.       }
  343.       if (HasAdminPermission(friend)) {
  344.         return null;
  345.       }
  346.       SendCantUseMessage(friend, vehicle);
  347.       return _false;
  348.     }
  349.  
  350.     #endregion Mount
  351.  
  352.     #region Loot
  353.  
  354.     private object CanLootEntity(BasePlayer friend, RidableHorse horse) {
  355.       if (friend == null || horse == null) {
  356.         return null;
  357.       }
  358.       return CanLootEntityInternal(friend, horse);
  359.     }
  360.  
  361.     private object CanLootEntity(BasePlayer friend, StorageContainer container) {
  362.       if (friend == null || container == null) {
  363.         return null;
  364.       }
  365.       var parentEntity = container.GetParentEntity();
  366.       if (parentEntity == null) {
  367.         return null;
  368.       }
  369.       return CanLootEntityInternal(friend, parentEntity);
  370.     }
  371.  
  372.     private object CanLootEntityInternal(BasePlayer friend, BaseEntity parentEntity) {
  373.       Vehicle vehicle;
  374.       if (!TryGetVehicle(parentEntity, out vehicle)) {
  375.         return null;
  376.       }
  377.       if (AreFriends(vehicle.PlayerId, friend.userID)) {
  378.         return null;
  379.       }
  380.       if (HasAdminPermission(friend)) {
  381.         return null;
  382.       }
  383.       SendCantUseMessage(friend, vehicle);
  384.       return _false;
  385.     }
  386.  
  387.     #endregion Loot
  388.  
  389.     #region Decay
  390.  
  391.     private void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo) {
  392.       if (entity == null || hitInfo?.damageTypes == null) {
  393.         return;
  394.       }
  395.       if (hitInfo.damageTypes.Has(DamageType.Decay)) {
  396.         Vehicle vehicle;
  397.         if (!TryGetVehicle(entity, out vehicle)) {
  398.           return;
  399.         }
  400.         hitInfo.damageTypes.Scale(DamageType.Decay, 0);
  401.       }
  402.     }
  403.  
  404.     #endregion Decay
  405.  
  406.     #region Claim
  407.  
  408.     private void OnEntitySpawned(BaseSubmarine baseSubmarine) {
  409.       TryClaimVehicle(baseSubmarine);
  410.     }
  411.  
  412.     private void OnEntitySpawned(MotorRowboat motorRowboat) {
  413.       TryClaimVehicle(motorRowboat);
  414.     }
  415.  
  416.     private void OnEntitySpawned(MiniCopter miniCopter) {
  417.       TryClaimVehicle(miniCopter);
  418.     }
  419.  
  420.     private void OnEntitySpawned(Tugboat tugboat) {
  421.       TryClaimVehicle(tugboat);
  422.     }
  423.  
  424.     private void OnRidableAnimalClaimed(BaseRidableAnimal ridableAnimal, BasePlayer player) {
  425.       TryClaimVehicle(ridableAnimal, player);
  426.     }
  427.  
  428.     #endregion Claim
  429.  
  430.     #region Damage
  431.  
  432.     // ScrapTransportHelicopter / ModularCar / TrainEngine / MagnetCrane
  433.     private object OnEntityEnter(TriggerHurtNotChild triggerHurtNotChild, BasePlayer player) {
  434.       if (triggerHurtNotChild == null || player == null || triggerHurtNotChild.SourceEntity == null) {
  435.         return null;
  436.       }
  437.       var sourceEntity = triggerHurtNotChild.SourceEntity;
  438.       if (vehiclesCache.ContainsKey(sourceEntity)) {
  439.         var baseVehicle = sourceEntity as BaseVehicle;
  440.         if (baseVehicle != null && player.userID.IsSteamId()) {
  441.           if (baseVehicle is TrainEngine) {
  442.             var transform = triggerHurtNotChild.transform;
  443.             MoveToPosition(player, transform.position + (Random.value >= 0.5 f ? -transform.right : transform.right) * 2.5 f);
  444.             return _false;
  445.           }
  446.           Vector3 pos;
  447.           if (GetDismountPosition(baseVehicle, player, out pos)) {
  448.             MoveToPosition(player, pos);
  449.           }
  450.         }
  451.         //triggerHurtNotChild.enabled = false;
  452.         return _false;
  453.       }
  454.       return null;
  455.     }
  456.  
  457.     // HotAirBalloon
  458.     private object OnEntityEnter(TriggerHurt triggerHurt, BasePlayer player) {
  459.       if (triggerHurt == null || player == null) {
  460.         return null;
  461.       }
  462.       var sourceEntity = triggerHurt.gameObject.ToBaseEntity();
  463.       if (sourceEntity == null) {
  464.         return null;
  465.       }
  466.       if (vehiclesCache.ContainsKey(sourceEntity)) {
  467.         if (player.userID.IsSteamId()) {
  468.           MoveToPosition(player, sourceEntity.CenterPoint() + Vector3.down);
  469.         }
  470.         //triggerHurt.enabled = false;
  471.         return _false;
  472.       }
  473.       return null;
  474.     }
  475.  
  476.     #endregion Damage
  477.  
  478.     #region Destroy
  479.  
  480.     private void OnEntityDeath(BaseCombatEntity entity, HitInfo info) {
  481.       OnEntityDeathOrKill(entity, true);
  482.     }
  483.  
  484.     private void OnEntityKill(BaseCombatEntity entity) {
  485.       OnEntityDeathOrKill(entity);
  486.     }
  487.  
  488.     #endregion Destroy
  489.  
  490.     #region Reskin
  491.  
  492.     private object OnEntityReskin(BaseEntity entity, ItemSkinDirectory.Skin skin, BasePlayer player) {
  493.       if (entity == null || player == null) {
  494.         return null;
  495.       }
  496.       Vehicle vehicle;
  497.       if (TryGetVehicle(entity, out vehicle)) {
  498.         return _false;
  499.       }
  500.       return null;
  501.     }
  502.  
  503.     #endregion Reskin
  504.  
  505.     #endregion Oxide Hooks
  506.  
  507.     #region Methods
  508.  
  509.     #region Message
  510.  
  511.     private void SendCantUseMessage(BasePlayer friend, Vehicle vehicle) {
  512.       var settings = GetBaseVehicleSettings(vehicle.VehicleType);
  513.       if (settings != null) {
  514.         var player = RustCore.FindPlayerById(vehicle.PlayerId);
  515.         var playerName = player?.displayName ?? ServerMgr.Instance.persistance.GetPlayerName(vehicle.PlayerId) ?? "Unknown";
  516.         Print(friend, Lang("CantUse", friend.UserIDString, settings.DisplayName, $ "<color=#{(player != null && player.IsConnected ? "
  517.           69 D214 " : "
  518.           FF6347 ")}>{playerName}</color>"));
  519.       }
  520.     }
  521.  
  522.     #endregion Message
  523.  
  524.     #region CheckEntity
  525.  
  526.     private void OnEntityDeathOrKill(BaseCombatEntity entity, bool isCrash = false) {
  527.       if (entity == null) {
  528.         return;
  529.       }
  530.       Vehicle vehicle;
  531.       if (!vehiclesCache.TryGetValue(entity, out vehicle)) {
  532.         return;
  533.       }
  534.  
  535.       RefundVehicleItems(vehicle, isCrash);
  536.  
  537.       var settings = GetBaseVehicleSettings(vehicle.VehicleType);
  538.       if (isCrash && settings.RemoveLicenseOnceCrash) {
  539.         RemoveVehicleLicense(vehicle.PlayerId, vehicle.VehicleType);
  540.       }
  541.  
  542.       vehicle.OnDeath();
  543.       vehiclesCache.Remove(entity);
  544.     }
  545.  
  546.     #endregion CheckEntity
  547.  
  548.     #region CheckVehicles
  549.  
  550.     private void CheckVehicles() {
  551.       var currentTimestamp = TimeEx.currentTimestamp;
  552.       foreach(var entry in vehiclesCache.ToArray()) {
  553.         if (entry.Key == null || entry.Key.IsDestroyed) {
  554.           continue;
  555.         }
  556.         if (VehicleIsActive(entry.Key, entry.Value, currentTimestamp)) {
  557.           continue;
  558.         }
  559.  
  560.         if (VehicleAnyMounted(entry.Key)) {
  561.           continue;
  562.         }
  563.         entry.Key.Kill(BaseNetworkable.DestroyMode.Gib);
  564.       }
  565.     }
  566.  
  567.     private bool VehicleIsActive(BaseEntity entity, Vehicle vehicle, double currentTimestamp) {
  568.       var settings = GetBaseVehicleSettings(vehicle.VehicleType);
  569.       if (settings.WipeTime <= 0) {
  570.         return true;
  571.       }
  572.       if (settings.ExcludeCupboard && entity.GetBuildingPrivilege() != null) {
  573.         return true;
  574.       }
  575.       return currentTimestamp - vehicle.LastDismount < settings.WipeTime;
  576.     }
  577.  
  578.     #endregion CheckVehicles
  579.  
  580.     #region Refund
  581.  
  582.     private void RefundVehicleItems(Vehicle vehicle, bool isCrash = false, bool isUnload = false) {
  583.       var entity = vehicle.Entity;
  584.       if (entity == null || entity.IsDestroyed) {
  585.         return;
  586.       }
  587.  
  588.       var settings = GetBaseVehicleSettings(vehicle.VehicleType);
  589.       settings.RefundVehicleItems(vehicle, isCrash, isUnload);
  590.     }
  591.  
  592.     private static void DropItemContainer(BaseEntity entity, ulong playerId, List < Item > collect) {
  593.       var droppedItemContainer = GameManager.server.CreateEntity(PREFAB_ITEM_DROP, entity.GetDropPosition(), entity.transform.rotation) as DroppedItemContainer;
  594.       if (droppedItemContainer != null) {
  595.         droppedItemContainer.inventory = new ItemContainer();
  596.         droppedItemContainer.inventory.ServerInitialize(null, Mathf.Min(collect.Count, droppedItemContainer.maxItemCount));
  597.         droppedItemContainer.inventory.GiveUID();
  598.         droppedItemContainer.inventory.entityOwner = droppedItemContainer;
  599.         droppedItemContainer.inventory.SetFlag(ItemContainer.Flag.NoItemInput, true);
  600.         for (var i = collect.Count - 1; i >= 0; i--) {
  601.           var item = collect[i];
  602.           if (!item.MoveToContainer(droppedItemContainer.inventory)) {
  603.             item.DropAndTossUpwards(droppedItemContainer.transform.position);
  604.           }
  605.         }
  606.  
  607.         droppedItemContainer.OwnerID = playerId;
  608.         droppedItemContainer.Spawn();
  609.       }
  610.     }
  611.  
  612.     #endregion Refund
  613.  
  614.     #region TryPay
  615.  
  616.     private bool TryPay(BasePlayer player, Dictionary < string, PriceInfo > prices, out string resources) {
  617.       if (permission.UserHasPermission(player.UserIDString, PERMISSION_BYPASS_COST)) {
  618.         resources = null;
  619.         return true;
  620.       }
  621.  
  622.       if (!CanPay(player, prices, out resources)) {
  623.         return false;
  624.       }
  625.  
  626.       var collect = Pool.GetList < Item > ();
  627.       foreach(var entry in prices) {
  628.         if (entry.Value.amount <= 0) {
  629.           continue;
  630.         }
  631.         var itemDefinition = ItemManager.FindItemDefinition(entry.Key);
  632.         if (itemDefinition != null) {
  633.           player.inventory.Take(collect, itemDefinition.itemid, entry.Value.amount);
  634.           player.Command("note.inv", itemDefinition.itemid, -entry.Value.amount);
  635.           continue;
  636.         }
  637.         switch (entry.Key.ToLower()) {
  638.         case "economics":
  639.           Economics?.Call("Withdraw", player.userID, (double) entry.Value.amount);
  640.           continue;
  641.  
  642.         case "serverrewards":
  643.           ServerRewards?.Call("TakePoints", player.userID, entry.Value.amount);
  644.           continue;
  645.         }
  646.       }
  647.  
  648.       foreach(var item in collect) {
  649.         item.Remove();
  650.       }
  651.       Pool.FreeList(ref collect);
  652.       resources = null;
  653.       return true;
  654.     }
  655.  
  656.     private bool CanPay(BasePlayer player, Dictionary < string, PriceInfo > prices, out string resources) {
  657.       var entries = new Hash < string,
  658.         int > ();
  659.       var language = RustTranslationAPI != null ? lang.GetLanguage(player.UserIDString) : null;
  660.       foreach(var entry in prices) {
  661.         if (entry.Value.amount <= 0) {
  662.           continue;
  663.         }
  664.         int missingAmount;
  665.         var itemDefinition = ItemManager.FindItemDefinition(entry.Key);
  666.         if (itemDefinition != null) {
  667.           missingAmount = entry.Value.amount - player.inventory.GetAmount(itemDefinition.itemid);
  668.         } else {
  669.           missingAmount = CheckBalance(entry.Key, entry.Value.amount, player.userID);
  670.         }
  671.  
  672.         if (missingAmount <= 0) {
  673.           continue;
  674.         }
  675.         var displayName = GetItemDisplayName(language, entry.Key, entry.Value.displayName);
  676.         entries[displayName] += missingAmount;
  677.       }
  678.       if (entries.Count > 0) {
  679.         var stringBuilder = new StringBuilder();
  680.         foreach(var entry in entries) {
  681.           stringBuilder.AppendLine($"* {Lang("
  682.             PriceFormat ", player.UserIDString, entry.Key, entry.Value)}");
  683.         }
  684.         resources = stringBuilder.ToString();
  685.         return false;
  686.       }
  687.       resources = null;
  688.       return true;
  689.     }
  690.  
  691.     private int CheckBalance(string key, int price, ulong playerId) {
  692.       switch (key.ToLower()) {
  693.       case "economics":
  694.         var balance = Economics?.Call("Balance", playerId);
  695.         if (balance is double) {
  696.           var n = price - (double) balance;
  697.           return n <= 0 ? 0 : (int) Math.Ceiling(n);
  698.         }
  699.         return price;
  700.  
  701.       case "serverrewards":
  702.         var points = ServerRewards?.Call("CheckPoints", playerId);
  703.         if (points is int) {
  704.           var n = price - (int) points;
  705.           return n <= 0 ? 0 : n;
  706.         }
  707.         return price;
  708.  
  709.       default:
  710.         PrintError($"Unknown Currency Type '{key}'");
  711.         return price;
  712.       }
  713.     }
  714.  
  715.     #endregion TryPay
  716.  
  717.     #region AreFriends
  718.  
  719.     private bool AreFriends(ulong playerId, ulong friendId) {
  720.       if (playerId == friendId) {
  721.         return true;
  722.       }
  723.       if (configData.global.useTeams && SameTeam(playerId, friendId)) {
  724.         return true;
  725.       }
  726.  
  727.       if (configData.global.useFriends && HasFriend(playerId, friendId)) {
  728.         return true;
  729.       }
  730.       if (configData.global.useClans && SameClan(playerId, friendId)) {
  731.         return true;
  732.       }
  733.       return false;
  734.     }
  735.  
  736.     private static bool SameTeam(ulong playerId, ulong friendId) {
  737.       if (!RelationshipManager.TeamsEnabled()) {
  738.         return false;
  739.       }
  740.       var playerTeam = RelationshipManager.ServerInstance.FindPlayersTeam(playerId);
  741.       if (playerTeam == null) {
  742.         return false;
  743.       }
  744.       var friendTeam = RelationshipManager.ServerInstance.FindPlayersTeam(friendId);
  745.       if (friendTeam == null) {
  746.         return false;
  747.       }
  748.       return playerTeam == friendTeam;
  749.     }
  750.  
  751.     private bool HasFriend(ulong playerId, ulong friendId) {
  752.       if (Friends == null) {
  753.         return false;
  754.       }
  755.       return (bool) Friends.Call("HasFriend", playerId, friendId);
  756.     }
  757.  
  758.     private bool SameClan(ulong playerId, ulong friendId) {
  759.       if (Clans == null) {
  760.         return false;
  761.       }
  762.       //Clans
  763.       var isMember = Clans.Call("IsClanMember", playerId.ToString(), friendId.ToString());
  764.       if (isMember != null) {
  765.         return (bool) isMember;
  766.       }
  767.       //Rust:IO Clans
  768.       var playerClan = Clans.Call("GetClanOf", playerId);
  769.       if (playerClan == null) {
  770.         return false;
  771.       }
  772.       var friendClan = Clans.Call("GetClanOf", friendId);
  773.       if (friendClan == null) {
  774.         return false;
  775.       }
  776.       return playerClan == friendClan;
  777.     }
  778.  
  779.     #endregion AreFriends
  780.  
  781.     #region IsPlayerBlocked
  782.  
  783.     private bool IsPlayerBlocked(BasePlayer player) {
  784.       if (NoEscape == null) {
  785.         return false;
  786.       }
  787.       if (configData.global.useRaidBlocker && IsRaidBlocked(player.UserIDString)) {
  788.         Print(player, Lang("RaidBlocked", player.UserIDString));
  789.         return true;
  790.       }
  791.       if (configData.global.useCombatBlocker && IsCombatBlocked(player.UserIDString)) {
  792.         Print(player, Lang("CombatBlocked", player.UserIDString));
  793.         return true;
  794.       }
  795.       return false;
  796.     }
  797.  
  798.     private bool IsRaidBlocked(string playerId) {
  799.       return (bool) NoEscape.Call("IsRaidBlocked", playerId);
  800.     }
  801.  
  802.     private bool IsCombatBlocked(string playerId) {
  803.       return (bool) NoEscape.Call("IsCombatBlocked", playerId);
  804.     }
  805.  
  806.     #endregion IsPlayerBlocked
  807.  
  808.     #region GetSettings
  809.  
  810.     private BaseVehicleSettings GetBaseVehicleSettings(string vehicleType) {
  811.       BaseVehicleSettings settings;
  812.       return allVehicleSettings.TryGetValue(vehicleType, out settings) ? settings : null;
  813.     }
  814.  
  815.     private BaseVehicleSettings GetBaseVehicleSettings(NormalVehicleType normalVehicleType) {
  816.       switch (normalVehicleType) {
  817.       case NormalVehicleType.Tugboat:
  818.         return configData.normalVehicles.tugboat;
  819.       case NormalVehicleType.Rowboat:
  820.         return configData.normalVehicles.rowboat;
  821.       case NormalVehicleType.RHIB:
  822.         return configData.normalVehicles.rhib;
  823.       case NormalVehicleType.Sedan:
  824.         return configData.normalVehicles.sedan;
  825.       case NormalVehicleType.HotAirBalloon:
  826.         return configData.normalVehicles.hotAirBalloon;
  827.       case NormalVehicleType.MiniCopter:
  828.         return configData.normalVehicles.miniCopter;
  829.       case NormalVehicleType.TransportHelicopter:
  830.         return configData.normalVehicles.transportHelicopter;
  831.       case NormalVehicleType.Chinook:
  832.         return configData.normalVehicles.chinook;
  833.       case NormalVehicleType.RidableHorse:
  834.         return configData.normalVehicles.ridableHorse;
  835.       case NormalVehicleType.WorkCart:
  836.         return configData.normalVehicles.workCart;
  837.       case NormalVehicleType.SedanRail:
  838.         return configData.normalVehicles.sedanRail;
  839.       case NormalVehicleType.MagnetCrane:
  840.         return configData.normalVehicles.magnetCrane;
  841.       case NormalVehicleType.SubmarineSolo:
  842.         return configData.normalVehicles.submarineSolo;
  843.       case NormalVehicleType.SubmarineDuo:
  844.         return configData.normalVehicles.submarineDuo;
  845.       case NormalVehicleType.Snowmobile:
  846.         return configData.normalVehicles.snowmobile;
  847.       case NormalVehicleType.TomahaSnowmobile:
  848.         return configData.normalVehicles.tomahaSnowmobile;
  849.       default:
  850.         return null;
  851.       }
  852.     }
  853.  
  854.     #endregion GetSettings
  855.  
  856.     #region Permission
  857.  
  858.     private bool HasAdminPermission(BasePlayer player) {
  859.       return permission.UserHasPermission(player.UserIDString, PERMISSION_ADMIN);
  860.     }
  861.  
  862.     private bool CanViewVehicleInfo(BasePlayer player, string vehicleType, BaseVehicleSettings settings) {
  863.       if (settings.Purchasable && settings.Commands.Count > 0) {
  864.         return HasVehiclePermission(player, vehicleType);
  865.       }
  866.       return false;
  867.     }
  868.  
  869.     private bool HasVehiclePermission(BasePlayer player, string vehicleType) {
  870.       var settings = GetBaseVehicleSettings(vehicleType);
  871.       if (!settings.UsePermission || string.IsNullOrEmpty(settings.Permission)) {
  872.         return true;
  873.       }
  874.       return permission.UserHasPermission(player.UserIDString, PERMISSION_ALL) ||
  875.         permission.UserHasPermission(player.UserIDString, settings.Permission);
  876.     }
  877.  
  878.     #endregion Permission
  879.  
  880.     #region Claim
  881.  
  882.     private void TryClaimVehicle(BaseVehicle baseVehicle) {
  883.       NextTick(() => {
  884.         if (baseVehicle == null) {
  885.           return;
  886.         }
  887.         var player = baseVehicle.creatorEntity as BasePlayer;
  888.         if (player == null || !player.userID.IsSteamId() || !baseVehicle.OnlyOwnerAccessible()) {
  889.           return;
  890.         }
  891.         var vehicleType = GetClaimableVehicleType(baseVehicle);
  892.         if (vehicleType.HasValue) {
  893.           TryClaimVehicle(player, baseVehicle, vehicleType.Value.ToString());
  894.         }
  895.       });
  896.     }
  897.  
  898.     private void TryClaimVehicle(BaseVehicle baseVehicle, BasePlayer player) {
  899.       if (player == null || !player.userID.IsSteamId()) {
  900.         return;
  901.       }
  902.       var vehicleType = GetClaimableVehicleType(baseVehicle);
  903.       if (vehicleType.HasValue) {
  904.         TryClaimVehicle(player, baseVehicle, vehicleType.Value.ToString());
  905.       }
  906.     }
  907.  
  908.     private bool TryClaimVehicle(BasePlayer player, BaseEntity entity, string vehicleType) {
  909.       Vehicle vehicle;
  910.       if (!storedData.IsVehiclePurchased(player.userID, vehicleType, out vehicle)) {
  911.         if (!configData.global.autoUnlockFromVendor) {
  912.           return false;
  913.         }
  914.         storedData.AddVehicleLicense(player.userID, vehicleType);
  915.         vehicle = storedData.GetVehicleLicense(player.userID, vehicleType);
  916.       }
  917.       if (vehicle.Entity == null || vehicle.Entity.IsDestroyed) {
  918.         var settings = GetBaseVehicleSettings(vehicle.VehicleType);
  919.         if (settings != null) {
  920.           settings.PreSetupVehicle(entity, vehicle, player);
  921.           settings.SetupVehicle(entity, vehicle, player, false);
  922.         }
  923.         CacheVehicleEntity(entity, vehicle, player);
  924.         return true;
  925.       }
  926.       return false;
  927.     }
  928.  
  929.     #endregion Claim
  930.  
  931.     private bool TryGetVehicle(BaseEntity entity, out Vehicle vehicle) {
  932.       if (!vehiclesCache.TryGetValue(entity, out vehicle)) {
  933.         var vehicleModule = entity as BaseVehicleModule;
  934.         if (vehicleModule == null) {
  935.           return false;
  936.         }
  937.         var parent = vehicleModule.Vehicle;
  938.         if (parent == null || !vehiclesCache.TryGetValue(parent, out vehicle)) {
  939.           return false;
  940.         }
  941.       }
  942.       return true;
  943.     }
  944.  
  945.     #region Helpers
  946.  
  947.     private static NormalVehicleType ? GetClaimableVehicleType(BaseVehicle baseVehicle) {
  948.       if (baseVehicle is BaseRidableAnimal) {
  949.         return NormalVehicleType.RidableHorse;
  950.       }
  951.       if (baseVehicle is ScrapTransportHelicopter) {
  952.         return NormalVehicleType.TransportHelicopter;
  953.       }
  954.       if (baseVehicle is MiniCopter) {
  955.         return NormalVehicleType.MiniCopter;
  956.       }
  957.       if (baseVehicle is RHIB) {
  958.         return NormalVehicleType.RHIB;
  959.       }
  960.       if (baseVehicle is Tugboat) {
  961.         return NormalVehicleType.Tugboat;
  962.       }
  963.       if (baseVehicle is MotorRowboat) {
  964.         return NormalVehicleType.Rowboat;
  965.       }
  966.       if (baseVehicle is SubmarineDuo) {
  967.         return NormalVehicleType.SubmarineDuo;
  968.       }
  969.       if (baseVehicle is BaseSubmarine) {
  970.         return NormalVehicleType.SubmarineSolo;
  971.       }
  972.       return null;
  973.     }
  974.  
  975.     private static bool GetDismountPosition(BaseVehicle baseVehicle, BasePlayer player, out Vector3 result) {
  976.       var parentVehicle = baseVehicle.VehicleParent();
  977.       if (parentVehicle != null) {
  978.         return GetDismountPosition(parentVehicle, player, out result);
  979.       }
  980.       var list = Pool.GetList < Vector3 > ();
  981.       foreach(var transform in baseVehicle.dismountPositions) {
  982.         if (baseVehicle.ValidDismountPosition(player, transform.position)) {
  983.           list.Add(transform.position);
  984.           if (baseVehicle.dismountStyle == BaseVehicle.DismountStyle.Ordered) {
  985.             break;
  986.           }
  987.         }
  988.       }
  989.       if (list.Count == 0) {
  990.         result = Vector3.zero;
  991.         Pool.FreeList(ref list);
  992.         return false;
  993.       }
  994.       var pos = player.transform.position;
  995.       list.Sort((a, b) => Vector3.Distance(a, pos).CompareTo(Vector3.Distance(b, pos)));
  996.       result = list[0];
  997.       Pool.FreeList(ref list);
  998.       return true;
  999.     }
  1000.  
  1001.     private static bool VehicleAnyMounted(BaseEntity entity) {
  1002.       var baseVehicle = entity as BaseVehicle;
  1003.       if (baseVehicle != null && baseVehicle.AnyMounted()) {
  1004.         return true;
  1005.       }
  1006.       return entity.GetComponentsInChildren < BasePlayer > ()?.Length > 0;
  1007.     }
  1008.  
  1009.     private static void DismountAllPlayers(BaseEntity entity) {
  1010.       var baseVehicle = entity as BaseVehicle;
  1011.       if (baseVehicle != null) {
  1012.         //(vehicle as BaseVehicle).DismountAllPlayers();
  1013.         foreach(var mountPointInfo in baseVehicle.allMountPoints) {
  1014.           if (mountPointInfo != null && mountPointInfo.mountable != null) {
  1015.             var mounted = mountPointInfo.mountable.GetMounted();
  1016.             if (mounted != null) {
  1017.               mountPointInfo.mountable.DismountPlayer(mounted);
  1018.             }
  1019.           }
  1020.         }
  1021.       }
  1022.       var players = entity.GetComponentsInChildren < BasePlayer > ();
  1023.       foreach(var player in players) {
  1024.         player.SetParent(null, true, true);
  1025.       }
  1026.     }
  1027.  
  1028.     private static Vector3 GetGroundPositionLookingAt(BasePlayer player, float distance, bool needUp = true) {
  1029.       RaycastHit hitInfo;
  1030.       var headRay = player.eyes.HeadRay();
  1031.       if (Physics.Raycast(headRay, out hitInfo, distance, LAYER_GROUND)) {
  1032.         return hitInfo.point;
  1033.       }
  1034.       return GetGroundPosition(headRay.origin + headRay.direction * distance, needUp);
  1035.     }
  1036.  
  1037.     private static Vector3 GetGroundPosition(Vector3 position, bool needUp = true) {
  1038.       RaycastHit hitInfo;
  1039.       position.y = Physics.Raycast(needUp ? position + Vector3.up * 250 : position, Vector3.down, out hitInfo, needUp ? 400 f : 50 f, LAYER_GROUND) ?
  1040.         hitInfo.point.y :
  1041.         TerrainMeta.HeightMap.GetHeight(position);
  1042.       return position;
  1043.     }
  1044.  
  1045.     private static bool IsInWater(Vector3 position) {
  1046.       var colliders = Pool.GetList < Collider > ();
  1047.       Vis.Colliders(position, 0.5 f, colliders);
  1048.       var flag = colliders.Any(x => x.gameObject.layer == (int) Layer.Water);
  1049.       Pool.FreeList(ref colliders);
  1050.       return flag || WaterLevel.Test(position, false, false);
  1051.     }
  1052.  
  1053.     private static void MoveToPosition(BasePlayer player, Vector3 position) {
  1054.       player.Teleport(position);
  1055.       player.ForceUpdateTriggers();
  1056.       //if (player.HasParent()) player.SetParent(null, true, true);
  1057.       player.SendNetworkUpdateImmediate();
  1058.     }
  1059.  
  1060.     #region Train Car
  1061.  
  1062.     #endregion
  1063.  
  1064.     #endregion Helpers
  1065.  
  1066.     #endregion Methods
  1067.  
  1068.     #region API
  1069.  
  1070.       [HookMethod(nameof(IsLicensedVehicle))]
  1071.     public bool IsLicensedVehicle(BaseEntity entity) {
  1072.       return vehiclesCache.ContainsKey(entity);
  1073.     }
  1074.  
  1075.     [HookMethod(nameof(GetLicensedVehicle))]
  1076.     public BaseEntity GetLicensedVehicle(ulong playerId, string license) {
  1077.       return storedData.GetVehicleLicense(playerId, license)?.Entity;
  1078.     }
  1079.  
  1080.     [HookMethod(nameof(HasVehicleLicense))]
  1081.     public bool HasVehicleLicense(ulong playerId, string license) {
  1082.       return storedData.HasVehicleLicense(playerId, license);
  1083.     }
  1084.  
  1085.     [HookMethod(nameof(RemoveVehicleLicense))]
  1086.     public bool RemoveVehicleLicense(ulong playerId, string license) {
  1087.       return storedData.RemoveVehicleLicense(playerId, license);
  1088.     }
  1089.  
  1090.     [HookMethod(nameof(AddVehicleLicense))]
  1091.     public bool AddVehicleLicense(ulong playerId, string license) {
  1092.       return storedData.AddVehicleLicense(playerId, license);
  1093.     }
  1094.  
  1095.     [HookMethod(nameof(GetVehicleLicenses))]
  1096.     public List < string > GetVehicleLicenses(ulong playerId) {
  1097.       return storedData.GetVehicleLicenseNames(playerId);
  1098.     }
  1099.  
  1100.     [HookMethod(nameof(PurchaseAllVehicles))]
  1101.     public void PurchaseAllVehicles(ulong playerId) {
  1102.       storedData.PurchaseAllVehicles(playerId);
  1103.     }
  1104.  
  1105.     #endregion API
  1106.  
  1107.     #region Commands
  1108.  
  1109.     #region Universal Command
  1110.  
  1111.     private void CmdUniversal(BasePlayer player, string command, string[] args) {
  1112.       if (!permission.UserHasPermission(player.UserIDString, PERMISSION_USE)) {
  1113.         Print(player, Lang("NotAllowed", player.UserIDString));
  1114.         return;
  1115.       }
  1116.  
  1117.       string vehicleType;
  1118.       if (IsValidOption(player, command, out vehicleType)) {
  1119.         var bypassCooldown = args.Length > 0 && IsValidBypassCooldownOption(args[0]);
  1120.         HandleUniversalCmd(player, vehicleType, bypassCooldown, command);
  1121.       }
  1122.     }
  1123.  
  1124.     private void HandleUniversalCmd(BasePlayer player, string vehicleType, bool bypassCooldown, string command) {
  1125.       Vehicle vehicle;
  1126.       if (storedData.IsVehiclePurchased(player.userID, vehicleType, out vehicle)) {
  1127.         string reason;
  1128.         var position = Vector3.zero;
  1129.         var rotation = Quaternion.identity;
  1130.         if (vehicle.Entity != null && !vehicle.Entity.IsDestroyed) {
  1131.           //recall
  1132.           if (CanRecall(player, vehicle, bypassCooldown, command, out reason, ref position, ref rotation)) {
  1133.             RecallVehicle(player, vehicle, position, rotation);
  1134.             return;
  1135.           }
  1136.         } else {
  1137.           //spawn
  1138.           if (CanSpawn(player, vehicle, bypassCooldown, command, out reason, ref position, ref rotation)) {
  1139.             SpawnVehicle(player, vehicle, position, rotation);
  1140.             return;
  1141.           }
  1142.         }
  1143.         Print(player, reason);
  1144.         return;
  1145.       }
  1146.       //buy
  1147.       BuyVehicle(player, vehicleType);
  1148.     }
  1149.  
  1150.     #endregion Universal Command
  1151.  
  1152.     #region Custom Kill Command
  1153.  
  1154.     private void CmdCustomKill(BasePlayer player, string command, string[] args) {
  1155.       if (!permission.UserHasPermission(player.UserIDString, PERMISSION_USE)) {
  1156.         Print(player, Lang("NotAllowed", player.UserIDString));
  1157.         return;
  1158.       }
  1159.       command = command.Remove(0, configData.chat.customKillCommandPrefix.Length);
  1160.       HandleKillCmd(player, command);
  1161.     }
  1162.  
  1163.     #endregion Custom Kill Command
  1164.  
  1165.     #region Help Command
  1166.  
  1167.     private void CmdLicenseHelp(BasePlayer player, string command, string[] args) {
  1168.       var stringBuilder = new StringBuilder();
  1169.       stringBuilder.AppendLine(Lang("Help", player.UserIDString));
  1170.       stringBuilder.AppendLine(Lang("HelpLicence1", player.UserIDString, configData.chat.buyCommand));
  1171.       stringBuilder.AppendLine(Lang("HelpLicence2", player.UserIDString, configData.chat.spawnCommand));
  1172.       stringBuilder.AppendLine(Lang("HelpLicence3", player.UserIDString, configData.chat.recallCommand));
  1173.       stringBuilder.AppendLine(Lang("HelpLicence4", player.UserIDString, configData.chat.killCommand));
  1174.  
  1175.       foreach(var entry in allVehicleSettings) {
  1176.         if (CanViewVehicleInfo(player, entry.Key, entry.Value)) {
  1177.           if (configData.chat.useUniversalCommand) {
  1178.             var firstCmd = entry.Value.Commands[0];
  1179.             stringBuilder.AppendLine(Lang("HelpLicence5", player.UserIDString, firstCmd, entry.Value.DisplayName));
  1180.           }
  1181.         }
  1182.       }
  1183.       Print(player, stringBuilder.ToString());
  1184.     }
  1185.  
  1186.     #endregion Help Command
  1187.  
  1188.     #region Remove Command
  1189.  
  1190.       [ConsoleCommand("vl.remove")]
  1191.     private void CCmdRemoveVehicle(ConsoleSystem.Arg arg) {
  1192.       if (arg.IsAdmin && arg.Args != null && arg.Args.Length == 2) {
  1193.         var option = arg.Args[0];
  1194.         string vehicleType;
  1195.         if (!IsValidVehicleType(option, out vehicleType)) {
  1196.           Print(arg, $ "{option} is not a valid vehicle type");
  1197.           return;
  1198.         }
  1199.         switch (arg.Args[1].ToLower()) {
  1200.         case "*":
  1201.         case "all": {
  1202.           storedData.RemoveLicenseForAllPlayers(vehicleType);
  1203.           var vehicleName = GetBaseVehicleSettings(vehicleType).DisplayName;
  1204.           Print(arg, $ "You successfully removed the vehicle({vehicleName}) of all players");
  1205.         }
  1206.         return;
  1207.  
  1208.         default: {
  1209.           var target = RustCore.FindPlayer(arg.Args[1]);
  1210.           if (target == null) {
  1211.             Print(arg, $ "Player '{arg.Args[1]}' not found");
  1212.             return;
  1213.           }
  1214.  
  1215.           var vehicleName = GetBaseVehicleSettings(vehicleType).DisplayName;
  1216.           if (RemoveVehicleLicense(target.userID, vehicleType)) {
  1217.             Print(arg, $ "You successfully removed the vehicle({vehicleName}) of {target.displayName}");
  1218.             return;
  1219.           }
  1220.  
  1221.           Print(arg, $ "{target.displayName} has not purchased vehicle({vehicleName}) and cannot be removed");
  1222.         }
  1223.         return;
  1224.         }
  1225.       }
  1226.     }
  1227.  
  1228.     [ConsoleCommand("vl.cleardata")]
  1229.     private void CCmdClearVehicle(ConsoleSystem.Arg arg) {
  1230.       if (arg.IsAdmin) {
  1231.         foreach(var vehicle in vehiclesCache.Keys.ToArray()) {
  1232.           vehicle.Kill(BaseNetworkable.DestroyMode.Gib);
  1233.         }
  1234.         vehiclesCache.Clear();
  1235.         ClearData();
  1236.         Print(arg, "You successfully cleaned up all vehicle data");
  1237.       }
  1238.     }
  1239.  
  1240.     #endregion Remove Command
  1241.  
  1242.     #region Buy Command
  1243.  
  1244.       [ConsoleCommand("vl.buy")]
  1245.     private void CCmdBuyVehicle(ConsoleSystem.Arg arg) {
  1246.       if (arg.IsAdmin && arg.Args != null && arg.Args.Length == 2) {
  1247.         var option = arg.Args[0];
  1248.         string vehicleType;
  1249.         if (!IsValidVehicleType(option, out vehicleType)) {
  1250.           Print(arg, $ "{option} is not a valid vehicle type");
  1251.           return;
  1252.         }
  1253.         switch (arg.Args[1].ToLower()) {
  1254.         case "*":
  1255.         case "all": {
  1256.           storedData.AddLicenseForAllPlayers(vehicleType);
  1257.           var vehicleName = GetBaseVehicleSettings(vehicleType).DisplayName;
  1258.           Print(arg, $ "You successfully purchased the vehicle({vehicleName}) for all players");
  1259.         }
  1260.         return;
  1261.  
  1262.         default: {
  1263.           var target = RustCore.FindPlayer(arg.Args[1]);
  1264.           if (target == null) {
  1265.             Print(arg, $ "Player '{arg.Args[1]}' not found");
  1266.             return;
  1267.           }
  1268.  
  1269.           var vehicleName = GetBaseVehicleSettings(vehicleType).DisplayName;
  1270.           if (AddVehicleLicense(target.userID, vehicleType)) {
  1271.             Print(arg, $ "You successfully purchased the vehicle({vehicleName}) for {target.displayName}");
  1272.             return;
  1273.           }
  1274.  
  1275.           Print(arg, $ "{target.displayName} has purchased vehicle({vehicleName})");
  1276.         }
  1277.         return;
  1278.         }
  1279.       }
  1280.       var player = arg.Player();
  1281.       if (player == null) {
  1282.         Print(arg, $ "The server console cannot use the '{arg.cmd.FullName}' command");
  1283.       } else {
  1284.         CmdBuyVehicle(player, arg.cmd.FullName, arg.Args);
  1285.       }
  1286.     }
  1287.  
  1288.     private void CmdBuyVehicle(BasePlayer player, string command, string[] args) {
  1289.       if (!permission.UserHasPermission(player.UserIDString, PERMISSION_USE)) {
  1290.         Print(player, Lang("NotAllowed", player.UserIDString));
  1291.         return;
  1292.       }
  1293.       if (args == null || args.Length < 1) {
  1294.         var stringBuilder = new StringBuilder();
  1295.         stringBuilder.AppendLine(Lang("Help", player.UserIDString));
  1296.         foreach(var entry in allVehicleSettings) {
  1297.           if (CanViewVehicleInfo(player, entry.Key, entry.Value)) {
  1298.             var firstCmd = entry.Value.Commands[0];
  1299.             if (entry.Value.PurchasePrices.Count > 0) {
  1300.               var prices = FormatPriceInfo(player, entry.Value.PurchasePrices);
  1301.               stringBuilder.AppendLine(Lang("HelpBuyPrice", player.UserIDString, configData.chat.buyCommand, firstCmd, entry.Value.DisplayName, prices));
  1302.             } else {
  1303.               stringBuilder.AppendLine(Lang("HelpBuy", player.UserIDString, configData.chat.buyCommand, firstCmd, entry.Value.DisplayName));
  1304.             }
  1305.           }
  1306.         }
  1307.         Print(player, stringBuilder.ToString());
  1308.         return;
  1309.       }
  1310.       string vehicleType;
  1311.       if (IsValidOption(player, args[0], out vehicleType)) {
  1312.         BuyVehicle(player, vehicleType);
  1313.       }
  1314.     }
  1315.  
  1316.     private bool BuyVehicle(BasePlayer player, string vehicleType) {
  1317.       var settings = GetBaseVehicleSettings(vehicleType);
  1318.       if (!settings.Purchasable) {
  1319.         Print(player, Lang("VehicleCannotBeBought", player.UserIDString, settings.DisplayName));
  1320.         return false;
  1321.       }
  1322.       var vehicles = storedData.GetPlayerVehicles(player.userID, false);
  1323.       if (vehicles.ContainsKey(vehicleType)) {
  1324.         Print(player, Lang("VehicleAlreadyPurchased", player.UserIDString, settings.DisplayName));
  1325.         return false;
  1326.       }
  1327.       string resources;
  1328.       if (settings.PurchasePrices.Count > 0 && !TryPay(player, settings.PurchasePrices, out resources)) {
  1329.         Print(player, Lang("NoResourcesToPurchaseVehicle", player.UserIDString, settings.DisplayName, resources));
  1330.         return false;
  1331.       }
  1332.       vehicles.Add(vehicleType, Vehicle.Create(player.userID, vehicleType));
  1333.       SaveData();
  1334.       Print(player, Lang("VehiclePurchased", player.UserIDString, settings.DisplayName, configData.chat.spawnCommand));
  1335.       return true;
  1336.     }
  1337.  
  1338.     #endregion Buy Command
  1339.  
  1340.     #region Spawn Command
  1341.  
  1342.       [ConsoleCommand("vl.spawn")]
  1343.     private void CCmdSpawnVehicle(ConsoleSystem.Arg arg) {
  1344.       var player = arg.Player();
  1345.       if (player == null) {
  1346.         Print(arg, $ "The server console cannot use the '{arg.cmd.FullName}' command");
  1347.       } else {
  1348.         CmdSpawnVehicle(player, arg.cmd.FullName, arg.Args);
  1349.       }
  1350.     }
  1351.  
  1352.     private void CmdSpawnVehicle(BasePlayer player, string command, string[] args) {
  1353.       if (!permission.UserHasPermission(player.UserIDString, PERMISSION_USE)) {
  1354.         Print(player, Lang("NotAllowed", player.UserIDString));
  1355.         return;
  1356.       }
  1357.       if (args == null || args.Length < 1) {
  1358.         var stringBuilder = new StringBuilder();
  1359.         stringBuilder.AppendLine(Lang("Help", player.UserIDString));
  1360.         foreach(var entry in allVehicleSettings) {
  1361.           if (CanViewVehicleInfo(player, entry.Key, entry.Value)) {
  1362.             var firstCmd = entry.Value.Commands[0];
  1363.             if (entry.Value.SpawnPrices.Count > 0) {
  1364.               var prices = FormatPriceInfo(player, entry.Value.SpawnPrices);
  1365.               stringBuilder.AppendLine(Lang("HelpSpawnPrice", player.UserIDString, configData.chat.spawnCommand, firstCmd, entry.Value.DisplayName, prices));
  1366.             } else {
  1367.               stringBuilder.AppendLine(Lang("HelpSpawn", player.UserIDString, configData.chat.spawnCommand, firstCmd, entry.Value.DisplayName));
  1368.             }
  1369.           }
  1370.         }
  1371.         Print(player, stringBuilder.ToString());
  1372.         return;
  1373.       }
  1374.       string vehicleType;
  1375.       if (IsValidOption(player, args[0], out vehicleType)) {
  1376.         var bypassCooldown = args.Length > 1 && IsValidBypassCooldownOption(args[1]);
  1377.         SpawnVehicle(player, vehicleType, bypassCooldown, command + " " + args[0]);
  1378.       }
  1379.     }
  1380.  
  1381.     private bool SpawnVehicle(BasePlayer player, string vehicleType, bool bypassCooldown, string command) {
  1382.       var settings = GetBaseVehicleSettings(vehicleType);
  1383.       Vehicle vehicle;
  1384.       if (!storedData.IsVehiclePurchased(player.userID, vehicleType, out vehicle)) {
  1385.         Print(player, Lang("VehicleNotYetPurchased", player.UserIDString, settings.DisplayName, configData.chat.buyCommand));
  1386.         return false;
  1387.       }
  1388.       if (vehicle.Entity != null && !vehicle.Entity.IsDestroyed) {
  1389.         Print(player, Lang("AlreadyVehicleOut", player.UserIDString, settings.DisplayName, configData.chat.recallCommand));
  1390.         return false;
  1391.       }
  1392.       string reason;
  1393.       var position = Vector3.zero;
  1394.       var rotation = Quaternion.identity;
  1395.       if (CanSpawn(player, vehicle, bypassCooldown, command, out reason, ref position, ref rotation)) {
  1396.         SpawnVehicle(player, vehicle, position, rotation);
  1397.         return false;
  1398.       }
  1399.       Print(player, reason);
  1400.       return true;
  1401.     }
  1402.  
  1403.     private bool CanSpawn(BasePlayer player, Vehicle vehicle, bool bypassCooldown, string command, out string reason, ref Vector3 position, ref Quaternion rotation) {
  1404.       var settings = GetBaseVehicleSettings(vehicle.VehicleType);
  1405.       BaseEntity randomVehicle = null;
  1406.       if (configData.global.limitVehicles > 0) {
  1407.         var activeVehicles = storedData.ActiveVehicles(player.userID);
  1408.         var count = activeVehicles.Count();
  1409.         if (count >= configData.global.limitVehicles) {
  1410.           if (configData.global.killVehicleLimited) {
  1411.             randomVehicle = activeVehicles.ElementAt(Random.Range(0, count));
  1412.           } else {
  1413.             reason = Lang("VehiclesLimit", player.UserIDString, configData.global.limitVehicles);
  1414.             return false;
  1415.           }
  1416.         }
  1417.       }
  1418.       if (!CanPlayerAction(player, vehicle, settings, out reason, ref position, ref rotation)) {
  1419.         return false;
  1420.       }
  1421.       var obj = Interface.CallHook("CanLicensedVehicleSpawn", player, vehicle.VehicleType, position, rotation);
  1422.       if (obj != null) {
  1423.         var s = obj as string;
  1424.         reason = s ?? Lang("SpawnWasBlocked", player.UserIDString, settings.DisplayName);
  1425.         return false;
  1426.       }
  1427.  
  1428.       #if DEBUG
  1429.       if (player.IsAdmin) {
  1430.         reason = null;
  1431.         return true;
  1432.       }
  1433.       #endif
  1434.       if (!CheckCooldown(player, vehicle, settings, bypassCooldown, true, command, out reason)) {
  1435.         return false;
  1436.       }
  1437.  
  1438.       string resources;
  1439.       if (settings.SpawnPrices.Count > 0 && !TryPay(player, settings.SpawnPrices, out resources)) {
  1440.         reason = Lang("NoResourcesToSpawnVehicle", player.UserIDString, settings.DisplayName, resources);
  1441.         return false;
  1442.       }
  1443.  
  1444.       if (randomVehicle != null) {
  1445.         randomVehicle.Kill(BaseNetworkable.DestroyMode.Gib);
  1446.       }
  1447.       reason = null;
  1448.       return true;
  1449.     }
  1450.  
  1451.     private void SpawnVehicle(BasePlayer player, Vehicle vehicle, Vector3 position, Quaternion rotation) {
  1452.       var settings = GetBaseVehicleSettings(vehicle.VehicleType);
  1453.       var entity = settings.SpawnVehicle(player, vehicle, position, rotation);
  1454.       if (entity == null) {
  1455.         return;
  1456.       }
  1457.       Interface.CallHook("OnLicensedVehicleSpawned", entity, player, vehicle.VehicleType);
  1458.       Print(player, Lang("VehicleSpawned", player.UserIDString, settings.DisplayName));
  1459.     }
  1460.  
  1461.     private void CacheVehicleEntity(BaseEntity entity, Vehicle vehicle, BasePlayer player) {
  1462.       vehicle.PlayerId = player.userID;
  1463.       vehicle.VehicleType = vehicle.VehicleType;
  1464.       vehicle.Entity = entity;
  1465.       vehicle.EntityId = entity.net.ID.Value;
  1466.       vehicle.LastDismount = vehicle.LastRecall = TimeEx.currentTimestamp;
  1467.       vehiclesCache.Add(entity, vehicle);
  1468.     }
  1469.  
  1470.     #endregion Spawn Command
  1471.  
  1472.     #region Recall Command
  1473.  
  1474.       [ConsoleCommand("vl.recall")]
  1475.     private void CCmdRecallVehicle(ConsoleSystem.Arg arg) {
  1476.       var player = arg.Player();
  1477.       if (player == null) {
  1478.         Print(arg, $ "The server console cannot use the '{arg.cmd.FullName}' command");
  1479.       } else {
  1480.         CmdRecallVehicle(player, arg.cmd.FullName, arg.Args);
  1481.       }
  1482.     }
  1483.  
  1484.     private void CmdRecallVehicle(BasePlayer player, string command, string[] args) {
  1485.       if (!permission.UserHasPermission(player.UserIDString, PERMISSION_USE)) {
  1486.         Print(player, Lang("NotAllowed", player.UserIDString));
  1487.         return;
  1488.       }
  1489.       if (args == null || args.Length < 1) {
  1490.         var stringBuilder = new StringBuilder();
  1491.         stringBuilder.AppendLine(Lang("Help", player.UserIDString));
  1492.         foreach(var entry in allVehicleSettings) {
  1493.           if (CanViewVehicleInfo(player, entry.Key, entry.Value)) {
  1494.             var firstCmd = entry.Value.Commands[0];
  1495.             if (entry.Value.RecallPrices.Count > 0) {
  1496.               var prices = FormatPriceInfo(player, entry.Value.RecallPrices);
  1497.               stringBuilder.AppendLine(Lang("HelpRecallPrice", player.UserIDString, configData.chat.recallCommand, firstCmd, entry.Value.DisplayName, prices));
  1498.             } else {
  1499.               stringBuilder.AppendLine(Lang("HelpRecall", player.UserIDString, configData.chat.recallCommand, firstCmd, entry.Value.DisplayName));
  1500.             }
  1501.           }
  1502.         }
  1503.         Print(player, stringBuilder.ToString());
  1504.         return;
  1505.       }
  1506.       string vehicleType;
  1507.       if (IsValidOption(player, args[0], out vehicleType)) {
  1508.         var bypassCooldown = args.Length > 1 && IsValidBypassCooldownOption(args[1]);
  1509.         RecallVehicle(player, vehicleType, bypassCooldown, command + " " + args[0]);
  1510.       }
  1511.     }
  1512.  
  1513.     private bool RecallVehicle(BasePlayer player, string vehicleType, bool bypassCooldown, string command) {
  1514.       var settings = GetBaseVehicleSettings(vehicleType);
  1515.       Vehicle vehicle;
  1516.       if (!storedData.IsVehiclePurchased(player.userID, vehicleType, out vehicle)) {
  1517.         Print(player, Lang("VehicleNotYetPurchased", player.UserIDString, settings.DisplayName, configData.chat.buyCommand));
  1518.         return false;
  1519.       }
  1520.       if (vehicle.Entity != null && !vehicle.Entity.IsDestroyed) {
  1521.         string reason;
  1522.         var position = Vector3.zero;
  1523.         var rotation = Quaternion.identity;
  1524.         if (CanRecall(player, vehicle, bypassCooldown, command, out reason, ref position, ref rotation)) {
  1525.           RecallVehicle(player, vehicle, position, rotation);
  1526.           return true;
  1527.         }
  1528.         Print(player, reason);
  1529.         return false;
  1530.       }
  1531.       Print(player, Lang("VehicleNotOut", player.UserIDString, settings.DisplayName, configData.chat.spawnCommand));
  1532.       return false;
  1533.     }
  1534.  
  1535.     private bool CanRecall(BasePlayer player, Vehicle vehicle, bool bypassCooldown, string command, out string reason, ref Vector3 position, ref Quaternion rotation) {
  1536.       var settings = GetBaseVehicleSettings(vehicle.VehicleType);
  1537.       if (settings.RecallMaxDistance > 0 && Vector3.Distance(player.transform.position, vehicle.Entity.transform.position) > settings.RecallMaxDistance) {
  1538.         reason = Lang("RecallTooFar", player.UserIDString, settings.RecallMaxDistance, settings.DisplayName);
  1539.         return false;
  1540.       }
  1541.       if (configData.global.anyMountedRecall && VehicleAnyMounted(vehicle.Entity)) {
  1542.         reason = Lang("PlayerMountedOnVehicle", player.UserIDString, settings.DisplayName);
  1543.         return false;
  1544.       }
  1545.       if (!CanPlayerAction(player, vehicle, settings, out reason, ref position, ref rotation)) {
  1546.         return false;
  1547.       }
  1548.  
  1549.       var obj = Interface.CallHook("CanLicensedVehicleRecall", vehicle.Entity, player, vehicle.VehicleType, position, rotation);
  1550.       if (obj != null) {
  1551.         var s = obj as string;
  1552.         reason = s ?? Lang("RecallWasBlocked", player.UserIDString, settings.DisplayName);
  1553.         return false;
  1554.       }
  1555.       #if DEBUG
  1556.       if (player.IsAdmin) {
  1557.         reason = null;
  1558.         return true;
  1559.       }
  1560.       #endif
  1561.       if (!CheckCooldown(player, vehicle, settings, bypassCooldown, false, command, out reason)) {
  1562.         return false;
  1563.       }
  1564.       string resources;
  1565.       if (settings.RecallPrices.Count > 0 && !TryPay(player, settings.RecallPrices, out resources)) {
  1566.         reason = Lang("NoResourcesToRecallVehicle", player.UserIDString, settings.DisplayName, resources);
  1567.         return false;
  1568.       }
  1569.       reason = null;
  1570.       return true;
  1571.     }
  1572.  
  1573.     private void RecallVehicle(BasePlayer player, Vehicle vehicle, Vector3 position, Quaternion rotation) {
  1574.       var settings = GetBaseVehicleSettings(vehicle.VehicleType);
  1575.  
  1576.       settings.PreRecallVehicle(player, vehicle, position, rotation);
  1577.       vehicle.Entity.transform.SetPositionAndRotation(position, rotation);
  1578.       vehicle.Entity.transform.hasChanged = true;
  1579.       settings.PostRecallVehicle(player, vehicle, position, rotation);
  1580.  
  1581.       vehicle.OnRecall();
  1582.  
  1583.       if (vehicle.Entity == null || vehicle.Entity.IsDestroyed) {
  1584.         Print(player, Lang("NotSpawnedOrRecalled", player.UserIDString, settings.DisplayName));
  1585.         return;
  1586.       }
  1587.  
  1588.       Interface.CallHook("OnLicensedVehicleRecalled", vehicle.Entity, player, vehicle.VehicleType);
  1589.       Print(player, Lang("VehicleRecalled", player.UserIDString, settings.DisplayName));
  1590.     }
  1591.  
  1592.     #endregion Recall Command
  1593.  
  1594.     #region Kill Command
  1595.  
  1596.       [ConsoleCommand("vl.kill")]
  1597.     private void CCmdKillVehicle(ConsoleSystem.Arg arg) {
  1598.       var player = arg.Player();
  1599.       if (player == null) {
  1600.         Print(arg, $ "The server console cannot use the '{arg.cmd.FullName}' command");
  1601.       } else {
  1602.         CmdKillVehicle(player, arg.cmd.FullName, arg.Args);
  1603.       }
  1604.     }
  1605.  
  1606.     private void CmdKillVehicle(BasePlayer player, string command, string[] args) {
  1607.       if (!permission.UserHasPermission(player.UserIDString, PERMISSION_USE)) {
  1608.         Print(player, Lang("NotAllowed", player.UserIDString));
  1609.         return;
  1610.       }
  1611.       if (args == null || args.Length < 1) {
  1612.         var stringBuilder = new StringBuilder();
  1613.         stringBuilder.AppendLine(Lang("Help", player.UserIDString));
  1614.         foreach(var entry in allVehicleSettings) {
  1615.           if (CanViewVehicleInfo(player, entry.Key, entry.Value)) {
  1616.             var firstCmd = entry.Value.Commands[0];
  1617.             if (!string.IsNullOrEmpty(configData.chat.customKillCommandPrefix)) {
  1618.               stringBuilder.AppendLine(Lang("HelpKillCustom", player.UserIDString, configData.chat.killCommand, firstCmd, configData.chat.customKillCommandPrefix + firstCmd, entry.Value.DisplayName));
  1619.             } else {
  1620.               stringBuilder.AppendLine(Lang("HelpKill", player.UserIDString, configData.chat.killCommand, firstCmd, entry.Value.DisplayName));
  1621.             }
  1622.           }
  1623.         }
  1624.         Print(player, stringBuilder.ToString());
  1625.         return;
  1626.       }
  1627.  
  1628.       HandleKillCmd(player, args[0]);
  1629.     }
  1630.  
  1631.     private void HandleKillCmd(BasePlayer player, string option) {
  1632.       string vehicleType;
  1633.       if (IsValidOption(player, option, out vehicleType)) {
  1634.         KillVehicle(player, vehicleType);
  1635.       }
  1636.     }
  1637.  
  1638.     private bool KillVehicle(BasePlayer player, string vehicleType) {
  1639.       var settings = GetBaseVehicleSettings(vehicleType);
  1640.       Vehicle vehicle;
  1641.       if (!storedData.IsVehiclePurchased(player.userID, vehicleType, out vehicle)) {
  1642.         Print(player, Lang("VehicleNotYetPurchased", player.UserIDString, settings.DisplayName, configData.chat.buyCommand));
  1643.         return false;
  1644.       }
  1645.       if (vehicle.Entity != null && !vehicle.Entity.IsDestroyed) {
  1646.         if (!CanKill(player, vehicle, settings)) {
  1647.           return false;
  1648.         }
  1649.         vehicle.Entity.Kill(BaseNetworkable.DestroyMode.Gib);
  1650.         Print(player, Lang("VehicleKilled", player.UserIDString, settings.DisplayName));
  1651.         return true;
  1652.       }
  1653.       Print(player, Lang("VehicleNotOut", player.UserIDString, settings.DisplayName, configData.chat.spawnCommand));
  1654.       return false;
  1655.     }
  1656.  
  1657.     private bool CanKill(BasePlayer player, Vehicle vehicle, BaseVehicleSettings settings) {
  1658.       if (configData.global.anyMountedKill && VehicleAnyMounted(vehicle.Entity)) {
  1659.         Print(player, Lang("PlayerMountedOnVehicle", player.UserIDString, settings.DisplayName));
  1660.         return false;
  1661.       }
  1662.       if (settings.KillMaxDistance > 0 && Vector3.Distance(player.transform.position, vehicle.Entity.transform.position) > settings.KillMaxDistance) {
  1663.         Print(player, Lang("KillTooFar", player.UserIDString, settings.KillMaxDistance, settings.DisplayName));
  1664.         return false;
  1665.       }
  1666.  
  1667.       return true;
  1668.     }
  1669.  
  1670.     #endregion Kill Command
  1671.  
  1672.     #region Command Helpers
  1673.  
  1674.     private bool IsValidBypassCooldownOption(string option) {
  1675.       return !string.IsNullOrEmpty(configData.chat.bypassCooldownCommand) &&
  1676.         string.Equals(option, configData.chat.bypassCooldownCommand, StringComparison.OrdinalIgnoreCase);
  1677.     }
  1678.  
  1679.     private bool IsValidOption(BasePlayer player, string option, out string vehicleType) {
  1680.       if (!commandToVehicleType.TryGetValue(option, out vehicleType)) {
  1681.         Print(player, Lang("OptionNotFound", player.UserIDString, option));
  1682.         return false;
  1683.       }
  1684.       if (!HasVehiclePermission(player, vehicleType)) {
  1685.         Print(player, Lang("NotAllowed", player.UserIDString));
  1686.         vehicleType = null;
  1687.         return false;
  1688.       }
  1689.       if (IsPlayerBlocked(player)) {
  1690.         vehicleType = null;
  1691.         return false;
  1692.       }
  1693.       return true;
  1694.     }
  1695.  
  1696.     private bool IsValidVehicleType(string option, out string vehicleType) {
  1697.       foreach(var entry in allVehicleSettings) {
  1698.         if (string.Equals(entry.Key, option, StringComparison.OrdinalIgnoreCase)) {
  1699.           vehicleType = entry.Key;
  1700.           return true;
  1701.         }
  1702.       }
  1703.  
  1704.       vehicleType = null;
  1705.       return false;
  1706.     }
  1707.  
  1708.     private string FormatPriceInfo(BasePlayer player, Dictionary < string, PriceInfo > prices) {
  1709.       var language = RustTranslationAPI != null ? lang.GetLanguage(player.UserIDString) : null;
  1710.       return string.Join(", ", from p in prices select Lang("PriceFormat", player.UserIDString, GetItemDisplayName(language, p.Key, p.Value.displayName), p.Value.amount));
  1711.     }
  1712.  
  1713.     private bool CanPlayerAction(BasePlayer player, Vehicle vehicle, BaseVehicleSettings settings, out string reason, ref Vector3 position, ref Quaternion rotation) {
  1714.       if (configData.global.preventBuildingBlocked && player.IsBuildingBlocked()) {
  1715.         reason = Lang("BuildingBlocked", player.UserIDString, settings.DisplayName);
  1716.         return false;
  1717.       }
  1718.       if (configData.global.preventSafeZone && player.InSafeZone()) {
  1719.         reason = Lang("PlayerInSafeZone", player.UserIDString, settings.DisplayName);
  1720.         return false;
  1721.       }
  1722.       if (configData.global.preventMountedOrParented && HasMountedOrParented(player, settings)) {
  1723.         reason = Lang("MountedOrParented", player.UserIDString, settings.DisplayName);
  1724.         return false;
  1725.       }
  1726.       if (!settings.TryGetVehicleParams(player, vehicle, out reason, ref position, ref rotation)) {
  1727.         return false;
  1728.       }
  1729.       reason = null;
  1730.       return true;
  1731.     }
  1732.  
  1733.     private bool HasMountedOrParented(BasePlayer player, BaseVehicleSettings settings) {
  1734.       if (player.GetMountedVehicle() != null) {
  1735.         return true;
  1736.       }
  1737.       var parentEntity = player.GetParentEntity();
  1738.       if (parentEntity != null) {
  1739.         if (configData.global.spawnLookingAt) {
  1740.           if (LandOnCargoShip != null && parentEntity is CargoShip && settings.IsFightVehicle) {
  1741.             return false;
  1742.           }
  1743.         }
  1744.         return true;
  1745.       }
  1746.       return false;
  1747.     }
  1748.  
  1749.     private bool CheckCooldown(BasePlayer player, Vehicle vehicle, BaseVehicleSettings settings, bool bypassCooldown, bool isSpawnCooldown, string command, out string reason) {
  1750.       var cooldown = settings.GetCooldown(player, isSpawnCooldown);
  1751.       if (cooldown > 0) {
  1752.         var timeLeft = Math.Ceiling(cooldown - (TimeEx.currentTimestamp - (isSpawnCooldown ? vehicle.LastDeath : vehicle.LastRecall)));
  1753.         if (timeLeft > 0) {
  1754.           var bypassPrices = isSpawnCooldown ? settings.BypassSpawnCooldownPrices : settings.BypassRecallCooldownPrices;
  1755.           if (bypassCooldown && bypassPrices.Count > 0) {
  1756.             string resources;
  1757.             if (!TryPay(player, bypassPrices, out resources)) {
  1758.               reason = Lang(isSpawnCooldown ? "NoResourcesToSpawnVehicleBypass" : "NoResourcesToRecallVehicleBypass", player.UserIDString, settings.DisplayName, resources);
  1759.               return false;
  1760.             }
  1761.  
  1762.             if (isSpawnCooldown) {
  1763.               vehicle.LastDeath = 0;
  1764.             } else {
  1765.               vehicle.LastRecall = 0;
  1766.             }
  1767.           } else {
  1768.             if (string.IsNullOrEmpty(configData.chat.bypassCooldownCommand) || bypassPrices.Count <= 0) {
  1769.               reason = Lang(isSpawnCooldown ? "VehicleOnSpawnCooldown" : "VehicleOnRecallCooldown", player.UserIDString, timeLeft, settings.DisplayName);
  1770.             } else {
  1771.               reason = Lang(isSpawnCooldown ? "VehicleOnSpawnCooldownPay" : "VehicleOnRecallCooldownPay", player.UserIDString, timeLeft, settings.DisplayName,
  1772.                 command + " " + configData.chat.bypassCooldownCommand,
  1773.                 FormatPriceInfo(player, isSpawnCooldown ? settings.BypassSpawnCooldownPrices : settings.BypassRecallCooldownPrices));
  1774.             }
  1775.             return false;
  1776.           }
  1777.         }
  1778.       }
  1779.       reason = null;
  1780.       return true;
  1781.     }
  1782.  
  1783.     #endregion Command Helpers
  1784.  
  1785.     #endregion Commands
  1786.  
  1787.     #region RustTranslationAPI
  1788.  
  1789.     private string GetItemTranslationByShortName(string language, string itemShortName) {
  1790.       return (string) RustTranslationAPI.Call("GetItemTranslationByShortName", language, itemShortName);
  1791.     }
  1792.  
  1793.     private string GetItemDisplayName(string language, string itemShortName, string displayName) {
  1794.       if (RustTranslationAPI != null) {
  1795.         var displayName1 = GetItemTranslationByShortName(language, itemShortName);
  1796.         if (!string.IsNullOrEmpty(displayName1)) {
  1797.           return displayName1;
  1798.         }
  1799.       }
  1800.       return displayName;
  1801.     }
  1802.  
  1803.     #endregion RustTranslationAPI
  1804.  
  1805.     #region ConfigurationFile
  1806.  
  1807.     public ConfigData configData {
  1808.       get;
  1809.       private set;
  1810.     }
  1811.  
  1812.     public class ConfigData {
  1813.       [JsonProperty(PropertyName = "Settings")]
  1814.       public GlobalSettings global = new GlobalSettings();
  1815.  
  1816.       [JsonProperty(PropertyName = "Chat Settings")]
  1817.       public ChatSettings chat = new ChatSettings();
  1818.  
  1819.       [JsonProperty(PropertyName = "Normal Vehicle Settings")]
  1820.       public NormalVehicleSettings normalVehicles = new NormalVehicleSettings();
  1821.  
  1822.       [JsonProperty(PropertyName = "Modular Vehicle Settings", ObjectCreationHandling = ObjectCreationHandling.Replace)]
  1823.       public Dictionary < string, ModularVehicleSettings > modularVehicles = new Dictionary < string, ModularVehicleSettings > {
  1824.         ["SmallCar"] = new ModularVehicleSettings {
  1825.           Purchasable = true,
  1826.             DisplayName = "Small Modular Car",
  1827.             Distance = 5,
  1828.             MinDistanceForPlayers = 3,
  1829.             UsePermission = true,
  1830.             Permission = "vehiclelicence.smallmodularcar",
  1831.             Commands = new List < string > {
  1832.               "small",
  1833.               "smallcar"
  1834.             },
  1835.             PurchasePrices = new Dictionary < string, PriceInfo > {
  1836.               ["scrap"] = new PriceInfo {
  1837.                 amount = 1600, displayName = "Scrap"
  1838.               }
  1839.             },
  1840.             SpawnPrices = new Dictionary < string, PriceInfo > {
  1841.               ["metal.refined"] = new PriceInfo {
  1842.                 amount = 10, displayName = "High Quality Metal"
  1843.               }
  1844.             },
  1845.             RecallPrices = new Dictionary < string, PriceInfo > {
  1846.               ["scrap"] = new PriceInfo {
  1847.                 amount = 5, displayName = "Scrap"
  1848.               }
  1849.             },
  1850.             SpawnCooldown = 7200,
  1851.             RecallCooldown = 30,
  1852.             CooldownPermissions = new Dictionary < string, CooldownPermission > {
  1853.               ["vehiclelicence.vip"] = new CooldownPermission {
  1854.                 spawnCooldown = 3600,
  1855.                   recallCooldown = 10
  1856.               }
  1857.             },
  1858.             ChassisType = ChassisType.Small,
  1859.             ModuleItems = new List < ModuleItem > {
  1860.               new ModuleItem {
  1861.                 shortName = "vehicle.1mod.cockpit.with.engine", healthPercentage = 50 f
  1862.               },
  1863.               new ModuleItem {
  1864.                 shortName = "vehicle.1mod.storage", healthPercentage = 50 f
  1865.               }
  1866.             },
  1867.             EngineItems = new List < EngineItem > {
  1868.               new EngineItem {
  1869.                 shortName = "carburetor1", conditionPercentage = 20 f
  1870.               },
  1871.               new EngineItem {
  1872.                 shortName = "crankshaft1", conditionPercentage = 20 f
  1873.               },
  1874.               new EngineItem {
  1875.                 shortName = "piston1", conditionPercentage = 20 f
  1876.               },
  1877.               new EngineItem {
  1878.                 shortName = "sparkplug1", conditionPercentage = 20 f
  1879.               },
  1880.               new EngineItem {
  1881.                 shortName = "valve1", conditionPercentage = 20 f
  1882.               }
  1883.             }
  1884.         },
  1885.         ["MediumCar"] = new ModularVehicleSettings {
  1886.           Purchasable = true,
  1887.             DisplayName = "Medium Modular Car",
  1888.             Distance = 5,
  1889.             MinDistanceForPlayers = 3,
  1890.             UsePermission = true,
  1891.             Permission = "vehiclelicence.mediumodularcar",
  1892.             Commands = new List < string > {
  1893.               "medium",
  1894.               "mediumcar"
  1895.             },
  1896.             PurchasePrices = new Dictionary < string, PriceInfo > {
  1897.               ["scrap"] = new PriceInfo {
  1898.                 amount = 2400, displayName = "Scrap"
  1899.               }
  1900.             },
  1901.             SpawnPrices = new Dictionary < string, PriceInfo > {
  1902.               ["metal.refined"] = new PriceInfo {
  1903.                 amount = 50, displayName = "High Quality Metal"
  1904.               }
  1905.             },
  1906.             RecallPrices = new Dictionary < string, PriceInfo > {
  1907.               ["scrap"] = new PriceInfo {
  1908.                 amount = 8, displayName = "Scrap"
  1909.               }
  1910.             },
  1911.             SpawnCooldown = 9000,
  1912.             RecallCooldown = 30,
  1913.             CooldownPermissions = new Dictionary < string, CooldownPermission > {
  1914.               ["vehiclelicence.vip"] = new CooldownPermission {
  1915.                 spawnCooldown = 4500,
  1916.                   recallCooldown = 10
  1917.               }
  1918.             },
  1919.             ChassisType = ChassisType.Medium,
  1920.             ModuleItems = new List < ModuleItem > {
  1921.               new ModuleItem {
  1922.                 shortName = "vehicle.1mod.cockpit.with.engine", healthPercentage = 50 f
  1923.               },
  1924.               new ModuleItem {
  1925.                 shortName = "vehicle.1mod.rear.seats", healthPercentage = 50 f
  1926.               },
  1927.               new ModuleItem {
  1928.                 shortName = "vehicle.1mod.flatbed", healthPercentage = 50 f
  1929.               }
  1930.             },
  1931.             EngineItems = new List < EngineItem > {
  1932.               new EngineItem {
  1933.                 shortName = "carburetor2", conditionPercentage = 20 f
  1934.               },
  1935.               new EngineItem {
  1936.                 shortName = "crankshaft2", conditionPercentage = 20 f
  1937.               },
  1938.               new EngineItem {
  1939.                 shortName = "piston2", conditionPercentage = 20 f
  1940.               },
  1941.               new EngineItem {
  1942.                 shortName = "sparkplug2", conditionPercentage = 20 f
  1943.               },
  1944.               new EngineItem {
  1945.                 shortName = "valve2", conditionPercentage = 20 f
  1946.               }
  1947.             }
  1948.         },
  1949.         ["LargeCar"] = new ModularVehicleSettings {
  1950.           Purchasable = true,
  1951.             DisplayName = "Large Modular Car",
  1952.             Distance = 6,
  1953.             MinDistanceForPlayers = 3,
  1954.             UsePermission = true,
  1955.             Permission = "vehiclelicence.largemodularcar",
  1956.             Commands = new List < string > {
  1957.               "large",
  1958.               "largecar"
  1959.             },
  1960.             PurchasePrices = new Dictionary < string, PriceInfo > {
  1961.               ["scrap"] = new PriceInfo {
  1962.                 amount = 3000, displayName = "Scrap"
  1963.               }
  1964.             },
  1965.             SpawnPrices = new Dictionary < string, PriceInfo > {
  1966.               ["metal.refined"] = new PriceInfo {
  1967.                 amount = 100, displayName = "High Quality Metal"
  1968.               }
  1969.             },
  1970.             RecallPrices = new Dictionary < string, PriceInfo > {
  1971.               ["scrap"] = new PriceInfo {
  1972.                 amount = 10, displayName = "Scrap"
  1973.               }
  1974.             },
  1975.             SpawnCooldown = 10800,
  1976.             RecallCooldown = 30,
  1977.             CooldownPermissions = new Dictionary < string, CooldownPermission > {
  1978.               ["vehiclelicence.vip"] = new CooldownPermission {
  1979.                 spawnCooldown = 5400,
  1980.                   recallCooldown = 10
  1981.               }
  1982.             },
  1983.             ChassisType = ChassisType.Large,
  1984.             ModuleItems = new List < ModuleItem > {
  1985.               new ModuleItem {
  1986.                 shortName = "vehicle.1mod.engine", healthPercentage = 50 f
  1987.               },
  1988.               new ModuleItem {
  1989.                 shortName = "vehicle.1mod.cockpit.armored", healthPercentage = 50 f
  1990.               },
  1991.               new ModuleItem {
  1992.                 shortName = "vehicle.1mod.passengers.armored", healthPercentage = 50 f
  1993.               },
  1994.               new ModuleItem {
  1995.                 shortName = "vehicle.1mod.storage", healthPercentage = 50 f
  1996.               }
  1997.             },
  1998.             EngineItems = new List < EngineItem > {
  1999.               new EngineItem {
  2000.                 shortName = "carburetor3", conditionPercentage = 10 f
  2001.               },
  2002.               new EngineItem {
  2003.                 shortName = "crankshaft3", conditionPercentage = 10 f
  2004.               },
  2005.               new EngineItem {
  2006.                 shortName = "piston3", conditionPercentage = 10 f
  2007.               },
  2008.               new EngineItem {
  2009.                 shortName = "piston3", conditionPercentage = 10 f
  2010.               },
  2011.               new EngineItem {
  2012.                 shortName = "sparkplug3", conditionPercentage = 10 f
  2013.               },
  2014.               new EngineItem {
  2015.                 shortName = "sparkplug3", conditionPercentage = 10 f
  2016.               },
  2017.               new EngineItem {
  2018.                 shortName = "valve3", conditionPercentage = 10 f
  2019.               },
  2020.               new EngineItem {
  2021.                 shortName = "valve3", conditionPercentage = 10 f
  2022.               }
  2023.             }
  2024.         }
  2025.       };
  2026.  
  2027.       [JsonProperty(PropertyName = "Train Vehicle Settings", ObjectCreationHandling = ObjectCreationHandling.Replace)]
  2028.       public Dictionary < string, TrainVehicleSettings > trainVehicles = new Dictionary < string, TrainVehicleSettings > {
  2029.         ["WorkCartAboveGround"] = new TrainVehicleSettings {
  2030.           Purchasable = true,
  2031.             DisplayName = "Work Cart Above Ground",
  2032.             Distance = 12,
  2033.             MinDistanceForPlayers = 6,
  2034.             UsePermission = true,
  2035.             Permission = "vehiclelicence.workcartaboveground",
  2036.             Commands = new List < string > {
  2037.               "cartground",
  2038.               "workcartground"
  2039.             },
  2040.             PurchasePrices = new Dictionary < string, PriceInfo > {
  2041.               ["scrap"] = new PriceInfo {
  2042.                 amount = 2000, displayName = "Scrap"
  2043.               }
  2044.             },
  2045.             SpawnCooldown = 1800,
  2046.             RecallCooldown = 30,
  2047.             CooldownPermissions = new Dictionary < string, CooldownPermission > {
  2048.               ["vehiclelicence.vip"] = new CooldownPermission {
  2049.                 spawnCooldown = 900,
  2050.                   recallCooldown = 10
  2051.               }
  2052.             },
  2053.             TrainComponents = new List < TrainComponent > {
  2054.               new TrainComponent {
  2055.                 type = TrainComponentType.Engine
  2056.               }
  2057.             }
  2058.         },
  2059.         ["WorkCartCovered"] = new TrainVehicleSettings {
  2060.           Purchasable = true,
  2061.             DisplayName = "Covered Work Cart",
  2062.             Distance = 12,
  2063.             MinDistanceForPlayers = 6,
  2064.             UsePermission = true,
  2065.             Permission = "vehiclelicence.coveredworkcart",
  2066.             Commands = new List < string > {
  2067.               "cartcovered",
  2068.               "coveredworkcart"
  2069.             },
  2070.             PurchasePrices = new Dictionary < string, PriceInfo > {
  2071.               ["scrap"] = new PriceInfo {
  2072.                 amount = 2000, displayName = "Scrap"
  2073.               }
  2074.             },
  2075.             SpawnCooldown = 1800,
  2076.             RecallCooldown = 30,
  2077.             CooldownPermissions = new Dictionary < string, CooldownPermission > {
  2078.               ["vehiclelicence.vip"] = new CooldownPermission {
  2079.                 spawnCooldown = 900,
  2080.                   recallCooldown = 10
  2081.               }
  2082.             },
  2083.             TrainComponents = new List < TrainComponent > {
  2084.               new TrainComponent {
  2085.                 type = TrainComponentType.CoveredEngine
  2086.               }
  2087.             }
  2088.         },
  2089.         ["CompleteTrain"] = new TrainVehicleSettings {
  2090.           Purchasable = true,
  2091.             DisplayName = "Complete Train",
  2092.             Distance = 12,
  2093.             MinDistanceForPlayers = 6,
  2094.             UsePermission = true,
  2095.             Permission = "vehiclelicence.completetrain",
  2096.             Commands = new List < string > {
  2097.               "ctrain",
  2098.               "completetrain"
  2099.             },
  2100.             PurchasePrices = new Dictionary < string, PriceInfo > {
  2101.               ["scrap"] = new PriceInfo {
  2102.                 amount = 6000, displayName = "Scrap"
  2103.               }
  2104.             },
  2105.             SpawnCooldown = 3600,
  2106.             RecallCooldown = 60,
  2107.             CooldownPermissions = new Dictionary < string, CooldownPermission > {
  2108.               ["vehiclelicence.vip"] = new CooldownPermission {
  2109.                 spawnCooldown = 900,
  2110.                   recallCooldown = 10
  2111.               }
  2112.             },
  2113.             TrainComponents = new List < TrainComponent > {
  2114.               new TrainComponent {
  2115.                 type = TrainComponentType.Engine
  2116.               },
  2117.               new TrainComponent {
  2118.                 type = TrainComponentType.WagonA
  2119.               },
  2120.               new TrainComponent {
  2121.                 type = TrainComponentType.WagonB
  2122.               },
  2123.               new TrainComponent {
  2124.                 type = TrainComponentType.WagonC
  2125.               },
  2126.               new TrainComponent {
  2127.                 type = TrainComponentType.Unloadable
  2128.               },
  2129.               new TrainComponent {
  2130.                 type = TrainComponentType.UnloadableLoot
  2131.               }
  2132.             }
  2133.         }
  2134.       };
  2135.  
  2136.       [JsonProperty(PropertyName = "Version")]
  2137.       public VersionNumber version;
  2138.     }
  2139.  
  2140.     public class ChatSettings {
  2141.       [JsonProperty(PropertyName = "Use Universal Chat Command")]
  2142.       public bool useUniversalCommand = true;
  2143.  
  2144.       [JsonProperty(PropertyName = "Help Chat Command")]
  2145.       public string helpCommand = "license";
  2146.  
  2147.       [JsonProperty(PropertyName = "Buy Chat Command")]
  2148.       public string buyCommand = "buy";
  2149.  
  2150.       [JsonProperty(PropertyName = "Spawn Chat Command")]
  2151.       public string spawnCommand = "spawn";
  2152.  
  2153.       [JsonProperty(PropertyName = "Recall Chat Command")]
  2154.       public string recallCommand = "recall";
  2155.  
  2156.       [JsonProperty(PropertyName = "Kill Chat Command")]
  2157.       public string killCommand = "kill";
  2158.  
  2159.       [JsonProperty(PropertyName = "Custom Kill Chat Command Prefix")]
  2160.       public string customKillCommandPrefix = "no";
  2161.  
  2162.       [JsonProperty(PropertyName = "Bypass Cooldown Command")]
  2163.       public string bypassCooldownCommand = "pay";
  2164.  
  2165.       [JsonProperty(PropertyName = "Chat Prefix")]
  2166.       public string prefix = "<color=#00FFFF>[VehicleLicense]</color>: ";
  2167.  
  2168.       [JsonProperty(PropertyName = "Chat SteamID Icon")]
  2169.       public ulong steamIDIcon = 76561198924840872;
  2170.     }
  2171.  
  2172.     public class GlobalSettings {
  2173.       [JsonProperty(PropertyName = "Store Vehicle On Plugin Unloaded / Server Restart")]
  2174.       public bool storeVehicle = true;
  2175.  
  2176.       [JsonProperty(PropertyName = "Clear Vehicle Data On Map Wipe")]
  2177.       public bool clearVehicleOnWipe;
  2178.  
  2179.       [JsonProperty(PropertyName = "Interval to check vehicle for wipe (Seconds)")]
  2180.       public float checkVehiclesInterval = 300;
  2181.  
  2182.       [JsonProperty(PropertyName = "Spawn vehicle in the direction you are looking at")]
  2183.       public bool spawnLookingAt = true;
  2184.  
  2185.       [JsonProperty(PropertyName = "Automatically claim vehicles purchased from vehicle vendors")]
  2186.       public bool autoClaimFromVendor;
  2187.  
  2188.       [JsonProperty(PropertyName = "Vehicle vendor purchases will unlock the license for the player")]
  2189.       public bool autoUnlockFromVendor;
  2190.  
  2191.       [JsonProperty(PropertyName = "Limit the number of vehicles at a time")]
  2192.       public int limitVehicles;
  2193.  
  2194.       [JsonProperty(PropertyName = "Kill a random vehicle when the number of vehicles is limited")]
  2195.       public bool killVehicleLimited;
  2196.  
  2197.       [JsonProperty(PropertyName = "Prevent vehicles from damaging players")]
  2198.       public bool preventDamagePlayer = true;
  2199.  
  2200.       [JsonProperty(PropertyName = "Prevent vehicles from shattering")]
  2201.       public bool preventShattering = true;
  2202.  
  2203.       [JsonProperty(PropertyName = "Prevent vehicles from spawning or recalling in safe zone")]
  2204.       public bool preventSafeZone = true;
  2205.  
  2206.       [JsonProperty(PropertyName = "Prevent vehicles from spawning or recalling when the player are building blocked")]
  2207.       public bool preventBuildingBlocked = true;
  2208.  
  2209.       [JsonProperty(PropertyName = "Prevent vehicles from spawning or recalling when the player is mounted or parented")]
  2210.       public bool preventMountedOrParented = true;
  2211.  
  2212.       [JsonProperty(PropertyName = "Check if any player mounted when recalling a vehicle")]
  2213.       public bool anyMountedRecall = true;
  2214.  
  2215.       [JsonProperty(PropertyName = "Check if any player mounted when killing a vehicle")]
  2216.       public bool anyMountedKill;
  2217.  
  2218.       [JsonProperty(PropertyName = "Dismount all players when a vehicle is recalled")]
  2219.       public bool dismountAllPlayersRecall = true;
  2220.  
  2221.       [JsonProperty(PropertyName = "Prevent other players from mounting vehicle")]
  2222.       public bool preventMounting = true;
  2223.  
  2224.       [JsonProperty(PropertyName = "Prevent mounting on driver's seat only")]
  2225.       public bool preventDriverSeat = true;
  2226.  
  2227.       [JsonProperty(PropertyName = "Prevent other players from looting fuel container and inventory")]
  2228.       public bool preventLooting = true;
  2229.  
  2230.       [JsonProperty(PropertyName = "Use Teams")]
  2231.       public bool useTeams;
  2232.  
  2233.       [JsonProperty(PropertyName = "Use Clans")]
  2234.       public bool useClans = true;
  2235.  
  2236.       [JsonProperty(PropertyName = "Use Friends")]
  2237.       public bool useFriends = true;
  2238.  
  2239.       [JsonProperty(PropertyName = "Vehicle No Decay")]
  2240.       public bool noDecay;
  2241.  
  2242.       [JsonProperty(PropertyName = "Vehicle No Fire Ball")]
  2243.       public bool noFireBall = true;
  2244.  
  2245.       [JsonProperty(PropertyName = "Vehicle No Server Gibs")]
  2246.       public bool noServerGibs = true;
  2247.  
  2248.       [JsonProperty(PropertyName = "Chinook No Map Marker")]
  2249.       public bool noMapMarker = true;
  2250.  
  2251.       [JsonProperty(PropertyName = "Use Raid Blocker (Need NoEscape Plugin)")]
  2252.       public bool useRaidBlocker;
  2253.  
  2254.       [JsonProperty(PropertyName = "Use Combat Blocker (Need NoEscape Plugin)")]
  2255.       public bool useCombatBlocker;
  2256.     }
  2257.  
  2258.     public class NormalVehicleSettings {
  2259.       [JsonProperty(PropertyName = "Sedan Vehicle", ObjectCreationHandling = ObjectCreationHandling.Replace)]
  2260.       public SedanSettings sedan = new SedanSettings {
  2261.         Purchasable = true,
  2262.           DisplayName = "Sedan",
  2263.           Distance = 5,
  2264.           MinDistanceForPlayers = 3,
  2265.           UsePermission = true,
  2266.           Permission = "vehiclelicence.sedan",
  2267.           Commands = new List < string > {
  2268.             "car",
  2269.             "sedan"
  2270.           },
  2271.           PurchasePrices = new Dictionary < string, PriceInfo > {
  2272.             ["scrap"] = new PriceInfo {
  2273.               amount = 300, displayName = "Scrap"
  2274.             }
  2275.           },
  2276.           SpawnCooldown = 300,
  2277.           RecallCooldown = 30,
  2278.           CooldownPermissions = new Dictionary < string, CooldownPermission > {
  2279.             ["vehiclelicence.vip"] = new CooldownPermission {
  2280.               spawnCooldown = 150,
  2281.                 recallCooldown = 10
  2282.             }
  2283.           }
  2284.       };
  2285.  
  2286.       [JsonProperty(PropertyName = "Chinook Vehicle", ObjectCreationHandling = ObjectCreationHandling.Replace)]
  2287.       public ChinookSettings chinook = new ChinookSettings {
  2288.         Purchasable = true,
  2289.           DisplayName = "Chinook",
  2290.           Distance = 15,
  2291.           MinDistanceForPlayers = 6,
  2292.           UsePermission = true,
  2293.           Permission = "vehiclelicence.chinook",
  2294.           Commands = new List < string > {
  2295.             "ch47",
  2296.             "chinook"
  2297.           },
  2298.           PurchasePrices = new Dictionary < string, PriceInfo > {
  2299.             ["scrap"] = new PriceInfo {
  2300.               amount = 3000, displayName = "Scrap"
  2301.             }
  2302.           },
  2303.           SpawnCooldown = 3000,
  2304.           RecallCooldown = 30,
  2305.           CooldownPermissions = new Dictionary < string, CooldownPermission > {
  2306.             ["vehiclelicence.vip"] = new CooldownPermission {
  2307.               spawnCooldown = 1500,
  2308.                 recallCooldown = 10
  2309.             }
  2310.           }
  2311.       };
  2312.  
  2313.       [JsonProperty(PropertyName = "Rowboat Vehicle", ObjectCreationHandling = ObjectCreationHandling.Replace)]
  2314.       public RowboatSettings rowboat = new RowboatSettings {
  2315.         Purchasable = true,
  2316.           DisplayName = "Row Boat",
  2317.           Distance = 5,
  2318.           MinDistanceForPlayers = 2,
  2319.           UsePermission = true,
  2320.           Permission = "vehiclelicence.rowboat",
  2321.           Commands = new List < string > {
  2322.             "row",
  2323.             "rowboat"
  2324.           },
  2325.           PurchasePrices = new Dictionary < string, PriceInfo > {
  2326.             ["scrap"] = new PriceInfo {
  2327.               amount = 500, displayName = "Scrap"
  2328.             }
  2329.           },
  2330.           SpawnCooldown = 300,
  2331.           RecallCooldown = 30,
  2332.           CooldownPermissions = new Dictionary < string, CooldownPermission > {
  2333.             ["vehiclelicence.vip"] = new CooldownPermission {
  2334.               spawnCooldown = 150,
  2335.                 recallCooldown = 10
  2336.             }
  2337.           }
  2338.       };
  2339.  
  2340.       [JsonProperty(PropertyName = "Tugboat Vehicle", ObjectCreationHandling = ObjectCreationHandling.Replace)]
  2341.       public TugboatSettings tugboat = new TugboatSettings {
  2342.         Purchasable = true,
  2343.           DisplayName = "Tugboat...Boat",
  2344.           Distance = 10,
  2345.           MinDistanceForPlayers = 3,
  2346.           UsePermission = true,
  2347.           Permission = "vehiclelicence.tug",
  2348.           Commands = new List < string > {
  2349.             "tugboat"
  2350.           },
  2351.           PurchasePrices = new Dictionary < string, PriceInfo > {
  2352.             ["scrap"] = new PriceInfo {
  2353.               amount = 10000, displayName = "Scrap"
  2354.             }
  2355.           },
  2356.           SpawnCooldown = 450,
  2357.           RecallCooldown = 30,
  2358.           CooldownPermissions = new Dictionary < string, CooldownPermission > {
  2359.             ["vehiclelicence.vip"] = new CooldownPermission {
  2360.               spawnCooldown = 225,
  2361.                 recallCooldown = 10
  2362.             }
  2363.           }
  2364.       };
  2365.  
  2366.       [JsonProperty(PropertyName = "RHIB Vehicle", ObjectCreationHandling = ObjectCreationHandling.Replace)]
  2367.       public RhibSettings rhib = new RhibSettings {
  2368.         Purchasable = true,
  2369.           DisplayName = "Rigid Hulled Inflatable Boat",
  2370.           Distance = 10,
  2371.           MinDistanceForPlayers = 3,
  2372.           UsePermission = true,
  2373.           Permission = "vehiclelicence.rhib",
  2374.           Commands = new List < string > {
  2375.             "rhib"
  2376.           },
  2377.           PurchasePrices = new Dictionary < string, PriceInfo > {
  2378.             ["scrap"] = new PriceInfo {
  2379.               amount = 1000, displayName = "Scrap"
  2380.             }
  2381.           },
  2382.           SpawnCooldown = 450,
  2383.           RecallCooldown = 30,
  2384.           CooldownPermissions = new Dictionary < string, CooldownPermission > {
  2385.             ["vehiclelicence.vip"] = new CooldownPermission {
  2386.               spawnCooldown = 225,
  2387.                 recallCooldown = 10
  2388.             }
  2389.           }
  2390.       };
  2391.  
  2392.       [JsonProperty(PropertyName = "Hot Air Balloon Vehicle", ObjectCreationHandling = ObjectCreationHandling.Replace)]
  2393.       public HotAirBalloonSettings hotAirBalloon = new HotAirBalloonSettings {
  2394.         Purchasable = true,
  2395.           DisplayName = "Hot Air Balloon",
  2396.           Distance = 20,
  2397.           MinDistanceForPlayers = 5,
  2398.           UsePermission = true,
  2399.           Permission = "vehiclelicence.hotairballoon",
  2400.           Commands = new List < string > {
  2401.             "hab",
  2402.             "hotairballoon"
  2403.           },
  2404.           PurchasePrices = new Dictionary < string, PriceInfo > {
  2405.             ["scrap"] = new PriceInfo {
  2406.               amount = 500, displayName = "Scrap"
  2407.             }
  2408.           },
  2409.           SpawnCooldown = 900,
  2410.           RecallCooldown = 30,
  2411.           CooldownPermissions = new Dictionary < string, CooldownPermission > {
  2412.             ["vehiclelicence.vip"] = new CooldownPermission {
  2413.               spawnCooldown = 450,
  2414.                 recallCooldown = 10
  2415.             }
  2416.           }
  2417.       };
  2418.  
  2419.       [JsonProperty(PropertyName = "Ridable Horse Vehicle", ObjectCreationHandling = ObjectCreationHandling.Replace)]
  2420.       public RidableHorseSettings ridableHorse = new RidableHorseSettings {
  2421.         Purchasable = true,
  2422.           DisplayName = "Ridable Horse",
  2423.           Distance = 6,
  2424.           MinDistanceForPlayers = 1,
  2425.           UsePermission = true,
  2426.           Permission = "vehiclelicence.ridablehorse",
  2427.           Commands = new List < string > {
  2428.             "horse",
  2429.             "ridablehorse"
  2430.           },
  2431.           PurchasePrices = new Dictionary < string, PriceInfo > {
  2432.             ["scrap"] = new PriceInfo {
  2433.               amount = 700, displayName = "Scrap"
  2434.             }
  2435.           },
  2436.           SpawnCooldown = 3000,
  2437.           RecallCooldown = 30,
  2438.           CooldownPermissions = new Dictionary < string, CooldownPermission > {
  2439.             ["vehiclelicence.vip"] = new CooldownPermission {
  2440.               spawnCooldown = 1500,
  2441.                 recallCooldown = 10
  2442.             }
  2443.           }
  2444.       };
  2445.  
  2446.       [JsonProperty(PropertyName = "Mini Copter Vehicle", ObjectCreationHandling = ObjectCreationHandling.Replace)]
  2447.       public MiniCopterSettings miniCopter = new MiniCopterSettings {
  2448.         Purchasable = true,
  2449.           DisplayName = "Mini Copter",
  2450.           Distance = 8,
  2451.           MinDistanceForPlayers = 2,
  2452.           UsePermission = true,
  2453.           Permission = "vehiclelicence.minicopter",
  2454.           Commands = new List < string > {
  2455.             "mini",
  2456.             "minicopter"
  2457.           },
  2458.           PurchasePrices = new Dictionary < string, PriceInfo > {
  2459.             ["scrap"] = new PriceInfo {
  2460.               amount = 4000, displayName = "Scrap"
  2461.             }
  2462.           },
  2463.           SpawnCooldown = 1800,
  2464.           RecallCooldown = 30,
  2465.           CooldownPermissions = new Dictionary < string, CooldownPermission > {
  2466.             ["vehiclelicence.vip"] = new CooldownPermission {
  2467.               spawnCooldown = 900,
  2468.                 recallCooldown = 10
  2469.             }
  2470.           }
  2471.       };
  2472.  
  2473.       [JsonProperty(PropertyName = "Transport Helicopter Vehicle", ObjectCreationHandling = ObjectCreationHandling.Replace)]
  2474.       public TransportHelicopterSettings transportHelicopter = new TransportHelicopterSettings {
  2475.         Purchasable = true,
  2476.           DisplayName = "Transport Copter",
  2477.           Distance = 10,
  2478.           MinDistanceForPlayers = 4,
  2479.           UsePermission = true,
  2480.           Permission = "vehiclelicence.transportcopter",
  2481.           Commands = new List < string > {
  2482.             "tcop",
  2483.             "transportcopter"
  2484.           },
  2485.           PurchasePrices = new Dictionary < string, PriceInfo > {
  2486.             ["scrap"] = new PriceInfo {
  2487.               amount = 5000, displayName = "Scrap"
  2488.             }
  2489.           },
  2490.           SpawnCooldown = 2400,
  2491.           RecallCooldown = 30,
  2492.           CooldownPermissions = new Dictionary < string, CooldownPermission > {
  2493.             ["vehiclelicence.vip"] = new CooldownPermission {
  2494.               spawnCooldown = 1200,
  2495.                 recallCooldown = 10
  2496.             }
  2497.           }
  2498.       };
  2499.  
  2500.       [JsonProperty(PropertyName = "Work Cart Vehicle", ObjectCreationHandling = ObjectCreationHandling.Replace)]
  2501.       public WorkCartSettings workCart = new WorkCartSettings {
  2502.         Purchasable = true,
  2503.           DisplayName = "Work Cart",
  2504.           Distance = 12,
  2505.           MinDistanceForPlayers = 6,
  2506.           UsePermission = true,
  2507.           Permission = "vehiclelicence.workcart",
  2508.           Commands = new List < string > {
  2509.             "cart",
  2510.             "workcart"
  2511.           },
  2512.           PurchasePrices = new Dictionary < string, PriceInfo > {
  2513.             ["scrap"] = new PriceInfo {
  2514.               amount = 2000, displayName = "Scrap"
  2515.             }
  2516.           },
  2517.           SpawnCooldown = 1800,
  2518.           RecallCooldown = 30,
  2519.           CooldownPermissions = new Dictionary < string, CooldownPermission > {
  2520.             ["vehiclelicence.vip"] = new CooldownPermission {
  2521.               spawnCooldown = 900,
  2522.                 recallCooldown = 10
  2523.             }
  2524.           }
  2525.       };
  2526.  
  2527.       [JsonProperty(PropertyName = "Sedan Rail Vehicle", ObjectCreationHandling = ObjectCreationHandling.Replace)]
  2528.       public WorkCartSettings sedanRail = new WorkCartSettings {
  2529.         Purchasable = true,
  2530.           DisplayName = "Sedan Rail",
  2531.           Distance = 6,
  2532.           MinDistanceForPlayers = 3,
  2533.           UsePermission = true,
  2534.           Permission = "vehiclelicence.sedanrail",
  2535.           Commands = new List < string > {
  2536.             "carrail",
  2537.             "sedanrail"
  2538.           },
  2539.           PurchasePrices = new Dictionary < string, PriceInfo > {
  2540.             ["scrap"] = new PriceInfo {
  2541.               amount = 500, displayName = "Scrap"
  2542.             }
  2543.           },
  2544.           SpawnCooldown = 600,
  2545.           RecallCooldown = 30,
  2546.           CooldownPermissions = new Dictionary < string, CooldownPermission > {
  2547.             ["vehiclelicence.vip"] = new CooldownPermission {
  2548.               spawnCooldown = 300,
  2549.                 recallCooldown = 10
  2550.             }
  2551.           }
  2552.       };
  2553.  
  2554.       [JsonProperty(PropertyName = "Magnet Crane Vehicle", ObjectCreationHandling = ObjectCreationHandling.Replace)]
  2555.       public MagnetCraneSettings magnetCrane = new MagnetCraneSettings {
  2556.         Purchasable = true,
  2557.           DisplayName = "Magnet Crane",
  2558.           Distance = 16,
  2559.           MinDistanceForPlayers = 8,
  2560.           UsePermission = true,
  2561.           Permission = "vehiclelicence.magnetcrane",
  2562.           Commands = new List < string > {
  2563.             "crane",
  2564.             "magnetcrane"
  2565.           },
  2566.           PurchasePrices = new Dictionary < string, PriceInfo > {
  2567.             ["scrap"] = new PriceInfo {
  2568.               amount = 2000, displayName = "Scrap"
  2569.             }
  2570.           },
  2571.           SpawnCooldown = 600,
  2572.           RecallCooldown = 30,
  2573.           CooldownPermissions = new Dictionary < string, CooldownPermission > {
  2574.             ["vehiclelicence.vip"] = new CooldownPermission {
  2575.               spawnCooldown = 300,
  2576.                 recallCooldown = 10
  2577.             }
  2578.           }
  2579.       };
  2580.  
  2581.       [JsonProperty(PropertyName = "Submarine Solo Vehicle", ObjectCreationHandling = ObjectCreationHandling.Replace)]
  2582.       public SubmarineSoloSettings submarineSolo = new SubmarineSoloSettings {
  2583.         Purchasable = true,
  2584.           DisplayName = "Submarine Solo",
  2585.           Distance = 5,
  2586.           MinDistanceForPlayers = 2,
  2587.           UsePermission = true,
  2588.           Permission = "vehiclelicence.submarinesolo",
  2589.           Commands = new List < string > {
  2590.             "subsolo",
  2591.             "solo"
  2592.           },
  2593.           PurchasePrices = new Dictionary < string, PriceInfo > {
  2594.             ["scrap"] = new PriceInfo {
  2595.               amount = 600, displayName = "Scrap"
  2596.             }
  2597.           },
  2598.           SpawnCooldown = 300,
  2599.           RecallCooldown = 30,
  2600.           CooldownPermissions = new Dictionary < string, CooldownPermission > {
  2601.             ["vehiclelicence.vip"] = new CooldownPermission {
  2602.               spawnCooldown = 150,
  2603.                 recallCooldown = 10
  2604.             }
  2605.           }
  2606.       };
  2607.  
  2608.       [JsonProperty(PropertyName = "Submarine Duo Vehicle", ObjectCreationHandling = ObjectCreationHandling.Replace)]
  2609.       public SubmarineDuoSettings submarineDuo = new SubmarineDuoSettings {
  2610.         Purchasable = true,
  2611.           DisplayName = "Submarine Duo",
  2612.           Distance = 5,
  2613.           MinDistanceForPlayers = 2,
  2614.           UsePermission = true,
  2615.           Permission = "vehiclelicence.submarineduo",
  2616.           Commands = new List < string > {
  2617.             "subduo",
  2618.             "duo"
  2619.           },
  2620.           PurchasePrices = new Dictionary < string, PriceInfo > {
  2621.             ["scrap"] = new PriceInfo {
  2622.               amount = 1000, displayName = "Scrap"
  2623.             }
  2624.           },
  2625.           SpawnCooldown = 300,
  2626.           RecallCooldown = 30,
  2627.           CooldownPermissions = new Dictionary < string, CooldownPermission > {
  2628.             ["vehiclelicence.vip"] = new CooldownPermission {
  2629.               spawnCooldown = 150,
  2630.                 recallCooldown = 10
  2631.             }
  2632.           }
  2633.       };
  2634.  
  2635.       [JsonProperty(PropertyName = "Snowmobile Vehicle", ObjectCreationHandling = ObjectCreationHandling.Replace)]
  2636.       public SnowmobileSettings snowmobile = new SnowmobileSettings {
  2637.         Purchasable = true,
  2638.           DisplayName = "Snowmobile",
  2639.           Distance = 5,
  2640.           MinDistanceForPlayers = 2,
  2641.           UsePermission = true,
  2642.           Permission = "vehiclelicence.snowmobile",
  2643.           Commands = new List < string > {
  2644.             "snow",
  2645.             "snowmobile"
  2646.           },
  2647.           PurchasePrices = new Dictionary < string, PriceInfo > {
  2648.             ["scrap"] = new PriceInfo {
  2649.               amount = 1000, displayName = "Scrap"
  2650.             }
  2651.           },
  2652.           SpawnCooldown = 300,
  2653.           RecallCooldown = 30,
  2654.           CooldownPermissions = new Dictionary < string, CooldownPermission > {
  2655.             ["vehiclelicence.vip"] = new CooldownPermission {
  2656.               spawnCooldown = 150,
  2657.                 recallCooldown = 10
  2658.             }
  2659.           }
  2660.       };
  2661.  
  2662.       [JsonProperty(PropertyName = "Tomaha Snowmobile Vehicle", ObjectCreationHandling = ObjectCreationHandling.Replace)]
  2663.       public SnowmobileSettings tomahaSnowmobile = new SnowmobileSettings {
  2664.         Purchasable = true,
  2665.           DisplayName = "Tomaha Snowmobile",
  2666.           Distance = 5,
  2667.           MinDistanceForPlayers = 2,
  2668.           UsePermission = true,
  2669.           Permission = "vehiclelicence.tomahasnowmobile",
  2670.           Commands = new List < string > {
  2671.             "tsnow",
  2672.             "tsnowmobile"
  2673.           },
  2674.           PurchasePrices = new Dictionary < string, PriceInfo > {
  2675.             ["scrap"] = new PriceInfo {
  2676.               amount = 1000, displayName = "Scrap"
  2677.             }
  2678.           },
  2679.           SpawnCooldown = 300,
  2680.           RecallCooldown = 30,
  2681.           CooldownPermissions = new Dictionary < string, CooldownPermission > {
  2682.             ["vehiclelicence.vip"] = new CooldownPermission {
  2683.               spawnCooldown = 150,
  2684.                 recallCooldown = 10
  2685.             }
  2686.           }
  2687.       };
  2688.     }
  2689.  
  2690.     #region BaseSettings
  2691.  
  2692.       [JsonObject(MemberSerialization.OptIn)]
  2693.     public abstract class BaseVehicleSettings {
  2694.       #region Properties
  2695.  
  2696.         [JsonProperty(PropertyName = "Purchasable")]
  2697.       public bool Purchasable {
  2698.         get;
  2699.         set;
  2700.       }
  2701.  
  2702.       [JsonProperty(PropertyName = "Display Name")]
  2703.       public string DisplayName {
  2704.         get;
  2705.         set;
  2706.       }
  2707.  
  2708.       [JsonProperty(PropertyName = "Use Permission")]
  2709.       public bool UsePermission {
  2710.         get;
  2711.         set;
  2712.       }
  2713.  
  2714.       [JsonProperty(PropertyName = "Permission")]
  2715.       public string Permission {
  2716.         get;
  2717.         set;
  2718.       }
  2719.  
  2720.       [JsonProperty(PropertyName = "Distance To Spawn")]
  2721.       public float Distance {
  2722.         get;
  2723.         set;
  2724.       }
  2725.  
  2726.       [JsonProperty(PropertyName = "Time Before Vehicle Wipe (Seconds)")]
  2727.       public double WipeTime {
  2728.         get;
  2729.         set;
  2730.       }
  2731.  
  2732.       [JsonProperty(PropertyName = "Exclude cupboard zones when wiping")]
  2733.       public bool ExcludeCupboard {
  2734.         get;
  2735.         set;
  2736.       }
  2737.  
  2738.       [JsonProperty(PropertyName = "Maximum Health")]
  2739.       public float MaxHealth {
  2740.         get;
  2741.         set;
  2742.       }
  2743.  
  2744.       [JsonProperty(PropertyName = "Can Recall Maximum Distance")]
  2745.       public float RecallMaxDistance {
  2746.         get;
  2747.         set;
  2748.       }
  2749.  
  2750.       [JsonProperty(PropertyName = "Can Kill Maximum Distance")]
  2751.       public float KillMaxDistance {
  2752.         get;
  2753.         set;
  2754.       }
  2755.  
  2756.       [JsonProperty(PropertyName = "Minimum distance from player to recall or spawn")]
  2757.       public float MinDistanceForPlayers {
  2758.         get;
  2759.         set;
  2760.       } = 3 f;
  2761.  
  2762.       [JsonProperty(PropertyName = "Remove License Once Crashed")]
  2763.       public bool RemoveLicenseOnceCrash {
  2764.         get;
  2765.         set;
  2766.       }
  2767.  
  2768.       [JsonProperty(PropertyName = "Commands")]
  2769.       public List < string > Commands {
  2770.         get;
  2771.         set;
  2772.       } = new List < string > ();
  2773.  
  2774.       [JsonProperty(PropertyName = "Purchase Prices")]
  2775.       public Dictionary < string, PriceInfo > PurchasePrices {
  2776.         get;
  2777.         set;
  2778.       } = new Dictionary < string, PriceInfo > ();
  2779.  
  2780.       [JsonProperty(PropertyName = "Spawn Prices")]
  2781.       public Dictionary < string, PriceInfo > SpawnPrices {
  2782.         get;
  2783.         set;
  2784.       } = new Dictionary < string, PriceInfo > ();
  2785.  
  2786.       [JsonProperty(PropertyName = "Recall Prices")]
  2787.       public Dictionary < string, PriceInfo > RecallPrices {
  2788.         get;
  2789.         set;
  2790.       } = new Dictionary < string, PriceInfo > ();
  2791.  
  2792.       [JsonProperty(PropertyName = "Recall Cooldown Bypass Prices")]
  2793.       public Dictionary < string, PriceInfo > BypassRecallCooldownPrices {
  2794.         get;
  2795.         set;
  2796.       } = new Dictionary < string, PriceInfo > ();
  2797.  
  2798.       [JsonProperty(PropertyName = "Spawn Cooldown Bypass Prices")]
  2799.       public Dictionary < string, PriceInfo > BypassSpawnCooldownPrices {
  2800.         get;
  2801.         set;
  2802.       } = new Dictionary < string, PriceInfo > ();
  2803.  
  2804.       [JsonProperty(PropertyName = "Spawn Cooldown (Seconds)")]
  2805.       public double SpawnCooldown {
  2806.         get;
  2807.         set;
  2808.       }
  2809.  
  2810.       [JsonProperty(PropertyName = "Recall Cooldown (Seconds)")]
  2811.       public double RecallCooldown {
  2812.         get;
  2813.         set;
  2814.       }
  2815.  
  2816.       [JsonProperty(PropertyName = "Cooldown Permissions")]
  2817.       public Dictionary < string, CooldownPermission > CooldownPermissions {
  2818.         get;
  2819.         set;
  2820.       } = new Dictionary < string, CooldownPermission > ();
  2821.  
  2822.       #endregion Properties
  2823.  
  2824.       protected ConfigData configData => Instance.configData;
  2825.  
  2826.       public virtual bool IsWaterVehicle => false;
  2827.       public virtual bool IsTrainVehicle => false;
  2828.       public virtual bool IsNormalVehicle => true;
  2829.       public virtual bool IsFightVehicle => false;
  2830.       public virtual bool IsModularVehicle => false;
  2831.       public virtual bool IsConnectableVehicle => false;
  2832.  
  2833.       protected virtual EntityFuelSystem GetFuelSystem(BaseEntity entity) {
  2834.         return null;
  2835.       }
  2836.  
  2837.       protected virtual IEnumerable < ItemContainer > GetInventories(BaseEntity entity) {
  2838.         yield
  2839.         break;
  2840.       }
  2841.  
  2842.       #region Spawn
  2843.  
  2844.       protected virtual string GetVehiclePrefab(string vehicleType) {
  2845.         NormalVehicleType normalVehicleType;
  2846.         if (Enum.TryParse(vehicleType, out normalVehicleType) && Enum.IsDefined(typeof (NormalVehicleType), normalVehicleType)) {
  2847.           switch (normalVehicleType) {
  2848.           case NormalVehicleType.Rowboat:
  2849.             return PREFAB_ROWBOAT;
  2850.           case NormalVehicleType.RHIB:
  2851.             return PREFAB_RHIB;
  2852.           case NormalVehicleType.Tugboat:
  2853.             return PREFAB_TUGBOAT;
  2854.           case NormalVehicleType.Sedan:
  2855.             return PREFAB_SEDAN;
  2856.           case NormalVehicleType.HotAirBalloon:
  2857.             return PREFAB_HOTAIRBALLOON;
  2858.           case NormalVehicleType.MiniCopter:
  2859.             return PREFAB_MINICOPTER;
  2860.           case NormalVehicleType.TransportHelicopter:
  2861.             return PREFAB_TRANSPORTCOPTER;
  2862.           case NormalVehicleType.Chinook:
  2863.             return PREFAB_CHINOOK;
  2864.           case NormalVehicleType.RidableHorse:
  2865.             return PREFAB_RIDABLEHORSE;
  2866.           case NormalVehicleType.WorkCart:
  2867.             return PREFAB_WORKCART;
  2868.           case NormalVehicleType.SedanRail:
  2869.             return PREFAB_SEDANRAIL;
  2870.           case NormalVehicleType.MagnetCrane:
  2871.             return PREFAB_MAGNET_CRANE;
  2872.           case NormalVehicleType.SubmarineSolo:
  2873.             return PREFAB_SUBMARINE_SOLO;
  2874.           case NormalVehicleType.SubmarineDuo:
  2875.             return PREFAB_SUBMARINE_DUO;
  2876.           case NormalVehicleType.Snowmobile:
  2877.             return PREFAB_SNOWMOBILE;
  2878.           case NormalVehicleType.TomahaSnowmobile:
  2879.             return PREFAB_SNOWMOBILE_TOMAHA;
  2880.           default:
  2881.             return null;
  2882.           }
  2883.         }
  2884.         return null;
  2885.       }
  2886.  
  2887.       public virtual BaseEntity SpawnVehicle(BasePlayer player, Vehicle vehicle, Vector3 position, Quaternion rotation) {
  2888.         var prefab = GetVehiclePrefab(vehicle.VehicleType);
  2889.         if (string.IsNullOrEmpty(prefab)) {
  2890.           throw new ArgumentException($"Prefab not found for {vehicle.VehicleType}");
  2891.         }
  2892.         var entity = GameManager.server.CreateEntity(prefab, position, rotation);
  2893.         if (entity == null) {
  2894.           return null;
  2895.         }
  2896.         PreSetupVehicle(entity, vehicle, player);
  2897.         entity.Spawn();
  2898.         SetupVehicle(entity, vehicle, player);
  2899.         if (!entity.IsDestroyed) {
  2900.           Instance.CacheVehicleEntity(entity, vehicle, player);
  2901.         } else {
  2902.           Instance.Print(player, Instance.Lang("NotSpawnedOrRecalled", player.UserIDString, DisplayName));
  2903.           return null;
  2904.         }
  2905.  
  2906.         return entity;
  2907.       }
  2908.  
  2909.       #region Setup
  2910.  
  2911.       public virtual void PreSetupVehicle(BaseEntity entity, Vehicle vehicle, BasePlayer player) {
  2912.         entity.enableSaving = configData.global.storeVehicle;
  2913.         entity.OwnerID = player.userID;
  2914.       }
  2915.  
  2916.       public virtual void SetupVehicle(BaseEntity entity, Vehicle vehicle, BasePlayer player, bool justCreated = true) {
  2917.         if (MaxHealth > 0 && Math.Abs(MaxHealth - entity.MaxHealth()) > 0 f) {
  2918.           (entity as BaseCombatEntity)?.InitializeHealth(MaxHealth, MaxHealth);
  2919.         }
  2920.         var helicopterVehicle = entity as BaseHelicopterVehicle;
  2921.         if (helicopterVehicle != null) {
  2922.           if (configData.global.noServerGibs) {
  2923.             helicopterVehicle.serverGibs.guid = string.Empty;
  2924.           }
  2925.           if (configData.global.noFireBall) {
  2926.             helicopterVehicle.fireBall.guid = string.Empty;
  2927.           }
  2928.           if (configData.global.noMapMarker) {
  2929.             var ch47Helicopter = entity as CH47Helicopter;
  2930.             if (ch47Helicopter != null) {
  2931.               if (ch47Helicopter.mapMarkerInstance) {
  2932.                 ch47Helicopter.mapMarkerInstance.Kill();
  2933.               }
  2934.               ch47Helicopter.mapMarkerEntityPrefab.guid = string.Empty;
  2935.             }
  2936.           }
  2937.         }
  2938.  
  2939.         if (configData.global.preventShattering) {
  2940.           var magnetLiftable = entity.GetComponent < MagnetLiftable > ();
  2941.           if (magnetLiftable != null) {
  2942.             UnityEngine.Object.Destroy(magnetLiftable);
  2943.           }
  2944.         }
  2945.       }
  2946.  
  2947.       #endregion Setup
  2948.  
  2949.       #endregion Spawn
  2950.  
  2951.       #region Recall
  2952.  
  2953.       public virtual void PreRecallVehicle(BasePlayer player, Vehicle vehicle, Vector3 position, Quaternion rotation) {
  2954.         if (configData.global.dismountAllPlayersRecall) {
  2955.           DismountAllPlayers(vehicle.Entity);
  2956.         }
  2957.  
  2958.         if (CanDropInventory()) {
  2959.           TryDropVehicleInventory(player, vehicle);
  2960.         }
  2961.  
  2962.         if (vehicle.Entity.HasParent()) {
  2963.           vehicle.Entity.SetParent(null, true, true);
  2964.         }
  2965.       }
  2966.  
  2967.       public virtual void PostRecallVehicle(BasePlayer player, Vehicle vehicle, Vector3 position, Quaternion rotation) {}
  2968.  
  2969.       #region DropInventory
  2970.  
  2971.       protected virtual bool CanDropInventory() {
  2972.         return false;
  2973.       }
  2974.  
  2975.       private void TryDropVehicleInventory(BasePlayer player, Vehicle vehicle) {
  2976.         var droppedItemContainer = DropVehicleInventory(player, vehicle);
  2977.         if (droppedItemContainer != null) {
  2978.           Instance.Print(player, Instance.Lang("VehicleInventoryDropped", player.UserIDString, DisplayName));
  2979.         }
  2980.       }
  2981.  
  2982.       protected virtual DroppedItemContainer DropVehicleInventory(BasePlayer player, Vehicle vehicle) {
  2983.         var inventories = GetInventories(vehicle.Entity);
  2984.         foreach(var inventory in inventories) {
  2985.           if (inventory != null) {
  2986.             return inventory.Drop(PREFAB_ITEM_DROP, vehicle.Entity.GetDropPosition(), vehicle.Entity.transform.rotation, 0);
  2987.           }
  2988.         }
  2989.         return null;
  2990.       }
  2991.  
  2992.       #endregion DropInventory
  2993.  
  2994.       #region Train Car
  2995.  
  2996.       protected bool TryGetTrainCarPositionAndRotation(BasePlayer player, Vehicle vehicle, ref string reason, ref Vector3 original, ref Quaternion rotation) {
  2997.         float distResult;
  2998.         TrainTrackSpline splineResult;
  2999.         if (!TrainTrackSpline.TryFindTrackNear(original, Distance, out splineResult, out distResult)) {
  3000.           reason = Instance.Lang("TooFarTrainTrack", player.UserIDString);
  3001.           return false;
  3002.         }
  3003.  
  3004.         var position = splineResult.GetPosition(distResult);
  3005.         if (!SpaceIsClearForTrainTrack(vehicle, position, rotation)) {
  3006.           reason = Instance.Lang("TooCloseTrainBarricadeOrWorkCart", player.UserIDString);
  3007.           return false;
  3008.         }
  3009.  
  3010.         original = position;
  3011.         reason = null;
  3012.         return true;
  3013.       }
  3014.  
  3015.       protected bool TryMoveToTrainTrackNear(TrainCar trainCar) {
  3016.         float distResult;
  3017.         TrainTrackSpline splineResult;
  3018.         if (TrainTrackSpline.TryFindTrackNear(trainCar.GetFrontWheelPos(), 2 f, out splineResult, out distResult)) {
  3019.           trainCar.FrontWheelSplineDist = distResult;
  3020.           Vector3 tangent;
  3021.           var positionAndTangent = splineResult.GetPositionAndTangent(trainCar.FrontWheelSplineDist, trainCar.transform.forward, out tangent);
  3022.           trainCar.SetTheRestFromFrontWheelData(ref splineResult, positionAndTangent, tangent, trainCar.localTrackSelection, null, true);
  3023.           trainCar.FrontTrackSection = splineResult;
  3024.           if (trainCar.SpaceIsClear()) {
  3025.             return true;
  3026.           }
  3027.         }
  3028.         return false;
  3029.       }
  3030.  
  3031.       protected bool SpaceIsClearForTrainTrack(Vehicle vehicle, Vector3 position, Quaternion rotation) {
  3032.         var colliders = Pool.GetList < Collider > ();
  3033.         if (vehicle.Entity == null) {
  3034.           var prefab = GetVehiclePrefab(vehicle.VehicleType);
  3035.           if (!string.IsNullOrEmpty(prefab)) {
  3036.             var trainEngine = GameManager.server.FindPrefab(prefab)?.GetComponent < TrainEngine > ();
  3037.             if (trainEngine != null) {
  3038.               GamePhysics.OverlapOBB(new OBB(position, trainEngine.transform.lossyScale, rotation, trainEngine.bounds), colliders, Layers.Mask.Vehicle_World);
  3039.             }
  3040.           }
  3041.         } else {
  3042.           GamePhysics.OverlapOBB(new OBB(position, vehicle.Entity.transform.lossyScale, rotation, vehicle.Entity.bounds), colliders, Layers.Mask.Vehicle_World);
  3043.         }
  3044.         var free = true;
  3045.         foreach(var item in colliders) {
  3046.           var baseEntity = item.ToBaseEntity();
  3047.           if (baseEntity == vehicle.Entity) {
  3048.             continue;
  3049.           }
  3050.           free = false;
  3051.           break;
  3052.         }
  3053.         Pool.FreeList(ref colliders);
  3054.         return free;
  3055.       }
  3056.  
  3057.       #endregion
  3058.  
  3059.       #endregion Recall
  3060.  
  3061.       #region Refund
  3062.  
  3063.       protected virtual bool CanRefundFuel(bool isCrash, bool isUnload) {
  3064.         return false;
  3065.       }
  3066.  
  3067.       protected virtual bool CanRefundInventory(bool isCrash, bool isUnload) {
  3068.         return false;
  3069.       }
  3070.  
  3071.       protected virtual void CollectVehicleItems(List < Item > items, Vehicle vehicle, bool isCrash, bool isUnload) {
  3072.         if (CanRefundFuel(isCrash, isUnload)) {
  3073.           var fuelSystem = GetFuelSystem(vehicle.Entity);
  3074.           if (fuelSystem != null) {
  3075.             var fuelContainer = fuelSystem.GetFuelContainer();
  3076.             if (fuelContainer != null && fuelContainer.inventory != null) {
  3077.               items.AddRange(fuelContainer.inventory.itemList);
  3078.             }
  3079.           }
  3080.         }
  3081.         if (CanRefundInventory(isCrash, isUnload)) {
  3082.           var inventories = GetInventories(vehicle.Entity);
  3083.           foreach(var inventory in inventories) {
  3084.             items.AddRange(inventory.itemList);
  3085.           }
  3086.         }
  3087.       }
  3088.  
  3089.       public void RefundVehicleItems(Vehicle vehicle, bool isCrash, bool isUnload) {
  3090.         var collect = Pool.GetList < Item > ();
  3091.  
  3092.         CollectVehicleItems(collect, vehicle, isCrash, isUnload);
  3093.  
  3094.         if (collect.Count > 0) {
  3095.           var player = RustCore.FindPlayerById(vehicle.PlayerId);
  3096.           if (player == null) {
  3097.             DropItemContainer(vehicle.Entity, vehicle.PlayerId, collect);
  3098.           } else {
  3099.             for (var i = collect.Count - 1; i >= 0; i--) {
  3100.               var item = collect[i];
  3101.               player.GiveItem(item);
  3102.             }
  3103.  
  3104.             if (player.IsConnected) {
  3105.               Instance.Print(player, Instance.Lang("RefundedVehicleItems", player.UserIDString, DisplayName));
  3106.             }
  3107.           }
  3108.         }
  3109.         Pool.FreeList(ref collect);
  3110.       }
  3111.  
  3112.       #endregion Refund
  3113.  
  3114.       #region GiveFuel
  3115.  
  3116.       protected void TryGiveFuel(BaseEntity entity, IFuelVehicle iFuelVehicle) {
  3117.         if (iFuelVehicle == null || iFuelVehicle.SpawnFuelAmount <= 0) {
  3118.           return;
  3119.         }
  3120.         var fuelSystem = GetFuelSystem(entity);
  3121.         if (fuelSystem != null) {
  3122.           var fuelContainer = fuelSystem.GetFuelContainer();
  3123.           if (fuelContainer != null && fuelContainer.inventory != null) {
  3124.             var fuelItem = ItemManager.CreateByItemID(ITEMID_FUEL, iFuelVehicle.SpawnFuelAmount);
  3125.             if (!fuelItem.MoveToContainer(fuelContainer.inventory)) {
  3126.               fuelItem.Remove();
  3127.             }
  3128.           }
  3129.         }
  3130.       }
  3131.  
  3132.       #endregion GiveFuel
  3133.  
  3134.       #region Permission
  3135.  
  3136.       public double GetCooldown(BasePlayer player, bool isSpawn) {
  3137.         var cooldown = isSpawn ? SpawnCooldown : RecallCooldown;
  3138.         foreach(var entry in CooldownPermissions) {
  3139.           var currentCooldown = isSpawn ? entry.Value.spawnCooldown : entry.Value.recallCooldown;
  3140.           if (cooldown > currentCooldown && Instance.permission.UserHasPermission(player.UserIDString, entry.Key)) {
  3141.             cooldown = currentCooldown;
  3142.           }
  3143.         }
  3144.         return cooldown;
  3145.       }
  3146.  
  3147.       #endregion Permission
  3148.  
  3149.       #region TryGetVehicleParams
  3150.  
  3151.       public virtual bool TryGetVehicleParams(BasePlayer player, Vehicle vehicle, out string reason, ref Vector3 spawnPos, ref Quaternion spawnRot) {
  3152.         Vector3 original;
  3153.         Quaternion rotation;
  3154.         if (!TryGetPositionAndRotation(player, vehicle, out reason, out original, out rotation)) {
  3155.           return false;
  3156.         }
  3157.  
  3158.         CorrectPositionAndRotation(player, vehicle, original, rotation, out spawnPos, out spawnRot);
  3159.         return true;
  3160.       }
  3161.  
  3162.       protected virtual float GetSpawnRotationAngle() {
  3163.         return 90 f;
  3164.       }
  3165.  
  3166.       protected virtual Vector3 GetOriginalPosition(BasePlayer player) {
  3167.         if (configData.global.spawnLookingAt || IsWaterVehicle || IsTrainVehicle) {
  3168.           return GetGroundPositionLookingAt(player, Distance, !IsTrainVehicle);
  3169.         }
  3170.  
  3171.         return player.transform.position;
  3172.       }
  3173.  
  3174.       protected virtual bool TryGetPositionAndRotation(BasePlayer player, Vehicle vehicle, out string reason, out Vector3 original, out Quaternion rotation) {
  3175.         original = GetOriginalPosition(player);
  3176.         rotation = Quaternion.identity;
  3177.         if (MinDistanceForPlayers > 0) {
  3178.           var nearbyPlayers = Pool.GetList < BasePlayer > ();
  3179.           Vis.Entities(original, MinDistanceForPlayers, nearbyPlayers, Layers.Mask.Player_Server);
  3180.           var flag = nearbyPlayers.Any(x => x.userID.IsSteamId() && x != player);
  3181.           Pool.FreeList(ref nearbyPlayers);
  3182.           if (flag) {
  3183.             reason = Instance.Lang("PlayersOnNearby", player.UserIDString, DisplayName);
  3184.             return false;
  3185.           }
  3186.         }
  3187.         if (IsWaterVehicle && !IsInWater(original)) {
  3188.           reason = Instance.Lang("NotLookingAtWater", player.UserIDString, DisplayName);
  3189.           return false;
  3190.         }
  3191.         reason = null;
  3192.         return true;
  3193.       }
  3194.  
  3195.       protected virtual void CorrectPositionAndRotation(BasePlayer player, Vehicle vehicle, Vector3 original, Quaternion rotation, out Vector3 spawnPos, out Quaternion spawnRot) {
  3196.         spawnPos = original;
  3197.         if (IsTrainVehicle) {
  3198.           var forward = player.eyes.HeadForward().WithY(0);
  3199.           spawnRot = forward != Vector3.zero ? Quaternion.LookRotation(forward) : Quaternion.identity;
  3200.           return;
  3201.         }
  3202.         if (configData.global.spawnLookingAt) {
  3203.           var needGetGround = true;
  3204.           if (IsWaterVehicle) {
  3205.             RaycastHit hit;
  3206.             if (Physics.Raycast(spawnPos, Vector3.up, out hit, 100, LAYER_GROUND) && hit.GetEntity() is StabilityEntity) {
  3207.               needGetGround = false; //At the dock
  3208.             }
  3209.           } else {
  3210.             if (TryGetCenterOfFloorNearby(ref spawnPos)) {
  3211.               needGetGround = false; //At the floor
  3212.             }
  3213.           }
  3214.           if (needGetGround) {
  3215.             spawnPos = GetGroundPosition(spawnPos);
  3216.           }
  3217.         } else {
  3218.           GetPositionWithNoPlayersNearby(player, ref spawnPos);
  3219.         }
  3220.  
  3221.         var normalized = (spawnPos - player.transform.position).normalized;
  3222.         var angle = normalized != Vector3.zero ? Quaternion.LookRotation(normalized).eulerAngles.y : Random.Range(0 f, 360 f);
  3223.         var rotationAngle = GetSpawnRotationAngle();
  3224.         spawnRot = Quaternion.Euler(Vector3.up * (angle + rotationAngle));
  3225.       }
  3226.  
  3227.       private void GetPositionWithNoPlayersNearby(BasePlayer player, ref Vector3 spawnPos) {
  3228.         var minDistance = Mathf.Min(MinDistanceForPlayers, 2.5 f);
  3229.         var maxDistance = Mathf.Max(Distance, minDistance);
  3230.  
  3231.         var players = new BasePlayer[1];
  3232.         var sourcePos = spawnPos;
  3233.         for (var i = 0; i < 10; i++) {
  3234.           spawnPos.x = sourcePos.x + Random.Range(minDistance, maxDistance) * (Random.value >= 0.5 f ? 1 : -1);
  3235.           spawnPos.z = sourcePos.z + Random.Range(minDistance, maxDistance) * (Random.value >= 0.5 f ? 1 : -1);
  3236.  
  3237.           if (BaseEntity.Query.Server.GetPlayersInSphere(spawnPos, minDistance, players, p => p.userID.IsSteamId() && p != player) == 0) {
  3238.             break;
  3239.           }
  3240.         }
  3241.         spawnPos = GetGroundPosition(spawnPos);
  3242.       }
  3243.  
  3244.       private bool TryGetCenterOfFloorNearby(ref Vector3 spawnPos) {
  3245.         var buildingBlocks = Pool.GetList < BuildingBlock > ();
  3246.         Vis.Entities(spawnPos, 2 f, buildingBlocks, Layers.Mask.Construction);
  3247.         if (buildingBlocks.Count > 0) {
  3248.           var position = spawnPos;
  3249.           var closestBuildingBlock = buildingBlocks
  3250.             .Where(x => !x.ShortPrefabName.Contains("wall"))
  3251.             .OrderBy(x => (x.transform.position - position).magnitude).FirstOrDefault();
  3252.           if (closestBuildingBlock != null) {
  3253.             var worldSpaceBounds = closestBuildingBlock.WorldSpaceBounds();
  3254.             spawnPos = worldSpaceBounds.position;
  3255.             spawnPos.y += worldSpaceBounds.extents.y;
  3256.             Pool.FreeList(ref buildingBlocks);
  3257.             return true;
  3258.           }
  3259.         }
  3260.         Pool.FreeList(ref buildingBlocks);
  3261.         return false;
  3262.       }
  3263.  
  3264.       #endregion TryGetVehicleParams
  3265.     }
  3266.  
  3267.     public abstract class FuelVehicleSettings: BaseVehicleSettings, IFuelVehicle {
  3268.       public int SpawnFuelAmount {
  3269.         get;
  3270.         set;
  3271.       }
  3272.       public bool RefundFuelOnKill {
  3273.         get;
  3274.         set;
  3275.       } = true;
  3276.       public bool RefundFuelOnCrash {
  3277.         get;
  3278.         set;
  3279.       } = true;
  3280.  
  3281.       public override void SetupVehicle(BaseEntity entity, Vehicle vehicle, BasePlayer player, bool justCreated = true) {
  3282.         if (justCreated) {
  3283.           TryGiveFuel(entity, this);
  3284.         }
  3285.         base.SetupVehicle(entity, vehicle, player, justCreated);
  3286.       }
  3287.  
  3288.       protected override bool CanRefundFuel(bool isCrash, bool isUnload) {
  3289.         return isUnload || (isCrash ? RefundFuelOnCrash : RefundFuelOnKill);
  3290.       }
  3291.     }
  3292.  
  3293.     public abstract class InventoryVehicleSettings: BaseVehicleSettings, IInventoryVehicle {
  3294.       public bool RefundInventoryOnKill {
  3295.         get;
  3296.         set;
  3297.       } = true;
  3298.       public bool RefundInventoryOnCrash {
  3299.         get;
  3300.         set;
  3301.       } = true;
  3302.       public bool DropInventoryOnRecall {
  3303.         get;
  3304.         set;
  3305.       }
  3306.  
  3307.       protected override bool CanDropInventory() {
  3308.         return DropInventoryOnRecall;
  3309.       }
  3310.  
  3311.       protected override bool CanRefundInventory(bool isCrash, bool isUnload) {
  3312.         return isUnload || (isCrash ? RefundInventoryOnCrash : RefundInventoryOnKill);
  3313.       }
  3314.     }
  3315.  
  3316.     public abstract class InvFuelVehicleSettings: BaseVehicleSettings, IFuelVehicle, IInventoryVehicle {
  3317.       public int SpawnFuelAmount {
  3318.         get;
  3319.         set;
  3320.       }
  3321.       public bool RefundFuelOnKill {
  3322.         get;
  3323.         set;
  3324.       } = true;
  3325.       public bool RefundFuelOnCrash {
  3326.         get;
  3327.         set;
  3328.       } = true;
  3329.       public bool RefundInventoryOnKill {
  3330.         get;
  3331.         set;
  3332.       } = true;
  3333.       public bool RefundInventoryOnCrash {
  3334.         get;
  3335.         set;
  3336.       } = true;
  3337.       public bool DropInventoryOnRecall {
  3338.         get;
  3339.         set;
  3340.       }
  3341.  
  3342.       public override void SetupVehicle(BaseEntity entity, Vehicle vehicle, BasePlayer player, bool justCreated = true) {
  3343.         if (justCreated) {
  3344.           TryGiveFuel(entity, this);
  3345.         }
  3346.         base.SetupVehicle(entity, vehicle, player, justCreated);
  3347.       }
  3348.  
  3349.       protected override bool CanDropInventory() {
  3350.         return DropInventoryOnRecall;
  3351.       }
  3352.  
  3353.       protected override bool CanRefundInventory(bool isCrash, bool isUnload) {
  3354.         return isUnload || (isCrash ? RefundInventoryOnCrash : RefundInventoryOnKill);
  3355.       }
  3356.  
  3357.       protected override bool CanRefundFuel(bool isCrash, bool isUnload) {
  3358.         return isUnload || (isCrash ? RefundFuelOnCrash : RefundFuelOnKill);
  3359.       }
  3360.     }
  3361.  
  3362.     #endregion BaseSettings
  3363.  
  3364.     #region Interface
  3365.  
  3366.     public interface IFuelVehicle {
  3367.       [JsonProperty(PropertyName = "Amount Of Fuel To Spawn", Order = 20)]
  3368.       int SpawnFuelAmount {
  3369.         get;
  3370.         set;
  3371.       }
  3372.  
  3373.       [JsonProperty(PropertyName = "Refund Fuel On Kill", Order = 21)]
  3374.       bool RefundFuelOnKill {
  3375.         get;
  3376.         set;
  3377.       }
  3378.  
  3379.       [JsonProperty(PropertyName = "Refund Fuel On Crash", Order = 22)]
  3380.       bool RefundFuelOnCrash {
  3381.         get;
  3382.         set;
  3383.       }
  3384.     }
  3385.  
  3386.     public interface IInventoryVehicle {
  3387.       [JsonProperty(PropertyName = "Refund Inventory On Kill", Order = 30)]
  3388.       bool RefundInventoryOnKill {
  3389.         get;
  3390.         set;
  3391.       }
  3392.  
  3393.       [JsonProperty(PropertyName = "Refund Inventory On Crash", Order = 31)]
  3394.       bool RefundInventoryOnCrash {
  3395.         get;
  3396.         set;
  3397.       }
  3398.  
  3399.       [JsonProperty(PropertyName = "Drop Inventory Items When Vehicle Recall", Order = 49)]
  3400.       bool DropInventoryOnRecall {
  3401.         get;
  3402.         set;
  3403.       }
  3404.     }
  3405.  
  3406.     public interface IModularVehicle {
  3407.       [JsonProperty(PropertyName = "Refund Engine Items On Kill", Order = 40)]
  3408.       bool RefundEngineOnKill {
  3409.         get;
  3410.         set;
  3411.       }
  3412.  
  3413.       [JsonProperty(PropertyName = "Refund Engine Items On Crash", Order = 41)]
  3414.       bool RefundEngineOnCrash {
  3415.         get;
  3416.         set;
  3417.       }
  3418.  
  3419.       [JsonProperty(PropertyName = "Refund Module Items On Kill", Order = 42)]
  3420.       bool RefundModuleOnKill {
  3421.         get;
  3422.         set;
  3423.       }
  3424.  
  3425.       [JsonProperty(PropertyName = "Refund Module Items On Crash", Order = 43)]
  3426.       bool RefundModuleOnCrash {
  3427.         get;
  3428.         set;
  3429.       }
  3430.     }
  3431.  
  3432.     public interface IAmmoVehicle {
  3433.       [JsonProperty(PropertyName = "Amount Of Ammo To Spawn", Order = 20)]
  3434.       int SpawnAmmoAmount {
  3435.         get;
  3436.         set;
  3437.       }
  3438.     }
  3439.  
  3440.     public interface ITrainVehicle {}
  3441.  
  3442.     #endregion Interface
  3443.  
  3444.     #region Struct
  3445.  
  3446.     public struct CooldownPermission {
  3447.       public double spawnCooldown;
  3448.       public double recallCooldown;
  3449.     }
  3450.  
  3451.     public struct ModuleItem {
  3452.       public string shortName;
  3453.       public float healthPercentage;
  3454.     }
  3455.  
  3456.     public struct EngineItem {
  3457.       public string shortName;
  3458.       public float conditionPercentage;
  3459.     }
  3460.  
  3461.     public struct PriceInfo {
  3462.       public int amount;
  3463.       public string displayName;
  3464.     }
  3465.  
  3466.     public struct TrainComponent {
  3467.       public TrainComponentType type;
  3468.     }
  3469.  
  3470.     #endregion Struct
  3471.  
  3472.     #region VehicleSettings
  3473.  
  3474.     public class SedanSettings: BaseVehicleSettings {}
  3475.  
  3476.     public class ChinookSettings: BaseVehicleSettings {}
  3477.  
  3478.     public class RowboatSettings: InvFuelVehicleSettings {
  3479.       public override bool IsWaterVehicle => true;
  3480.  
  3481.       protected override EntityFuelSystem GetFuelSystem(BaseEntity entity) {
  3482.         return (entity as MotorRowboat)?.GetFuelSystem();
  3483.       }
  3484.  
  3485.       protected override IEnumerable < ItemContainer > GetInventories(BaseEntity entity) {
  3486.         yield
  3487.         return (entity as MotorRowboat)?.storageUnitInstance.Get(true)?.inventory;
  3488.       }
  3489.     }
  3490.  
  3491.     public class RhibSettings: RowboatSettings {}
  3492.  
  3493.     public class TugboatSettings: InvFuelVehicleSettings {
  3494.       public override bool IsWaterVehicle => true;
  3495.  
  3496.       protected override EntityFuelSystem GetFuelSystem(BaseEntity entity) {
  3497.         return (entity as MotorRowboat)?.GetFuelSystem();
  3498.       }
  3499.     }
  3500.  
  3501.     public class HotAirBalloonSettings: InvFuelVehicleSettings {
  3502.       protected override float GetSpawnRotationAngle() {
  3503.         return 180 f;
  3504.       }
  3505.  
  3506.       protected override EntityFuelSystem GetFuelSystem(BaseEntity entity) {
  3507.         return (entity as HotAirBalloon)?.fuelSystem;
  3508.       }
  3509.  
  3510.       protected override IEnumerable < ItemContainer > GetInventories(BaseEntity entity) {
  3511.         yield
  3512.         return (entity as HotAirBalloon)?.storageUnitInstance.Get(true)?.inventory;
  3513.       }
  3514.     }
  3515.  
  3516.     public class MiniCopterSettings: FuelVehicleSettings {
  3517.       public override bool IsFightVehicle => true;
  3518.  
  3519.       protected override EntityFuelSystem GetFuelSystem(BaseEntity entity) {
  3520.         return (entity as MiniCopter)?.GetFuelSystem();
  3521.       }
  3522.     }
  3523.  
  3524.     public class TransportHelicopterSettings: MiniCopterSettings {}
  3525.  
  3526.     public class RidableHorseSettings: InventoryVehicleSettings {
  3527.       protected override IEnumerable < ItemContainer > GetInventories(BaseEntity entity) {
  3528.         yield
  3529.         return (entity as RidableHorse)?.inventory;
  3530.       }
  3531.  
  3532.       public override void PostRecallVehicle(BasePlayer player, Vehicle vehicle, Vector3 position, Quaternion rotation) {
  3533.         base.PostRecallVehicle(player, vehicle, position, rotation);
  3534.  
  3535.         var ridableHorse = vehicle.Entity as RidableHorse;
  3536.         if (ridableHorse != null) {
  3537.           ridableHorse.TryLeaveHitch();
  3538.           ridableHorse.DropToGround(ridableHorse.transform.position, true); //ridableHorse.UpdateDropToGroundForDuration(2f);
  3539.         }
  3540.       }
  3541.  
  3542.       protected override void CorrectPositionAndRotation(BasePlayer player, Vehicle vehicle, Vector3 original, Quaternion rotation, out Vector3 spawnPos, out Quaternion spawnRot) {
  3543.         base.CorrectPositionAndRotation(player, vehicle, original, rotation, out spawnPos, out spawnRot);
  3544.         spawnPos += Vector3.up * 0.3 f;
  3545.       }
  3546.     }
  3547.  
  3548.     // Only work cart (TrainEngine)
  3549.     public class WorkCartSettings: FuelVehicleSettings {
  3550.       public override bool IsTrainVehicle => true;
  3551.  
  3552.       public bool IsConnectableEngine(TrainEngine trainEngine) {
  3553.         return trainEngine.frontCoupling != null && trainEngine.rearCoupling != null;
  3554.       }
  3555.  
  3556.       protected override EntityFuelSystem GetFuelSystem(BaseEntity entity) {
  3557.         return (entity as TrainEngine)?.GetFuelSystem();
  3558.       }
  3559.  
  3560.       public override void PostRecallVehicle(BasePlayer player, Vehicle vehicle, Vector3 position, Quaternion rotation) {
  3561.         base.PostRecallVehicle(player, vehicle, position, rotation);
  3562.         var trainEngine = vehicle.Entity as TrainEngine;
  3563.         if (trainEngine != null) {
  3564.           TryMoveToTrainTrackNear(trainEngine);
  3565.         }
  3566.       }
  3567.  
  3568.       protected override bool TryGetPositionAndRotation(BasePlayer player, Vehicle vehicle, out string reason, out Vector3 original, out Quaternion rotation) {
  3569.         if (base.TryGetPositionAndRotation(player, vehicle, out reason, out original, out rotation)) {
  3570.           if (!TryGetTrainCarPositionAndRotation(player, vehicle, ref reason, ref original, ref rotation)) {
  3571.             return false;
  3572.           }
  3573.         }
  3574.         return true;
  3575.       }
  3576.     }
  3577.  
  3578.     public class MagnetCraneSettings: FuelVehicleSettings {
  3579.       protected override EntityFuelSystem GetFuelSystem(BaseEntity entity) {
  3580.         return (entity as MagnetCrane)?.GetFuelSystem();
  3581.       }
  3582.     }
  3583.  
  3584.     public class SubmarineSoloSettings: InvFuelVehicleSettings, IAmmoVehicle {
  3585.       private
  3586.       const int AMMO_ITEM_ID = -1671551935;
  3587.  
  3588.       public int SpawnAmmoAmount {
  3589.         get;
  3590.         set;
  3591.       }
  3592.       public override bool IsWaterVehicle => true;
  3593.  
  3594.       protected override EntityFuelSystem GetFuelSystem(BaseEntity entity) {
  3595.         return (entity as BaseSubmarine)?.GetFuelSystem();
  3596.       }
  3597.  
  3598.       protected override IEnumerable < ItemContainer > GetInventories(BaseEntity entity) {
  3599.         yield
  3600.         return (entity as BaseSubmarine)?.GetItemContainer()?.inventory;
  3601.         yield
  3602.         return (entity as BaseSubmarine)?.GetTorpedoContainer()?.inventory;
  3603.       }
  3604.  
  3605.       public override void SetupVehicle(BaseEntity entity, Vehicle vehicle, BasePlayer player, bool justCreated = true) {
  3606.         if (justCreated) {
  3607.           TryGiveAmmo(entity);
  3608.         }
  3609.         base.SetupVehicle(entity, vehicle, player, justCreated);
  3610.       }
  3611.  
  3612.       private void TryGiveAmmo(BaseEntity entity) {
  3613.         if (entity == null || SpawnAmmoAmount <= 0) {
  3614.           return;
  3615.         }
  3616.         var ammoContainer = (entity as BaseSubmarine)?.GetTorpedoContainer();
  3617.         if (ammoContainer != null && ammoContainer.inventory != null) {
  3618.           var ammoItem = ItemManager.CreateByItemID(AMMO_ITEM_ID, SpawnAmmoAmount);
  3619.           if (!ammoItem.MoveToContainer(ammoContainer.inventory)) {
  3620.             ammoItem.Remove();
  3621.           }
  3622.         }
  3623.       }
  3624.     }
  3625.  
  3626.     public class SubmarineDuoSettings: SubmarineSoloSettings {}
  3627.  
  3628.     public class SnowmobileSettings: InvFuelVehicleSettings {
  3629.       protected override EntityFuelSystem GetFuelSystem(BaseEntity entity) {
  3630.         return (entity as Snowmobile)?.GetFuelSystem();
  3631.       }
  3632.  
  3633.       protected override IEnumerable < ItemContainer > GetInventories(BaseEntity entity) {
  3634.         yield
  3635.         return (entity as Snowmobile)?.GetItemContainer()?.inventory;
  3636.       }
  3637.     }
  3638.  
  3639.     public class ModularVehicleSettings: InvFuelVehicleSettings, IModularVehicle {
  3640.       #region Properties
  3641.  
  3642.       public bool RefundEngineOnKill {
  3643.         get;
  3644.         set;
  3645.       } = true;
  3646.       public bool RefundEngineOnCrash {
  3647.         get;
  3648.         set;
  3649.       } = true;
  3650.       public bool RefundModuleOnKill {
  3651.         get;
  3652.         set;
  3653.       } = true;
  3654.       public bool RefundModuleOnCrash {
  3655.         get;
  3656.         set;
  3657.       } = true;
  3658.  
  3659.       [JsonProperty(PropertyName = "Chassis Type (Small, Medium, Large)", Order = 50)]
  3660.       public ChassisType ChassisType {
  3661.         get;
  3662.         set;
  3663.       } = ChassisType.Small;
  3664.  
  3665.       [JsonProperty(PropertyName = "Vehicle Module Items", Order = 51)]
  3666.       public List < ModuleItem > ModuleItems {
  3667.         get;
  3668.         set;
  3669.       } = new List < ModuleItem > ();
  3670.  
  3671.       [JsonProperty(PropertyName = "Vehicle Engine Items", Order = 52)]
  3672.       public List < EngineItem > EngineItems {
  3673.         get;
  3674.         set;
  3675.       } = new List < EngineItem > ();
  3676.  
  3677.       #endregion Properties
  3678.  
  3679.       #region ModuleItems
  3680.  
  3681.       private List < ModuleItem > _validModuleItems;
  3682.  
  3683.       public IEnumerable < ModuleItem > ValidModuleItems {
  3684.         get {
  3685.           if (_validModuleItems == null) {
  3686.             _validModuleItems = new List < ModuleItem > ();
  3687.             foreach(var modularItem in ModuleItems) {
  3688.               var itemDefinition = ItemManager.FindItemDefinition(modularItem.shortName);
  3689.               if (itemDefinition != null) {
  3690.                 var itemModVehicleModule = itemDefinition.GetComponent < ItemModVehicleModule > ();
  3691.                 if (itemModVehicleModule == null || !itemModVehicleModule.entityPrefab.isValid) {
  3692.                   Instance.PrintError($"'{modularItem}' is not a valid vehicle module");
  3693.                   continue;
  3694.                 }
  3695.                 _validModuleItems.Add(modularItem);
  3696.               }
  3697.             }
  3698.           }
  3699.           return _validModuleItems;
  3700.         }
  3701.       }
  3702.  
  3703.       public IEnumerable < Item > CreateModuleItems() {
  3704.         foreach(var moduleItem in ValidModuleItems) {
  3705.           var item = ItemManager.CreateByName(moduleItem.shortName);
  3706.           if (item != null) {
  3707.             item.condition = item.maxCondition * (moduleItem.healthPercentage / 100 f);
  3708.             item.MarkDirty();
  3709.             yield
  3710.             return item;
  3711.           }
  3712.         }
  3713.       }
  3714.  
  3715.       #endregion ModuleItems
  3716.  
  3717.       #region EngineItems
  3718.  
  3719.       private List < EngineItem > _validEngineItems;
  3720.  
  3721.       public IEnumerable < EngineItem > ValidEngineItems {
  3722.         get {
  3723.           if (_validEngineItems == null) {
  3724.             _validEngineItems = new List < EngineItem > ();
  3725.             foreach(var modularItem in EngineItems) {
  3726.               var itemDefinition = ItemManager.FindItemDefinition(modularItem.shortName);
  3727.               if (itemDefinition != null) {
  3728.                 var itemModEngineItem = itemDefinition.GetComponent < ItemModEngineItem > ();
  3729.                 if (itemModEngineItem == null) {
  3730.                   Instance.PrintError($"'{modularItem}' is not a valid engine item");
  3731.                   continue;
  3732.                 }
  3733.                 _validEngineItems.Add(modularItem);
  3734.               }
  3735.             }
  3736.           }
  3737.           return _validEngineItems;
  3738.         }
  3739.       }
  3740.  
  3741.       public IEnumerable < Item > CreateEngineItems() {
  3742.         foreach(var engineItem in ValidEngineItems) {
  3743.           var item = ItemManager.CreateByName(engineItem.shortName);
  3744.           if (item != null) {
  3745.             item.condition = item.maxCondition * (engineItem.conditionPercentage / 100 f);
  3746.             item.MarkDirty();
  3747.             yield
  3748.             return item;
  3749.           }
  3750.         }
  3751.       }
  3752.  
  3753.       #endregion EngineItems
  3754.  
  3755.       public override bool IsNormalVehicle => false;
  3756.       public override bool IsModularVehicle => true;
  3757.  
  3758.       protected override EntityFuelSystem GetFuelSystem(BaseEntity entity) {
  3759.         return (entity as ModularCar)?.GetFuelSystem();
  3760.       }
  3761.  
  3762.       #region Spawn
  3763.  
  3764.       protected override string GetVehiclePrefab(string vehicleType) {
  3765.         switch (ChassisType) {
  3766.         case ChassisType.Small:
  3767.           return PREFAB_CHASSIS_SMALL;
  3768.         case ChassisType.Medium:
  3769.           return PREFAB_CHASSIS_MEDIUM;
  3770.         case ChassisType.Large:
  3771.           return PREFAB_CHASSIS_LARGE;
  3772.         default:
  3773.           return null;
  3774.         }
  3775.       }
  3776.  
  3777.       #region Setup
  3778.  
  3779.       public override void SetupVehicle(BaseEntity entity, Vehicle vehicle, BasePlayer player, bool justCreated = true) {
  3780.         var modularCar = entity as ModularCar;
  3781.         if (modularCar != null) {
  3782.           if (ValidModuleItems.Any()) {
  3783.             AttacheVehicleModules(modularCar, vehicle);
  3784.           }
  3785.           if (ValidEngineItems.Any()) {
  3786.             Instance.NextTick(() => {
  3787.               AddItemsToVehicleEngine(modularCar, vehicle);
  3788.             });
  3789.           }
  3790.         }
  3791.         base.SetupVehicle(entity, vehicle, player, justCreated);
  3792.       }
  3793.  
  3794.       #endregion Setup
  3795.  
  3796.       #endregion Spawn
  3797.  
  3798.       #region Recall
  3799.  
  3800.       public override void PreRecallVehicle(BasePlayer player, Vehicle vehicle, Vector3 position, Quaternion rotation) {
  3801.         base.PreRecallVehicle(player, vehicle, position, rotation);
  3802.  
  3803.         if (vehicle.Entity is ModularCar) {
  3804.           var modularCarGarages = Pool.GetList < ModularCarGarage > ();
  3805.           Vis.Entities(vehicle.Entity.transform.position, 3 f, modularCarGarages, Layers.Mask.Deployed | Layers.Mask.Default);
  3806.           var modularCarGarage = modularCarGarages.FirstOrDefault(x => x.carOccupant == vehicle.Entity);
  3807.           Pool.FreeList(ref modularCarGarages);
  3808.           if (modularCarGarage != null) {
  3809.             modularCarGarage.enabled = false;
  3810.             modularCarGarage.ReleaseOccupant();
  3811.             modularCarGarage.Invoke(() => modularCarGarage.enabled = true, 0.25 f);
  3812.           }
  3813.         }
  3814.       }
  3815.  
  3816.       #region DropInventory
  3817.  
  3818.       protected override DroppedItemContainer DropVehicleInventory(BasePlayer player, Vehicle vehicle) {
  3819.         var modularCar = vehicle.Entity as ModularCar;
  3820.         if (modularCar != null) {
  3821.           foreach(var moduleEntity in modularCar.AttachedModuleEntities) {
  3822.             if (moduleEntity is VehicleModuleEngine) {
  3823.               continue;
  3824.             }
  3825.             var moduleStorage = moduleEntity as VehicleModuleStorage;
  3826.             if (moduleStorage != null) {
  3827.               return moduleStorage.GetContainer()?.inventory?.Drop(PREFAB_ITEM_DROP, vehicle.Entity.GetDropPosition(), vehicle.Entity.transform.rotation, 0);
  3828.             }
  3829.           }
  3830.         }
  3831.         return null;
  3832.       }
  3833.  
  3834.       #endregion DropInventory
  3835.  
  3836.       #endregion Recall
  3837.  
  3838.       #region Refund
  3839.  
  3840.       private void GetRefundStatus(bool isCrash, bool isUnload, out bool refundFuel, out bool refundInventory, out bool refundEngine, out bool refundModule) {
  3841.         if (isUnload) {
  3842.           refundFuel = refundInventory = refundEngine = refundModule = true;
  3843.           return;
  3844.         }
  3845.         refundFuel = isCrash ? RefundFuelOnCrash : RefundFuelOnKill;
  3846.         refundInventory = isCrash ? RefundInventoryOnCrash : RefundInventoryOnKill;
  3847.         refundEngine = isCrash ? RefundEngineOnCrash : RefundEngineOnKill;
  3848.         refundModule = isCrash ? RefundModuleOnCrash : RefundModuleOnKill;
  3849.       }
  3850.  
  3851.       protected override void CollectVehicleItems(List < Item > items, Vehicle vehicle, bool isCrash, bool isUnload) {
  3852.         var modularCar = vehicle.Entity as ModularCar;
  3853.         if (modularCar != null) {
  3854.           bool refundFuel, refundInventory, refundEngine, refundModule;
  3855.           GetRefundStatus(isCrash, isUnload, out refundFuel, out refundInventory, out refundEngine, out refundModule);
  3856.  
  3857.           foreach(var moduleEntity in modularCar.AttachedModuleEntities) {
  3858.             if (refundEngine) {
  3859.               var moduleEngine = moduleEntity as VehicleModuleEngine;
  3860.               if (moduleEngine != null) {
  3861.                 var engineContainer = moduleEngine.GetContainer()?.inventory;
  3862.                 if (engineContainer != null) {
  3863.                   items.AddRange(engineContainer.itemList);
  3864.                 }
  3865.                 continue;
  3866.               }
  3867.             }
  3868.             if (refundInventory) {
  3869.               var moduleStorage = moduleEntity as VehicleModuleStorage;
  3870.               if (moduleStorage != null && !(moduleEntity is VehicleModuleEngine)) {
  3871.                 var storageContainer = moduleStorage.GetContainer()?.inventory;
  3872.                 if (storageContainer != null) {
  3873.                   items.AddRange(storageContainer.itemList);
  3874.                 }
  3875.               }
  3876.             }
  3877.           }
  3878.           if (refundFuel) {
  3879.             var fuelSystem = GetFuelSystem(modularCar);
  3880.             if (fuelSystem != null) {
  3881.               var fuelContainer = fuelSystem.GetFuelContainer()?.inventory;
  3882.               if (fuelContainer != null) {
  3883.                 items.AddRange(fuelContainer.itemList);
  3884.               }
  3885.             }
  3886.           }
  3887.           if (refundModule) {
  3888.             var moduleContainer = modularCar.Inventory?.ModuleContainer;
  3889.             if (moduleContainer != null) {
  3890.               items.AddRange(moduleContainer.itemList);
  3891.             }
  3892.           }
  3893.           //var chassisContainer = modularCar.Inventory?.ChassisContainer;
  3894.           //if (chassisContainer != null)
  3895.           //{
  3896.           //    collect.AddRange(chassisContainer.itemList);
  3897.           //}
  3898.         }
  3899.       }
  3900.  
  3901.       #endregion Refund
  3902.  
  3903.       #region VehicleModules
  3904.  
  3905.       private void AttacheVehicleModules(ModularCar modularCar, Vehicle vehicle) {
  3906.         foreach(var moduleItem in CreateModuleItems()) {
  3907.           if (!modularCar.TryAddModule(moduleItem)) {
  3908.             Instance?.PrintError($"Module item '{moduleItem.info.shortname}' in '{vehicle.VehicleType}' cannot be attached to the vehicle");
  3909.             moduleItem.Remove();
  3910.           }
  3911.         }
  3912.       }
  3913.  
  3914.       private void AddItemsToVehicleEngine(ModularCar modularCar, Vehicle vehicle) {
  3915.         if (modularCar == null || modularCar.IsDestroyed) {
  3916.           return;
  3917.         }
  3918.         foreach(var moduleEntity in modularCar.AttachedModuleEntities) {
  3919.           var vehicleModuleEngine = moduleEntity as VehicleModuleEngine;
  3920.           if (vehicleModuleEngine != null) {
  3921.             var engineInventory = vehicleModuleEngine.GetContainer()?.inventory;
  3922.             if (engineInventory != null) {
  3923.               foreach(var engineItem in CreateEngineItems()) {
  3924.                 var moved = false;
  3925.                 for (var i = 0; i < engineInventory.capacity; i++) {
  3926.                   if (engineItem.MoveToContainer(engineInventory, i, false)) {
  3927.                     moved = true;
  3928.                     break;
  3929.                   }
  3930.                 }
  3931.                 if (!moved) {
  3932.                   Instance?.PrintError($"Engine item '{engineItem.info.shortname}' in '{vehicle.VehicleType}' cannot be move to the vehicle engine inventory");
  3933.                   engineItem.Remove();
  3934.                   engineItem.DoRemove();
  3935.                 }
  3936.               }
  3937.             }
  3938.           }
  3939.         }
  3940.       }
  3941.  
  3942.       #endregion VehicleModules
  3943.     }
  3944.  
  3945.     public class TrainVehicleSettings: FuelVehicleSettings, ITrainVehicle {
  3946.       #region Properties
  3947.  
  3948.         [JsonProperty(PropertyName = "Train Components", Order = 50)]
  3949.       public List < TrainComponent > TrainComponents {
  3950.         get;
  3951.         set;
  3952.       } = new List < TrainComponent > ();
  3953.  
  3954.       #endregion Properties
  3955.  
  3956.       public override bool IsNormalVehicle => false;
  3957.       public override bool IsTrainVehicle => true;
  3958.       public override bool IsConnectableVehicle => true;
  3959.  
  3960.       protected override EntityFuelSystem GetFuelSystem(BaseEntity entity) {
  3961.         return (entity as TrainCar)?.GetFuelSystem();
  3962.       }
  3963.  
  3964.       protected override string GetVehiclePrefab(string vehicleType) {
  3965.         return TrainComponents.Count > 0 ? GetTrainVehiclePrefab(TrainComponents[0].type) : base.GetVehiclePrefab(vehicleType);
  3966.       }
  3967.  
  3968.       #region Spawn
  3969.  
  3970.       private static string GetTrainVehiclePrefab(TrainComponentType componentType) {
  3971.         switch (componentType) {
  3972.         case TrainComponentType.Engine:
  3973.           return PREFAB_TRAINENGINE;
  3974.         case TrainComponentType.CoveredEngine:
  3975.           return PREFAB_TRAINENGINE_COVERED;
  3976.         case TrainComponentType.Locomotive:
  3977.           return PREFAB_TRAINENGINE_LOCOMOTIVE;
  3978.         case TrainComponentType.WagonA:
  3979.           return PREFAB_TRAINWAGON_A;
  3980.         case TrainComponentType.WagonB:
  3981.           return PREFAB_TRAINWAGON_B;
  3982.         case TrainComponentType.WagonC:
  3983.           return PREFAB_TRAINWAGON_C;
  3984.         case TrainComponentType.Unloadable:
  3985.           return PREFAB_TRAINWAGON_UNLOADABLE;
  3986.         case TrainComponentType.UnloadableLoot:
  3987.           return PREFAB_TRAINWAGON_UNLOADABLE_LOOT;
  3988.         case TrainComponentType.UnloadableFuel:
  3989.           return PREFAB_TRAINWAGON_UNLOADABLE_FUEL;
  3990.         case TrainComponentType.Caboose:
  3991.           return PREFAB_CABOOSE;
  3992.         default:
  3993.           return null;
  3994.         }
  3995.       }
  3996.  
  3997.       public override BaseEntity SpawnVehicle(BasePlayer player, Vehicle vehicle, Vector3 position, Quaternion rotation) {
  3998.         TrainCar prevTrainCar = null, primaryTrainCar = null;
  3999.         foreach(var component in TrainComponents) {
  4000.           var prefab = GetTrainVehiclePrefab(component.type);
  4001.           if (string.IsNullOrEmpty(prefab)) {
  4002.             throw new ArgumentException($"Prefab not found for {vehicle.VehicleType}({component.type})");
  4003.           }
  4004.           float distResult;
  4005.           TrainTrackSpline splineResult;
  4006.           if (prevTrainCar == null) {
  4007.             if (TrainTrackSpline.TryFindTrackNear(position, 20 f, out splineResult, out distResult)) {
  4008.               position = splineResult.GetPosition(distResult);
  4009.               prevTrainCar = GameManager.server.CreateEntity(prefab, position, rotation) as TrainCar;
  4010.               if (prevTrainCar == null) {
  4011.                 continue;
  4012.               }
  4013.               PreSetupVehicle(prevTrainCar, vehicle, player);
  4014.               prevTrainCar.Spawn();
  4015.               prevTrainCar.CancelInvoke(prevTrainCar.KillMessage);
  4016.               SetupVehicle(prevTrainCar, vehicle, player);
  4017.             }
  4018.           } else {
  4019.             var newTrainCar = GameManager.server.CreateEntity(prefab, prevTrainCar.transform.position, prevTrainCar.transform.rotation) as TrainCar;
  4020.             if (newTrainCar == null) {
  4021.               continue;
  4022.             }
  4023.  
  4024.             position += prevTrainCar.transform.rotation * (newTrainCar.bounds.center - Vector3.forward * (newTrainCar.bounds.extents.z + prevTrainCar.bounds.extents.z));
  4025.             if (TrainTrackSpline.TryFindTrackNear(position, 20 f, out splineResult, out distResult)) {
  4026.               position = splineResult.GetPosition(distResult);
  4027.               newTrainCar.transform.position = position;
  4028.  
  4029.               PreSetupVehicle(newTrainCar, vehicle, player);
  4030.               newTrainCar.Spawn();
  4031.               newTrainCar.CancelInvoke(newTrainCar.KillMessage);
  4032.               SetupVehicle(newTrainCar, vehicle, player);
  4033.  
  4034.               float minSplineDist;
  4035.               var distance = prevTrainCar.RearTrackSection.GetDistance(position, 1 f, out minSplineDist);
  4036.               var preferredAltTrack = prevTrainCar.RearTrackSection != prevTrainCar.FrontTrackSection ? prevTrainCar.RearTrackSection : null;
  4037.               newTrainCar.MoveFrontWheelsAlongTrackSpline(prevTrainCar.RearTrackSection, minSplineDist, distance, preferredAltTrack, TrainTrackSpline.TrackSelection.Default);
  4038.  
  4039.               newTrainCar.coupling.frontCoupling.TryCouple(prevTrainCar.coupling.rearCoupling, true);
  4040.               prevTrainCar = newTrainCar;
  4041.             }
  4042.           }
  4043.           if (primaryTrainCar == null) {
  4044.             primaryTrainCar = prevTrainCar;
  4045.           }
  4046.         }
  4047.         if (primaryTrainCar == null || primaryTrainCar.IsDestroyed) {
  4048.           Instance.Print(player, Instance.Lang("NotSpawnedOrRecalled", player.UserIDString, DisplayName));
  4049.           return null;
  4050.         }
  4051.         Instance.CacheVehicleEntity(primaryTrainCar, vehicle, player);
  4052.         return primaryTrainCar;
  4053.       }
  4054.  
  4055.       #endregion Spawn
  4056.  
  4057.       #region Recall
  4058.  
  4059.       public override void PreRecallVehicle(BasePlayer player, Vehicle vehicle, Vector3 position, Quaternion rotation) {
  4060.         base.PreRecallVehicle(player, vehicle, position, rotation);
  4061.         var trainCar = vehicle.Entity as TrainCar;
  4062.         if (trainCar != null) {
  4063.           trainCar.coupling.Uncouple(true);
  4064.           trainCar.coupling.Uncouple(false);
  4065.         }
  4066.       }
  4067.  
  4068.       public override void PostRecallVehicle(BasePlayer player, Vehicle vehicle, Vector3 position, Quaternion rotation) {
  4069.         base.PostRecallVehicle(player, vehicle, position, rotation);
  4070.         var trainCar = vehicle.Entity as TrainCar;
  4071.         if (trainCar != null) {
  4072.           TryMoveToTrainTrackNear(trainCar);
  4073.         }
  4074.       }
  4075.  
  4076.       #endregion Recall
  4077.  
  4078.       #region Refund
  4079.  
  4080.       protected override void CollectVehicleItems(List < Item > items, Vehicle vehicle, bool isCrash, bool isUnload) {
  4081.         // Refund primary engine fuel only
  4082.         if (CanRefundFuel(isCrash, isUnload)) {
  4083.           var trainCar = vehicle.Entity as TrainCar;
  4084.           if (trainCar != null) {
  4085.             var fuelSystem = GetFuelSystem(trainCar);
  4086.             if (fuelSystem != null) {
  4087.               var fuelContainer = fuelSystem.GetFuelContainer()?.inventory;
  4088.               if (fuelContainer != null) {
  4089.                 items.AddRange(fuelContainer.itemList);
  4090.               }
  4091.             }
  4092.           }
  4093.         }
  4094.       }
  4095.  
  4096.       #endregion Refund
  4097.  
  4098.       #region TryGetVehicleParams
  4099.  
  4100.       protected override bool TryGetPositionAndRotation(BasePlayer player, Vehicle vehicle, out string reason, out Vector3 original, out Quaternion rotation) {
  4101.         if (base.TryGetPositionAndRotation(player, vehicle, out reason, out original, out rotation)) {
  4102.           if (!TryGetTrainCarPositionAndRotation(player, vehicle, ref reason, ref original, ref rotation)) {
  4103.             return false;
  4104.           }
  4105.         }
  4106.         return true;
  4107.       }
  4108.  
  4109.       // protected override void CorrectPositionAndRotation(BasePlayer player, Vehicle vehicle, Vector3 original, Quaternion rotation, out Vector3 spawnPos, out Quaternion spawnRot)
  4110.       // {
  4111.       //     base.CorrectPositionAndRotation(player, vehicle, original, rotation, out spawnPos, out spawnRot);
  4112.       //     // No rotation on recall
  4113.       //     if (vehicle.Entity != null)
  4114.       //     {
  4115.       //         spawnRot = vehicle.Entity.transform.rotation;
  4116.       //     }
  4117.       // }
  4118.  
  4119.       #endregion TryGetVehicleParams
  4120.     }
  4121.  
  4122.     #endregion VehicleSettings
  4123.  
  4124.     protected override void LoadConfig() {
  4125.       base.LoadConfig();
  4126.       try {
  4127.         PreprocessOldConfig();
  4128.         configData = Config.ReadObject < ConfigData > ();
  4129.         if (configData == null) {
  4130.           LoadDefaultConfig();
  4131.         } else {
  4132.           UpdateConfigValues();
  4133.         }
  4134.       } catch (Exception ex) {
  4135.         PrintError($"The configuration file is corrupted. \n{ex}");
  4136.         LoadDefaultConfig();
  4137.       }
  4138.       SaveConfig();
  4139.     }
  4140.  
  4141.     protected override void LoadDefaultConfig() {
  4142.       PrintWarning("Creating a new configuration file");
  4143.       configData = new ConfigData();
  4144.       configData.version = Version;
  4145.     }
  4146.  
  4147.     protected override void SaveConfig() {
  4148.       Config.WriteObject(configData);
  4149.     }
  4150.  
  4151.     private void UpdateConfigValues() {
  4152.       if (configData.version < Version) {
  4153.         if (configData.version <=
  4154.           default (VersionNumber)) {
  4155.           string prefix, prefixColor;
  4156.           if (GetConfigValue(out prefix, "Chat Settings", "Chat Prefix") && GetConfigValue(out prefixColor, "Chat Settings", "Chat Prefix Color")) {
  4157.             configData.chat.prefix = $ "<color={prefixColor}>{prefix}</color>: ";
  4158.           }
  4159.         }
  4160.         if (configData.version <= new VersionNumber(1, 7, 3)) {
  4161.           configData.normalVehicles.sedan.MinDistanceForPlayers = 3 f;
  4162.           configData.normalVehicles.chinook.MinDistanceForPlayers = 5 f;
  4163.           configData.normalVehicles.rowboat.MinDistanceForPlayers = 2 f;
  4164.           configData.normalVehicles.rhib.MinDistanceForPlayers = 3 f;
  4165.           configData.normalVehicles.hotAirBalloon.MinDistanceForPlayers = 4 f;
  4166.           configData.normalVehicles.ridableHorse.MinDistanceForPlayers = 1 f;
  4167.           configData.normalVehicles.miniCopter.MinDistanceForPlayers = 2 f;
  4168.           configData.normalVehicles.transportHelicopter.MinDistanceForPlayers = 4 f;
  4169.           foreach(var entry in configData.modularVehicles) {
  4170.             switch (entry.Value.ChassisType) {
  4171.             case ChassisType.Small:
  4172.               entry.Value.MinDistanceForPlayers = 2 f;
  4173.               break;
  4174.  
  4175.             case ChassisType.Medium:
  4176.               entry.Value.MinDistanceForPlayers = 2.5 f;
  4177.               break;
  4178.  
  4179.             case ChassisType.Large:
  4180.               entry.Value.MinDistanceForPlayers = 3 f;
  4181.               break;
  4182.  
  4183.             default:
  4184.               continue;
  4185.             }
  4186.           }
  4187.         }
  4188.         if (configData.version >= new VersionNumber(1, 7, 17) && configData.version <= new VersionNumber(1, 7, 18)) {
  4189.           LoadData();
  4190.           foreach(var data in storedData.playerData) {
  4191.             Vehicle vehicle;
  4192.             if (data.Value.TryGetValue("SubmarineDouble", out vehicle)) {
  4193.               data.Value.Remove("SubmarineDouble");
  4194.               data.Value.Add(nameof(NormalVehicleType.SubmarineDuo), vehicle);
  4195.             }
  4196.           }
  4197.           SaveData();
  4198.         }
  4199.         configData.version = Version;
  4200.       }
  4201.     }
  4202.  
  4203.     private bool GetConfigValue < T > (out T value, params string[] path) {
  4204.       var configValue = Config.Get(path);
  4205.       if (configValue != null) {
  4206.         if (configValue is T) {
  4207.           value = (T) configValue;
  4208.           return true;
  4209.         }
  4210.         try {
  4211.           value = Config.ConvertValue < T > (configValue);
  4212.           return true;
  4213.         } catch (Exception ex) {
  4214.           PrintError($"GetConfigValue ERROR: path: {string.Join("\\
  4215.             ", path)}\n{ex}");
  4216.         }
  4217.       }
  4218.  
  4219.       value =
  4220.         default (T);
  4221.       return false;
  4222.     }
  4223.  
  4224.     private void SetConfigValue(params object[] pathAndTrailingValue) {
  4225.       Config.Set(pathAndTrailingValue);
  4226.     }
  4227.  
  4228.     #region Preprocess Old Config
  4229.  
  4230.     private void PreprocessOldConfig() {
  4231.       var config = Config.ReadObject < JObject > ();
  4232.       if (config == null) {
  4233.         return;
  4234.       }
  4235.       //Interface.Oxide.DataFileSystem.WriteObject(Name + "_old", jObject);
  4236.       VersionNumber oldVersion;
  4237.       if (GetConfigVersionPre(config, out oldVersion)) {
  4238.         if (oldVersion < Version) {
  4239.           if (oldVersion < new VersionNumber(1, 7, 35)) {
  4240.             try {
  4241.               if (config["Train Vehicle Settings"] == null) {
  4242.                 config["Train Vehicle Settings"] = JObject.FromObject(new ConfigData().trainVehicles);
  4243.               }
  4244.               var workCartAboveGround = GetConfigValue(config, "Normal Vehicle Settings", "Work Cart Above Ground Vehicle");
  4245.               if (workCartAboveGround != null) {
  4246.                 var settings = workCartAboveGround.ToObject < TrainVehicleSettings > ();
  4247.                 settings.TrainComponents = new List < TrainComponent > {
  4248.                   new TrainComponent {
  4249.                     type = TrainComponentType.Engine
  4250.                   }
  4251.                 };
  4252.                 config["Train Vehicle Settings"]["WorkCartAboveGround"] = JObject.FromObject(settings);;
  4253.               }
  4254.               var coveredWorkCart = GetConfigValue(config, "Normal Vehicle Settings", "Covered Work Cart Vehicle");
  4255.               if (coveredWorkCart != null) {
  4256.                 var settings = coveredWorkCart.ToObject < TrainVehicleSettings > ();
  4257.                 settings.TrainComponents = new List < TrainComponent > {
  4258.                   new TrainComponent {
  4259.                     type = TrainComponentType.CoveredEngine
  4260.                   }
  4261.                 };
  4262.                 config["Train Vehicle Settings"]["WorkCartCovered"] = JObject.FromObject(settings);
  4263.               }
  4264.             } catch {
  4265.               // ignored
  4266.             }
  4267.           }
  4268.           Config.WriteObject(config);
  4269.           // Interface.Oxide.DataFileSystem.WriteObject(Name + "_new", jObject);
  4270.         }
  4271.       }
  4272.     }
  4273.  
  4274.     private JObject GetConfigValue(JObject config, params string[] path) {
  4275.       if (path.Length < 1) {
  4276.         throw new ArgumentException("path is empty");
  4277.       }
  4278.  
  4279.       try {
  4280.         JToken jToken;
  4281.         if (!config.TryGetValue(path[0], out jToken)) {
  4282.           return null;
  4283.         }
  4284.  
  4285.         for (var i = 1; i < path.Length; i++) {
  4286.           var jObject = jToken as JObject;
  4287.           if (jObject == null || !jObject.TryGetValue(path[i], out jToken)) {
  4288.             return null;
  4289.           }
  4290.         }
  4291.         return jToken as JObject;
  4292.       } catch (Exception ex) {
  4293.         PrintError($"GetConfigValue ERROR: path: {string.Join("\\
  4294.           ", path)}\n{ex}");
  4295.       }
  4296.       return null;
  4297.     }
  4298.  
  4299.     private bool GetConfigValuePre < T > (JObject config, out T value, params string[] path) {
  4300.       if (path.Length < 1) {
  4301.         throw new ArgumentException("path is empty");
  4302.       }
  4303.  
  4304.       try {
  4305.         JToken jToken;
  4306.         if (!config.TryGetValue(path[0], out jToken)) {
  4307.           value =
  4308.             default (T);
  4309.           return false;
  4310.         }
  4311.  
  4312.         for (var i = 1; i < path.Length; i++) {
  4313.           var jObject = jToken.ToObject < JObject > ();
  4314.           if (jObject == null || !jObject.TryGetValue(path[i], out jToken)) {
  4315.             value =
  4316.               default (T);
  4317.             return false;
  4318.           }
  4319.         }
  4320.         value = jToken.ToObject < T > ();
  4321.         return true;
  4322.       } catch (Exception ex) {
  4323.         PrintError($"GetConfigValuePre ERROR: path: {string.Join("\\
  4324.           ", path)}\n{ex}");
  4325.       }
  4326.       value =
  4327.         default (T);
  4328.       return false;
  4329.     }
  4330.  
  4331.     private void SetConfigValuePre(JObject config, object value, params string[] path) {
  4332.       if (path.Length < 1) {
  4333.         throw new ArgumentException("path is empty");
  4334.       }
  4335.  
  4336.       try {
  4337.         JToken jToken;
  4338.         if (!config.TryGetValue(path[0], out jToken)) {
  4339.           if (path.Length == 1) {
  4340.             jToken = JToken.FromObject(value);
  4341.             config.Add(path[0], jToken);
  4342.             return;
  4343.           }
  4344.           jToken = new JObject();
  4345.           config.Add(path[0], jToken);
  4346.         }
  4347.  
  4348.         for (var i = 1; i < path.Length - 1; i++) {
  4349.           var jObject = jToken as JObject;
  4350.           if (jObject == null || !jObject.TryGetValue(path[i], out jToken)) {
  4351.             jToken = new JObject();
  4352.             jObject?.Add(path[i], jToken);
  4353.           }
  4354.         }
  4355.         var targetToken = jToken as JObject;
  4356.         if (targetToken != null) {
  4357.           targetToken[path[path.Length - 1]] = JToken.FromObject(value);
  4358.         }
  4359.         // (jToken as JObject)?.TryAdd(path[path.Length - 1], JToken.FromObject(value));
  4360.       } catch (Exception ex) {
  4361.         PrintError($"SetConfigValuePre ERROR: value: {value} path: {string.Join("\\
  4362.           ", path)}\n{ex}");
  4363.       }
  4364.     }
  4365.  
  4366.     private bool GetConfigVersionPre(JObject config, out VersionNumber version) {
  4367.       try {
  4368.         JToken jToken;
  4369.         if (config.TryGetValue("Version", out jToken)) {
  4370.           version = jToken.ToObject < VersionNumber > ();
  4371.           return true;
  4372.         }
  4373.       } catch {
  4374.         // ignored
  4375.       }
  4376.       version =
  4377.         default (VersionNumber);
  4378.       return false;
  4379.     }
  4380.  
  4381.     #endregion Preprocess Old Config
  4382.  
  4383.     #endregion ConfigurationFile
  4384.  
  4385.     #region DataFile
  4386.  
  4387.     public StoredData storedData {
  4388.       get;
  4389.       private set;
  4390.     }
  4391.  
  4392.     public class StoredData {
  4393.       public readonly Dictionary < ulong, Dictionary < string, Vehicle >> playerData = new Dictionary < ulong, Dictionary < string, Vehicle >> ();
  4394.  
  4395.       public IEnumerable < BaseEntity > ActiveVehicles(ulong playerId) {
  4396.         Dictionary < string, Vehicle > vehicles;
  4397.         if (!playerData.TryGetValue(playerId, out vehicles)) {
  4398.           yield
  4399.           break;
  4400.         }
  4401.  
  4402.         foreach(var vehicle in vehicles.Values) {
  4403.           if (vehicle.Entity != null && !vehicle.Entity.IsDestroyed) {
  4404.             yield
  4405.             return vehicle.Entity;
  4406.           }
  4407.         }
  4408.       }
  4409.  
  4410.       public Dictionary < string, Vehicle > GetPlayerVehicles(ulong playerId, bool readOnly = true) {
  4411.         Dictionary < string, Vehicle > vehicles;
  4412.         if (!playerData.TryGetValue(playerId, out vehicles)) {
  4413.           if (!readOnly) {
  4414.             vehicles = new Dictionary < string, Vehicle > ();
  4415.             playerData.Add(playerId, vehicles);
  4416.             return vehicles;
  4417.           }
  4418.           return null;
  4419.         }
  4420.         return vehicles;
  4421.       }
  4422.  
  4423.       public bool IsVehiclePurchased(ulong playerId, string vehicleType, out Vehicle vehicle) {
  4424.         vehicle = GetVehicleLicense(playerId, vehicleType);
  4425.         if (vehicle == null) {
  4426.           return false;
  4427.         }
  4428.         return true;
  4429.       }
  4430.  
  4431.       public Vehicle GetVehicleLicense(ulong playerId, string vehicleType) {
  4432.         Dictionary < string, Vehicle > vehicles;
  4433.         if (!playerData.TryGetValue(playerId, out vehicles)) {
  4434.           return null;
  4435.         }
  4436.         Vehicle vehicle;
  4437.         if (!vehicles.TryGetValue(vehicleType, out vehicle)) {
  4438.           return null;
  4439.         }
  4440.         return vehicle;
  4441.       }
  4442.  
  4443.       public bool HasVehicleLicense(ulong playerId, string vehicleType) {
  4444.         Dictionary < string, Vehicle > vehicles;
  4445.         if (!playerData.TryGetValue(playerId, out vehicles)) {
  4446.           return false;
  4447.         }
  4448.         return vehicles.ContainsKey(vehicleType);
  4449.       }
  4450.  
  4451.       public bool AddVehicleLicense(ulong playerId, string vehicleType) {
  4452.         Dictionary < string, Vehicle > vehicles;
  4453.         if (!playerData.TryGetValue(playerId, out vehicles)) {
  4454.           vehicles = new Dictionary < string, Vehicle > ();
  4455.           playerData.Add(playerId, vehicles);
  4456.         }
  4457.         if (vehicles.ContainsKey(vehicleType)) {
  4458.           return false;
  4459.         }
  4460.         vehicles.Add(vehicleType, Vehicle.Create(playerId, vehicleType));
  4461.         Instance.SaveData();
  4462.         return true;
  4463.       }
  4464.  
  4465.       public bool RemoveVehicleLicense(ulong playerId, string vehicleType) {
  4466.         Dictionary < string, Vehicle > vehicles;
  4467.         if (!playerData.TryGetValue(playerId, out vehicles)) {
  4468.           return false;
  4469.         }
  4470.  
  4471.         if (!vehicles.Remove(vehicleType)) {
  4472.           return false;
  4473.         }
  4474.         Instance.SaveData();
  4475.         return true;
  4476.       }
  4477.  
  4478.       public List < string > GetVehicleLicenseNames(ulong playerId) {
  4479.         Dictionary < string, Vehicle > vehicles;
  4480.         if (!playerData.TryGetValue(playerId, out vehicles)) {
  4481.           return new List < string > ();
  4482.         }
  4483.         return vehicles.Keys.ToList();
  4484.       }
  4485.  
  4486.       public void PurchaseAllVehicles(ulong playerId) {
  4487.         var changed = false;
  4488.         Dictionary < string, Vehicle > vehicles;
  4489.         if (!playerData.TryGetValue(playerId, out vehicles)) {
  4490.           vehicles = new Dictionary < string, Vehicle > ();
  4491.           playerData.Add(playerId, vehicles);
  4492.         }
  4493.         foreach(var vehicleType in Instance.allVehicleSettings.Keys) {
  4494.           if (!vehicles.ContainsKey(vehicleType)) {
  4495.             vehicles.Add(vehicleType, Vehicle.Create(playerId, vehicleType));
  4496.             changed = true;
  4497.           }
  4498.         }
  4499.  
  4500.         if (changed) {
  4501.           Instance.SaveData();
  4502.         }
  4503.       }
  4504.  
  4505.       public void AddLicenseForAllPlayers(string vehicleType) {
  4506.         foreach(var entry in playerData) {
  4507.           if (!entry.Value.ContainsKey(vehicleType)) {
  4508.             entry.Value.Add(vehicleType, Vehicle.Create(entry.Key, vehicleType));
  4509.           }
  4510.         }
  4511.       }
  4512.  
  4513.       public void RemoveLicenseForAllPlayers(string vehicleType) {
  4514.         foreach(var entry in playerData) {
  4515.           entry.Value.Remove(vehicleType);
  4516.         }
  4517.       }
  4518.  
  4519.       public void ResetPlayerData() {
  4520.         foreach(var vehicleEntries in playerData) {
  4521.           foreach(var vehicleEntry in vehicleEntries.Value) {
  4522.             vehicleEntry.Value.Reset();
  4523.           }
  4524.         }
  4525.       }
  4526.     }
  4527.  
  4528.     [JsonObject(MemberSerialization.OptIn)]
  4529.     public class Vehicle {
  4530.       [JsonProperty("entityID")]
  4531.       public ulong EntityId {
  4532.         get;
  4533.         set;
  4534.       }
  4535.  
  4536.       [JsonProperty("lastDeath")]
  4537.       public double LastDeath {
  4538.         get;
  4539.         set;
  4540.       }
  4541.  
  4542.       public ulong PlayerId {
  4543.         get;
  4544.         set;
  4545.       }
  4546.       public BaseEntity Entity {
  4547.         get;
  4548.         set;
  4549.       }
  4550.       public string VehicleType {
  4551.         get;
  4552.         set;
  4553.       }
  4554.       public double LastRecall {
  4555.         get;
  4556.         set;
  4557.       }
  4558.       public double LastDismount {
  4559.         get;
  4560.         set;
  4561.       }
  4562.  
  4563.       public void OnDismount() {
  4564.         LastDismount = TimeEx.currentTimestamp;
  4565.       }
  4566.  
  4567.       public void OnRecall() {
  4568.         LastRecall = TimeEx.currentTimestamp;
  4569.       }
  4570.  
  4571.       public void OnDeath() {
  4572.         Entity = null;
  4573.         EntityId = 0;
  4574.         LastDeath = TimeEx.currentTimestamp;
  4575.       }
  4576.  
  4577.       public void Reset() {
  4578.         EntityId = 0;
  4579.         LastDeath = 0;
  4580.       }
  4581.  
  4582.       public static Vehicle Create(ulong playerId, string vehicleType) {
  4583.         var vehicle = new Vehicle();
  4584.         vehicle.VehicleType = vehicleType;
  4585.         vehicle.PlayerId = playerId;
  4586.         return vehicle;
  4587.       }
  4588.     }
  4589.  
  4590.     private void LoadData() {
  4591.       try {
  4592.         storedData = Interface.Oxide.DataFileSystem.ReadObject < StoredData > (Name);
  4593.       } catch {
  4594.         storedData = null;
  4595.       }
  4596.       if (storedData == null) {
  4597.         ClearData();
  4598.       }
  4599.     }
  4600.  
  4601.     private void ClearData() {
  4602.       storedData = new StoredData();
  4603.       SaveData();
  4604.     }
  4605.  
  4606.     private void SaveData() {
  4607.       Interface.Oxide.DataFileSystem.WriteObject(Name, storedData);
  4608.     }
  4609.  
  4610.     private void OnNewSave() {
  4611.       if (configData.global.clearVehicleOnWipe) {
  4612.         ClearData();
  4613.       } else {
  4614.         storedData.ResetPlayerData();
  4615.         SaveData();
  4616.       }
  4617.     }
  4618.  
  4619.     #endregion DataFile
  4620.  
  4621.     #region LanguageFile
  4622.  
  4623.     private void Print(BasePlayer player, string message) {
  4624.       Player.Message(player, message, configData.chat.prefix, configData.chat.steamIDIcon);
  4625.     }
  4626.  
  4627.     private void Print(ConsoleSystem.Arg arg, string message) {
  4628.       var player = arg.Player();
  4629.       if (player == null) {
  4630.         Puts(message);
  4631.       } else {
  4632.         PrintToConsole(player, message);
  4633.       }
  4634.     }
  4635.  
  4636.     private string Lang(string key, string id = null, params object[] args) {
  4637.       try {
  4638.         return string.Format(lang.GetMessage(key, this, id), args);
  4639.       } catch (Exception) {
  4640.         PrintError($"Error in the language formatting of '{key}'. (userid: {id}. lang: {lang.GetLanguage(id)}. args: {string.Join(", ", args)})");
  4641.         throw;
  4642.       }
  4643.     }
  4644.  
  4645.     protected override void LoadDefaultMessages() {
  4646.       lang.RegisterMessages(new Dictionary < string, string > {
  4647.         ["Help"] = "These are the available commands:",
  4648.         ["HelpLicence1"] = "<color=#4DFF4D>/{0}</color> -- To buy a vehicle",
  4649.         ["HelpLicence2"] = "<color=#4DFF4D>/{0}</color> -- To spawn a vehicle",
  4650.         ["HelpLicence3"] = "<color=#4DFF4D>/{0}</color> -- To recall a vehicle",
  4651.         ["HelpLicence4"] = "<color=#4DFF4D>/{0}</color> -- To kill a vehicle",
  4652.         ["HelpLicence5"] = "<color=#4DFF4D>/{0}</color> -- To buy, spawn or recall a <color=#009EFF>{1}</color>",
  4653.  
  4654.         ["PriceFormat"] = "<color=#FF1919>{0}</color> x{1}",
  4655.         ["HelpBuy"] = "<color=#4DFF4D>/{0} {1}</color> -- To buy a <color=#009EFF>{2}</color>",
  4656.         ["HelpBuyPrice"] = "<color=#4DFF4D>/{0} {1}</color> -- To buy a <color=#009EFF>{2}</color>. Price: {3}",
  4657.         ["HelpSpawn"] = "<color=#4DFF4D>/{0} {1}</color> -- To spawn a <color=#009EFF>{2}</color>",
  4658.         ["HelpSpawnPrice"] = "<color=#4DFF4D>/{0} {1}</color> -- To spawn a <color=#009EFF>{2}</color>. Price: {3}",
  4659.         ["HelpRecall"] = "<color=#4DFF4D>/{0} {1}</color> -- To recall a <color=#009EFF>{2}</color>",
  4660.         ["HelpRecallPrice"] = "<color=#4DFF4D>/{0} {1}</color> -- To recall a <color=#009EFF>{2}</color>. Price: {3}",
  4661.         ["HelpKill"] = "<color=#4DFF4D>/{0} {1}</color> -- To kill a <color=#009EFF>{2}</color>",
  4662.         ["HelpKillCustom"] = "<color=#4DFF4D>/{0} {1}</color> or <color=#4DFF4D>/{2}</color>  -- To kill a <color=#009EFF>{3}</color>",
  4663.  
  4664.         ["NotAllowed"] = "You do not have permission to use this command.",
  4665.         ["RaidBlocked"] = "<color=#FF1919>You may not do that while raid blocked</color>.",
  4666.         ["CombatBlocked"] = "<color=#FF1919>You may not do that while combat blocked</color>.",
  4667.         ["OptionNotFound"] = "This <color=#009EFF>{0}</color> option doesn't exist.",
  4668.         ["VehiclePurchased"] = "You have purchased a <color=#009EFF>{0}</color>, type <color=#4DFF4D>/{1}</color> for more information.",
  4669.         ["VehicleAlreadyPurchased"] = "You have already purchased <color=#009EFF>{0}</color>.",
  4670.         ["VehicleCannotBeBought"] = "<color=#009EFF>{0}</color> is unpurchasable",
  4671.         ["VehicleNotOut"] = "<color=#009EFF>{0}</color> is not out, type <color=#4DFF4D>/{1}</color> for more information.",
  4672.         ["AlreadyVehicleOut"] = "You already have a <color=#009EFF>{0}</color> outside, type <color=#4DFF4D>/{1}</color> for more information.",
  4673.         ["VehicleNotYetPurchased"] = "You have not yet purchased a <color=#009EFF>{0}</color>, type <color=#4DFF4D>/{1}</color> for more information.",
  4674.         ["VehicleSpawned"] = "You spawned your <color=#009EFF>{0}</color>.",
  4675.         ["VehicleRecalled"] = "You recalled your <color=#009EFF>{0}</color>.",
  4676.         ["VehicleKilled"] = "You killed your <color=#009EFF>{0}</color>.",
  4677.         ["VehicleOnSpawnCooldown"] = "You must wait <color=#FF1919>{0}</color> seconds before you can spawn your <color=#009EFF>{1}</color>.",
  4678.         ["VehicleOnRecallCooldown"] = "You must wait <color=#FF1919>{0}</color> seconds before you can recall your <color=#009EFF>{1}</color>.",
  4679.         ["VehicleOnSpawnCooldownPay"] = "You must wait <color=#FF1919>{0}</color> seconds before you can spawn your <color=#009EFF>{1}</color>. You can bypass this cooldown by using the <color=#FF1919>/{2}</color> command to pay <color=#009EFF>{3}</color>",
  4680.         ["VehicleOnRecallCooldownPay"] = "You must wait <color=#FF1919>{0}</color> seconds before you can recall your <color=#009EFF>{1}</color>. You can bypass this cooldown by using the <color=#FF1919>/{2}</color> command to pay <color=#009EFF>{3}</color>",
  4681.         ["NotLookingAtWater"] = "You must be looking at water to spawn or recall a <color=#009EFF>{0}</color>.",
  4682.         ["BuildingBlocked"] = "You can't spawn a <color=#009EFF>{0}</color> if you don't have the building privileges.",
  4683.         ["RefundedVehicleItems"] = "Your <color=#009EFF>{0}</color> vehicle items was refunded to your inventory.",
  4684.         ["PlayerMountedOnVehicle"] = "It cannot be recalled or killed when players mounted on your <color=#009EFF>{0}</color>.",
  4685.         ["PlayerInSafeZone"] = "You cannot spawn or recall your <color=#009EFF>{0}</color> in the safe zone.",
  4686.         ["VehicleInventoryDropped"] = "Your <color=#009EFF>{0}</color> vehicle inventory cannot be recalled, it have dropped to the ground.",
  4687.         ["NoResourcesToPurchaseVehicle"] = "You don't have enough resources to buy a <color=#009EFF>{0}</color>. You are missing: \n{1}",
  4688.         ["NoResourcesToSpawnVehicle"] = "You don't have enough resources to spawn a <color=#009EFF>{0}</color>. You are missing: \n{1}",
  4689.         ["NoResourcesToSpawnVehicleBypass"] = "You don't have enough resources to bypass the cooldown to spawn a <color=#009EFF>{0}</color>. You are missing: \n{1}",
  4690.         ["NoResourcesToRecallVehicle"] = "You don't have enough resources to recall a <color=#009EFF>{0}</color>. You are missing: \n{1}",
  4691.         ["NoResourcesToRecallVehicleBypass"] = "You don't have enough resources to bypass the cooldown to recall a <color=#009EFF>{0}</color>. You are missing: \n{1}",
  4692.         ["MountedOrParented"] = "You cannot spawn or recall a <color=#009EFF>{0}</color> when mounted or parented.",
  4693.         ["RecallTooFar"] = "You must be within <color=#FF1919>{0}</color> meters of <color=#009EFF>{1}</color> to recall.",
  4694.         ["KillTooFar"] = "You must be within <color=#FF1919>{0}</color> meters of <color=#009EFF>{1}</color> to kill.",
  4695.         ["PlayersOnNearby"] = "You cannot spawn or recall a <color=#009EFF>{0}</color> when there are players near the position you are looking at.",
  4696.         ["RecallWasBlocked"] = "An external plugin blocked you from recalling a <color=#009EFF>{0}</color>.",
  4697.         ["SpawnWasBlocked"] = "An external plugin blocked you from spawning a <color=#009EFF>{0}</color>.",
  4698.         ["VehiclesLimit"] = "You can have up to <color=#009EFF>{0}</color> vehicles at a time.",
  4699.         ["TooFarTrainTrack"] = "You are too far from the train track.",
  4700.         ["TooCloseTrainBarricadeOrWorkCart"] = "You are too close to the train barricade or work cart.",
  4701.         ["NotSpawnedOrRecalled"] = "For some reason, your <color=#009EFF>{0}</color> vehicle was not spawned/recalled",
  4702.  
  4703.         ["CantUse"] = "Sorry! This {0} belongs to {1}.You cannot use it."
  4704.       }, this);
  4705.       lang.RegisterMessages(new Dictionary < string, string > {
  4706.         ["Help"] = "可用命令列表:",
  4707.         ["HelpLicence1"] = "<color=#4DFF4D>/{0}</color> -- 购买一辆载具",
  4708.         ["HelpLicence2"] = "<color=#4DFF4D>/{0}</color> -- 生成一辆载具",
  4709.         ["HelpLicence3"] = "<color=#4DFF4D>/{0}</color> -- 召回一辆载具",
  4710.         ["HelpLicence4"] = "<color=#4DFF4D>/{0}</color> -- 摧毁一辆载具",
  4711.         ["HelpLicence5"] = "<color=#4DFF4D>/{0}</color> -- 购买,生成,召回一辆 <color=#009EFF>{1}</color>",
  4712.  
  4713.         ["PriceFormat"] = "<color=#FF1919>{0}</color> x{1}",
  4714.         ["HelpBuy"] = "<color=#4DFF4D>/{0} {1}</color> -- 购买一辆 <color=#009EFF>{2}</color>",
  4715.         ["HelpBuyPrice"] = "<color=#4DFF4D>/{0} {1}</color> -- 购买一辆 <color=#009EFF>{2}</color>,价格: {3}",
  4716.         ["HelpSpawn"] = "<color=#4DFF4D>/{0} {1}</color> -- 生成一辆 <color=#009EFF>{2}</color>",
  4717.         ["HelpSpawnPrice"] = "<color=#4DFF4D>/{0} {1}</color> -- 生成一辆 <color=#009EFF>{2}</color>,价格: {3}",
  4718.         ["HelpRecall"] = "<color=#4DFF4D>/{0} {1}</color> -- 召回一辆 <color=#009EFF>{2}</color>",
  4719.         ["HelpRecallPrice"] = "<color=#4DFF4D>/{0} {1}</color> -- 召回一辆 <color=#009EFF>{2}</color>,价格: {3}",
  4720.         ["HelpKill"] = "<color=#4DFF4D>/{0} {1}</color> -- 摧毁一辆 <color=#009EFF>{2}</color>",
  4721.         ["HelpKillCustom"] = "<color=#4DFF4D>/{0} {1}</color> 或者 <color=#4DFF4D>/{2}</color>  -- 摧毁一辆 <color=#009EFF>{3}</color>",
  4722.  
  4723.         ["NotAllowed"] = "您没有权限使用该命令",
  4724.         ["RaidBlocked"] = "<color=#FF1919>您被突袭阻止了,不能使用该命令</color>",
  4725.         ["CombatBlocked"] = "<color=#FF1919>您被战斗阻止了,不能使用该命令</color>",
  4726.         ["OptionNotFound"] = "选项 <color=#009EFF>{0}</color> 不存在",
  4727.         ["VehiclePurchased"] = "您购买了 <color=#009EFF>{0}</color>, 输入 <color=#4DFF4D>/{1}</color> 了解更多信息",
  4728.         ["VehicleAlreadyPurchased"] = "您已经购买了 <color=#009EFF>{0}</color>",
  4729.         ["VehicleCannotBeBought"] = "<color=#009EFF>{0}</color> 是不可购买的",
  4730.         ["VehicleNotOut"] = "您还没有生成您的 <color=#009EFF>{0}</color>, 输入 <color=#4DFF4D>/{1}</color> 了解更多信息",
  4731.         ["AlreadyVehicleOut"] = "您已经生成了您的 <color=#009EFF>{0}</color>, 输入 <color=#4DFF4D>/{1}</color> 了解更多信息",
  4732.         ["VehicleNotYetPurchased"] = "您还没有购买 <color=#009EFF>{0}</color>, 输入 <color=#4DFF4D>/{1}</color> 了解更多信息",
  4733.         ["VehicleSpawned"] = "您生成了您的 <color=#009EFF>{0}</color>",
  4734.         ["VehicleRecalled"] = "您召回了您的 <color=#009EFF>{0}</color>",
  4735.         ["VehicleKilled"] = "您摧毁了您的 <color=#009EFF>{0}</color>",
  4736.         ["VehicleOnSpawnCooldown"] = "您必须等待 <color=#FF1919>{0}</color> 秒,才能生成您的 <color=#009EFF>{1}</color>",
  4737.         ["VehicleOnRecallCooldown"] = "您必须等待 <color=#FF1919>{0}</color> 秒,才能召回您的 <color=#009EFF>{1}</color>",
  4738.         ["VehicleOnSpawnCooldownPay"] = "您必须等待 <color=#FF1919>{0}</color> 秒,才能生成您的 <color=#009EFF>{1}</color>。你可以使用 <color=#FF1919>/{2}</color> 命令支付 <color=#009EFF>{3}</color> 来绕过这个冷却时间",
  4739.         ["VehicleOnRecallCooldownPay"] = "您必须等待 <color=#FF1919>{0}</color> 秒,才能召回您的 <color=#009EFF>{1}</color>。你可以使用 <color=#FF1919>/{2}</color> 命令支付 <color=#009EFF>{3}</color> 来绕过这个冷却时间",
  4740.         ["NotLookingAtWater"] = "您必须看着水面才能生成您的 <color=#009EFF>{0}</color>",
  4741.         ["BuildingBlocked"] = "您没有领地柜权限,无法生成您的 <color=#009EFF>{0}</color>",
  4742.         ["RefundedVehicleItems"] = "您的 <color=#009EFF>{0}</color> 载具物品已经归还回您的库存",
  4743.         ["PlayerMountedOnVehicle"] = "您的 <color=#009EFF>{0}</color> 上坐着玩家,无法被召回或摧毁",
  4744.         ["PlayerInSafeZone"] = "您不能在安全区域内生成或召回您的 <color=#009EFF>{0}</color>",
  4745.         ["VehicleInventoryDropped"] = "您的 <color=#009EFF>{0}</color> 载具物品不能召回,它已经掉落在地上了",
  4746.         ["NoResourcesToPurchaseVehicle"] = "您没有足够的资源购买 <color=#009EFF>{0}</color>,还需要: \n{1}",
  4747.         ["NoResourcesToSpawnVehicle"] = "您没有足够的资源生成 <color=#009EFF>{0}</color>,还需要: \n{1}",
  4748.         ["NoResourcesToSpawnVehicleBypass"] = "您没有足够的资源绕过冷却时间来生成 <color=#009EFF>{0}</color>,还需要: \n{1}",
  4749.         ["NoResourcesToRecallVehicle"] = "您没有足够的资源召回 <color=#009EFF>{0}</color>,还需要: \n{1}",
  4750.         ["NoResourcesToRecallVehicleBypass"] = "您没有足够的资源绕过冷却时间来召回 <color=#009EFF>{0}</color>,还需要: \n{1}",
  4751.         ["MountedOrParented"] = "当您坐着或者在附着在实体上时无法生成或召回 <color=#009EFF>{0}</color>",
  4752.         ["RecallTooFar"] = "您必须在 <color=#FF1919>{0}</color> 米内才能召回您的 <color=#009EFF>{1}</color>",
  4753.         ["KillTooFar"] = "您必须在 <color=#FF1919>{0}</color> 米内才能摧毁您的 <color=#009EFF>{1}</color>",
  4754.         ["PlayersOnNearby"] = "您正在看着的位置附近有玩家时无法生成或召回 <color=#009EFF>{0}</color>",
  4755.         ["RecallWasBlocked"] = "有其他插件阻止您召回 <color=#009EFF>{0}</color>.",
  4756.         ["SpawnWasBlocked"] = "有其他插件阻止您生成 <color=#009EFF>{0}</color>.",
  4757.         ["VehiclesLimit"] = "您在同一时间内最多可以拥有 <color=#009EFF>{0}</color> 辆载具",
  4758.         ["TooFarTrainTrack"] = "您距离铁路轨道太远了",
  4759.         ["TooCloseTrainBarricadeOrWorkCart"] = "您距离铁轨障碍物或其它火车太近了",
  4760.         ["NotSpawnedOrRecalled"] = "由于某些原因,您的 <color=#009EFF>{0}</color> 载具无法生成或召回",
  4761.  
  4762.         ["CantUse"] = "您不能使用它,这个 {0} 属于 {1}"
  4763.       }, this, "zh-CN");
  4764.       lang.RegisterMessages(new Dictionary < string, string > {
  4765.         ["Help"] = "Список доступных команд:",
  4766.         ["HelpLicence1"] = "<color=#4DFF4D>/{0}</color> -- Купить транспорт",
  4767.         ["HelpLicence2"] = "<color=#4DFF4D>/{0}</color> -- Создать транспорт",
  4768.         ["HelpLicence3"] = "<color=#4DFF4D>/{0}</color> -- Вызвать транспорт",
  4769.         ["HelpLicence4"] = "<color=#4DFF4D>/{0}</color> -- Уничтожить транспорт",
  4770.         ["HelpLicence5"] = "<color=#4DFF4D>/{0}</color> -- Купить, создать, или вызвать <color=#009EFF>{1}</color>",
  4771.  
  4772.         ["PriceFormat"] = "<color=#FF1919>{0}</color> x{1}",
  4773.         ["HelpBuy"] = "<color=#4DFF4D>/{0} {1}</color> -- Купить <color=#009EFF>{2}</color>.",
  4774.         ["HelpBuyPrice"] = "<color=#4DFF4D>/{0} {1}</color> -- Купить <color=#009EFF>{2}</color>. Цена: {3}",
  4775.         ["HelpSpawn"] = "<color=#4DFF4D>/{0} {1}</color> -- Создать <color=#009EFF>{2}</color>",
  4776.         ["HelpSpawnPrice"] = "<color=#4DFF4D>/{0} {1}</color> -- Вызывать <color=#009EFF>{2}</color>. Цена: {3}",
  4777.         ["HelpRecall"] = "<color=#4DFF4D>/{0} {1}</color> -- Вызвать <color=#009EFF>{2}</color>",
  4778.         ["HelpRecallPrice"] = "<color=#4DFF4D>/{0} {1}</color> -- Вызвать <color=#009EFF>{2}</color>. Цена: {3}",
  4779.         ["HelpKill"] = "<color=#4DFF4D>/{0} {1}</color> -- Уничтожить <color=#009EFF>{2}</color>",
  4780.         ["HelpKillCustom"] = "<color=#4DFF4D>/{0} {1}</color> или же <color=#4DFF4D>/{2}</color>  -- Уничтожить <color=#009EFF>{3}</color>",
  4781.  
  4782.         ["NotAllowed"] = "У вас нет разрешения для использования данной команды.",
  4783.         ["RaidBlocked"] = "<color=#FF1919>Вы не можете это сделать из-за блокировки (рейд)</color>.",
  4784.         ["CombatBlocked"] = "<color=#FF1919>Вы не можете это сделать из-за блокировки (бой)</color>.",
  4785.         ["OptionNotFound"] = "Опция <color=#009EFF>{0}</color> не существует.",
  4786.         ["VehiclePurchased"] = "Вы приобрели <color=#009EFF>{0}</color>, напишите <color=#4DFF4D>/{1}</color> для получения дополнительной информации.",
  4787.         ["VehicleAlreadyPurchased"] = "Вы уже приобрели <color=#009EFF>{0}</color>.",
  4788.         ["VehicleCannotBeBought"] = "<color=#009EFF>{0}</color> приобрести невозможно",
  4789.         ["VehicleNotOut"] = "<color=#009EFF>{0}</color> отсутствует. Напишите <color=#4DFF4D>/{1}</color> для получения дополнительной информации.",
  4790.         ["AlreadyVehicleOut"] = "У вас уже есть <color=#009EFF>{0}</color>, напишите <color=#4DFF4D>/{1}</color>  для получения дополнительной информации.",
  4791.         ["VehicleNotYetPurchased"] = "Вы ещё не приобрели <color=#009EFF>{0}</color>. Напишите <color=#4DFF4D>/{1}</color> для получения дополнительной информации.",
  4792.         ["VehicleSpawned"] = "Вы создали ваш <color=#009EFF>{0}</color>.",
  4793.         ["VehicleRecalled"] = "Вы вызвали ваш <color=#009EFF>{0}</color>.",
  4794.         ["VehicleKilled"] = "Вы уничтожили ваш <color=#009EFF>{0}</color>.",
  4795.         ["VehicleOnSpawnCooldown"] = "Вам необходимо подождать <color=#FF1919>{0}</color> секунд прежде, чем создать свой <color=#009EFF>{1}</color>.",
  4796.         ["VehicleOnRecallCooldown"] = "Вам необходимо подождать <color=#FF1919>{0}</color> секунд прежде, чем вызвать свой <color=#009EFF>{1}</color>.",
  4797.         ["VehicleOnSpawnCooldownPay"] = "Вам необходимо подождать <color=#FF1919>{0}</color> секунд прежде, чем создать свой <color=#009EFF>{1}</color>. Вы можете обойти это время восстановления, используя команду <color=#FF1919>/{2}</color>, чтобы заплатить <color=#009EFF>{3}</color>",
  4798.         ["VehicleOnRecallCooldownPay"] = "Вам необходимо подождать <color=#FF1919>{0}</color> секунд прежде, чем вызвать свой <color=#009EFF>{1}</color>. Вы можете обойти это время восстановления, используя команду <color=#FF1919>/{2}</color>, чтобы заплатить <color=#009EFF>{3}</color>",
  4799.         ["NotLookingAtWater"] = "Вы должны смотреть на воду, чтобы создать или вызвать <color=#009EFF>{0}</color>.",
  4800.         ["BuildingBlocked"] = "Вы не можете создать <color=#009EFF>{0}</color> если отсутствует право строительства.",
  4801.         ["RefundedVehicleItems"] = "Запчасти от вашего <color=#009EFF>{0}</color> были возвращены в ваш инвентарь.",
  4802.         ["PlayerMountedOnVehicle"] = "Нельзя вызвать, когда игрок находится в вашем <color=#009EFF>{0}</color>.",
  4803.         ["PlayerInSafeZone"] = "Вы не можете создать, или вызвать ваш <color=#009EFF>{0}</color> в безопасной зоне.",
  4804.         ["VehicleInventoryDropped"] = "Инвентарь из вашего <color=#009EFF>{0}</color> не может быть вызван, он выброшен на землю.",
  4805.         ["NoResourcesToPurchaseVehicle"] = "У вас недостаточно ресурсов для покупки <color=#009EFF>{0}</color>. Вам не хватает: \n{1}",
  4806.         ["NoResourcesToSpawnVehicle"] = "У вас недостаточно ресурсов для покупки <color=#009EFF>{0}</color>. Вам не хватает: \n{1}",
  4807.         ["NoResourcesToSpawnVehicleBypass"] = "У вас недостаточно ресурсов для покупки <color=#009EFF>{0}</color>. Вам не хватает: \n{1}",
  4808.         ["NoResourcesToRecallVehicle"] = "У вас недостаточно ресурсов для покупки <color=#009EFF>{0}</color>. Вам не хватает: \n{1}",
  4809.         ["NoResourcesToRecallVehicleBypass"] = "У вас недостаточно ресурсов для покупки <color=#009EFF>{0}</color>. Вам не хватает: \n{1}",
  4810.         ["MountedOrParented"] = "Вы не можете создать <color=#009EFF>{0}</color> когда сидите или привязаны к объекту.",
  4811.         ["RecallTooFar"] = "Вы должны быть в пределах <color=#FF1919>{0}</color> метров от <color=#009EFF>{1}</color>, чтобы вызывать.",
  4812.         ["KillTooFar"] = "Вы должны быть в пределах <color=#FF1919>{0}</color> метров от <color=#009EFF>{1}</color>, уничтожить.",
  4813.         ["PlayersOnNearby"] = "Вы не можете создать <color=#009EFF>{0}</color> когда рядом с той позицией, на которую вы смотрите, есть игроки.",
  4814.         ["RecallWasBlocked"] = "Внешний плагин заблокировал вам вызвать <color=#009EFF>{0}</color>.",
  4815.         ["SpawnWasBlocked"] = "Внешний плагин заблокировал вам создать <color=#009EFF>{0}</color>.",
  4816.         ["VehiclesLimit"] = "У вас может быть до <color=#009EFF>{0}</color> автомобилей одновременно",
  4817.         ["TooFarTrainTrack"] = "Вы слишком далеко от железнодорожных путей",
  4818.         ["TooCloseTrainBarricadeOrWorkCart"] = "Вы слишком близко к железнодорожной баррикаде или рабочей тележке",
  4819.         ["NotSpawnedOrRecalled"] = "По какой-то причине ваш <color=#009EFF>{0}</color>  автомобилей не был вызван / отозван",
  4820.  
  4821.         ["CantUse"] = "Простите! Этот {0} принадлежит {1}. Вы не можете его использовать."
  4822.       }, this, "ru");
  4823.     }
  4824.  
  4825.     #endregion LanguageFile
  4826.   }
  4827. }
Advertisement
Add Comment
Please, Sign In to add comment