LeeFall

tugboats VL

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