C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust;
using UnityEngine;
namespace Oxide.Plugins {
[Info("Vehicle Licence", "Sorrow/TheDoc/Arainrr", "1.4.10")]
[Description("Allows players to buy vehicles and then spawn or store it")]
public class VehicleLicence : RustPlugin {
#region Fields
[PluginReference]
private readonly Plugin Economics, ServerRewards, Friends, Clans, NoEscape;
private const string PERMISSION_ALL = "vehiclelicence.all";
private const string PERMISSION_ROWBOAT = "vehiclelicence.rowboat";
private const string PERMISSION_RHIB = "vehiclelicence.rhib";
private const string PERMISSION_SEDAN = "vehiclelicence.sedan";
private const string PERMISSION_HOTAIRBALLOON = "vehiclelicence.hotairballoon";
private const string PERMISSION_MINICOPTER = "vehiclelicence.minicopter";
private const string PERMISSION_TRANSPORTCOPTER = "vehiclelicence.transportcopter";
private const string PERMISSION_CHINOOK = "vehiclelicence.chinook";
private const string PERMISSION_RIDABLEHORSE = "vehiclelicence.ridablehorse";
private const string PERMISSION_BYPASS_COST = "vehiclelicence.bypasscost";
private const string PREFAB_ROWBOAT = "assets/content/vehicles/boats/rowboat/rowboat.prefab";
private const string PREFAB_RHIB = "assets/content/vehicles/boats/rhib/rhib.prefab";
private const string PREFAB_SEDAN = "assets/content/vehicles/sedan_a/sedantest.entity.prefab";
private const string PREFAB_HOTAIRBALLOON = "assets/prefabs/deployable/hot air balloon/hotairballoon.prefab";
private const string PREFAB_MINICOPTER = "assets/content/vehicles/minicopter/minicopter.entity.prefab";
private const string PREFAB_TRANSPORTCOPTER = "assets/content/vehicles/scrap heli carrier/scraptransporthelicopter.prefab";
private const string PREFAB_CHINOOK = "assets/prefabs/npc/ch47/ch47.entity.prefab";
private const string PREFAB_RIDABLEHORSE = "assets/rust.ai/nextai/testridablehorse.prefab";
private const string PREFAB_ITEM_DROP = "assets/prefabs/misc/item drop/item_drop.prefab";
private readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0);
private readonly Dictionary<BaseEntity, Vehicle> vehiclesCache = new Dictionary<BaseEntity, Vehicle>();
private readonly static int LAYER_GROUND = LayerMask.GetMask("Terrain", "World", "Construction", "Deployed");
private double CurrentTime => DateTime.Now.Subtract(epoch).TotalSeconds;
private enum VehicleType {
Rowboat, RHIB, Sedan, HotAirBalloon, MiniCopter, TransportHelicopter, Chinook, RidableHorse
}
#endregion Fields
#region Oxide Hooks
private void Init() {
LoadData();
UpdataOldData();
permission.RegisterPermission(PERMISSION_ALL, this);
permission.RegisterPermission(PERMISSION_ROWBOAT, this);
permission.RegisterPermission(PERMISSION_RHIB, this);
permission.RegisterPermission(PERMISSION_SEDAN, this);
permission.RegisterPermission(PERMISSION_HOTAIRBALLOON, this);
permission.RegisterPermission(PERMISSION_MINICOPTER, this);
permission.RegisterPermission(PERMISSION_TRANSPORTCOPTER, this);
permission.RegisterPermission(PERMISSION_CHINOOK, this);
permission.RegisterPermission(PERMISSION_RIDABLEHORSE, this);
permission.RegisterPermission(PERMISSION_BYPASS_COST, this);
foreach (var perm in configData.permCooldown.Keys) if (!permission.PermissionExists(perm, this)) permission.RegisterPermission(perm, this);
cmd.AddChatCommand(configData.chatS.helpCommand, this, nameof(CmdLicenceHelp));
cmd.AddChatCommand(configData.chatS.buyCommand, this, nameof(CmdBuyVehicle));
cmd.AddChatCommand(configData.chatS.spawnCommand, this, nameof(CmdSpawnVehicle));
cmd.AddChatCommand(configData.chatS.recallCommand, this, nameof(CmdRecallVehicle));
}
private void OnServerInitialized() {
if (!configData.settings.preventMounting) Unsubscribe(nameof(CanMountEntity));
if (configData.settings.checkVehiclesTime > 0) CheckVehicles();
else Unsubscribe(nameof(OnEntityDismounted));
if (!configData.settings.noDecay) Unsubscribe(nameof(OnEntityTakeDamage));
}
private void Unload() {
foreach (var entry in vehiclesCache.ToList()) {
if (entry.Key != null && !entry.Key.IsDestroyed) {
RefundFuel(entry.Key, entry.Value);
entry.Key.Kill(BaseNetworkable.DestroyMode.Gib);
}
}
SaveData();
}
private void OnServerSave() => timer.Once(UnityEngine.Random.Range(0f, 60f), SaveData);
private void OnPlayerConnected(BasePlayer player) {
if (permission.UserHasPermission(player.UserIDString, PERMISSION_BYPASS_COST)) PurchaseAllVehicles(player.userID);
}
private void OnEntityDismounted(BaseMountable entity, BasePlayer player) {
var vehicleParent = entity?.VehicleParent();
if (vehicleParent == null || vehicleParent.IsDestroyed) return;
if (!vehiclesCache.ContainsKey(vehicleParent)) return;
vehiclesCache[vehicleParent].lastDismount = CurrentTime;
}
private object CanMountEntity(BasePlayer friend, BaseMountable entity) {
var vehicleParent = entity?.VehicleParent();
if (vehicleParent == null || vehicleParent.IsDestroyed) return null;
if (!vehiclesCache.ContainsKey(vehicleParent)) return null;
var ownerID = vehiclesCache[vehicleParent].playerID;
if (ownerID == friend.userID || AreFriends(ownerID.ToString(), friend.UserIDString)) return null;
if (configData.settings.blockDriverSeat && vehicleParent.HasMountPoints() && entity != vehicleParent.mountPoints[0].mountable) return null;
return false;
}
private void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo) {
if (entity == null) return;
if (!vehiclesCache.ContainsKey(entity)) return;
if (hitInfo?.damageTypes?.Get(Rust.DamageType.Decay) > 0) hitInfo.damageTypes.Scale(Rust.DamageType.Decay, 0);
}
private void OnEntityDeath(BaseCombatEntity entity, HitInfo info) => CheckEntity(entity, true);
private void OnEntityKill(BaseEntity entity) => CheckEntity(entity);
#endregion Oxide Hooks
#region Update Old Data
private void UpdataOldData() {
Dictionary<ulong, LicencedPlayer> licencedPlayer;
try {
licencedPlayer = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<ulong, LicencedPlayer>>(Name);
}
catch {
return;
}
foreach (var entry in licencedPlayer) {
if (entry.Value.Vehicles.Count > 0) {
var vehicleTypes = new Dictionary<VehicleType, Vehicle>();
foreach (var vehicle in entry.Value.Vehicles) vehicleTypes.Add(GetVehiclePrefab(vehicle.Value.Prefab), new Vehicle());
storedData.playerData.Add(entry.Key, vehicleTypes);
}
}
SaveData();
}
private class LicencedPlayer {
public readonly ulong Userid;
public Dictionary<string, Vehicle1> Vehicles;
}
private class Vehicle1 {
public ulong Userid;
public string Prefab;
public uint Id;
public TimeSpan Spawned;
public DateTime LastDismount;
}
private VehicleType GetVehiclePrefab(string prefab) {
switch (prefab) {
case PREFAB_ROWBOAT : return VehicleType.Rowboat;
case PREFAB_RHIB : return VehicleType.RHIB;
case PREFAB_SEDAN : return VehicleType.Sedan;
case PREFAB_HOTAIRBALLOON : return VehicleType.HotAirBalloon;
case PREFAB_MINICOPTER : return VehicleType.MiniCopter;
case PREFAB_TRANSPORTCOPTER : return VehicleType.TransportHelicopter;
case PREFAB_CHINOOK : return VehicleType.Chinook;
case PREFAB_RIDABLEHORSE : return VehicleType.RidableHorse;
}
return default(VehicleType);
}
#endregion Update Old Data
#region Helpers
private void CheckEntity(BaseEntity entity, bool isDeath = false) {
if (entity == null) return;
Vehicle vehicle;
if (!vehiclesCache.TryGetValue(entity, out vehicle)) return;
vehiclesCache.Remove(entity);
if (!configData.settings.notRefundFuelOnCrash) RefundFuel(entity, vehicle);
if (storedData.playerData.ContainsKey(vehicle.playerID) && storedData.playerData[vehicle.playerID].ContainsKey(vehicle.vehicleType)) {
if (isDeath && configData.settings.removeVehicleOnCrash) {
storedData.playerData[vehicle.playerID].Remove(vehicle.vehicleType);
return;
}
storedData.playerData[vehicle.playerID][vehicle.vehicleType].entityID = 0;
storedData.playerData[vehicle.playerID][vehicle.vehicleType].lastDeath = CurrentTime;
}
}
private void PurchaseAllVehicles(ulong playerID) {
if (!storedData.playerData.ContainsKey(playerID)) storedData.playerData.Add(playerID, new Dictionary<VehicleType, Vehicle>());
foreach (int value in Enum.GetValues(typeof(VehicleType))) {
var vehicleType = (VehicleType)value;
if (!storedData.playerData[playerID].ContainsKey(vehicleType)) storedData.playerData[playerID].Add(vehicleType, new Vehicle());
}
SaveData();
}
private void CheckVehicles() {
foreach (var entry in vehiclesCache.ToList()) {
if (entry.Key == null || entry.Key.IsDestroyed) continue;
if (VehicleAnyMounted(entry.Key)) continue;
if (VehicleIsActive(entry.Value)) continue;
RefundFuel(entry.Key, entry.Value);
entry.Key.Kill(BaseNetworkable.DestroyMode.Gib);
}
timer.Once(configData.settings.checkVehiclesTime, CheckVehicles);
}
private bool VehicleIsActive(Vehicle vehicle) {
var vehicleSetting = configData.vehicleS[vehicle.vehicleType];
if (vehicleSetting.wipeTime <= 0) return true;
return CurrentTime - vehicle.lastDismount < vehicleSetting.wipeTime;
}
private bool HasPermission(BasePlayer player, string key) {
if (permission.UserHasPermission(player.UserIDString, PERMISSION_ALL)) return true;
foreach (var entry in configData.vehicleS) {
if (entry.Value.commands.Any(x => x.ToLower().Equals(key))) {
switch (entry.Key) {
case VehicleType.Rowboat : return permission.UserHasPermission(player.UserIDString, PERMISSION_ROWBOAT);
case VehicleType.RHIB : return permission.UserHasPermission(player.UserIDString, PERMISSION_RHIB);
case VehicleType.Sedan : return permission.UserHasPermission(player.UserIDString, PERMISSION_SEDAN);
case VehicleType.HotAirBalloon : return permission.UserHasPermission(player.UserIDString, PERMISSION_HOTAIRBALLOON);
case VehicleType.MiniCopter : return permission.UserHasPermission(player.UserIDString, PERMISSION_MINICOPTER);
case VehicleType.TransportHelicopter : return permission.UserHasPermission(player.UserIDString, PERMISSION_TRANSPORTCOPTER);
case VehicleType.Chinook : return permission.UserHasPermission(player.UserIDString, PERMISSION_CHINOOK);
case VehicleType.RidableHorse : return permission.UserHasPermission(player.UserIDString, PERMISSION_RIDABLEHORSE);
}
return false;
}
}
return false;
}
private string GetVehiclePrefab(VehicleType vehicleType) {
switch (vehicleType) {
case VehicleType.Rowboat : return PREFAB_ROWBOAT;
case VehicleType.RHIB : return PREFAB_RHIB;
case VehicleType.Sedan : return PREFAB_SEDAN;
case VehicleType.HotAirBalloon : return PREFAB_HOTAIRBALLOON;
case VehicleType.MiniCopter : return PREFAB_MINICOPTER;
case VehicleType.TransportHelicopter : return PREFAB_TRANSPORTCOPTER;
case VehicleType.Chinook : return PREFAB_CHINOOK;
case VehicleType.RidableHorse : return PREFAB_RIDABLEHORSE;
}
return string.Empty;
}
private bool AreFriends(string playerID, string friendID) {
if (configData.settings.useFriends && Friends != null) {
var r = Friends.CallHook("HasFriend", playerID, friendID);
if (r != null && (bool)r) return true;
}
if (configData.settings.useClans && Clans != null) {
if (Clans.ResourceId == 842) //Rust:IO Clans
{
var playerClan = Clans.Call("GetClanOf", playerID);
var friendClan = Clans.Call("GetClanOf", friendID);
if (playerClan != null && friendClan != null) return (string)playerClan == (string)friendClan;
}
else //Clans
{
var isMember = Clans.Call("IsClanMember", playerID, friendID);
if (isMember != null && (bool)isMember) return true;
}
}
return false;
}
private bool VehicleAnyMounted(BaseEntity entity) {
if (entity is BaseVehicle) return (entity as BaseVehicle).AnyMounted();
List<BasePlayer> players = Facepunch.Pool.GetList<BasePlayer>();
Vis.Entities(entity.transform.position, 2.5f, players, Rust.Layers.Server.Players);
bool flag = players.Count > 0;
Facepunch.Pool.FreeList(ref players);
return flag;
}
private void RefundFuel(BaseEntity entity, Vehicle vehicle) {
ItemContainer itemContainer = null;
switch (vehicle.vehicleType) {
case VehicleType.Chinook : case VehicleType.Sedan : return;
case VehicleType.MiniCopter : case VehicleType.TransportHelicopter : itemContainer = (entity as MiniCopter)?.fuelStorageInstance.Get(true)?.GetComponent<StorageContainer>()?.inventory;
break;
case VehicleType.HotAirBalloon : itemContainer = (entity as HotAirBalloon)?.fuelStorageInstance.Get(true)?.GetComponent<StorageContainer>()?.inventory;
break;
case VehicleType.RHIB : case VehicleType.Rowboat : itemContainer = (entity as MotorRowboat)?.fuelStorageInstance.Get(true)?.GetComponent<StorageContainer>()?.inventory;
break;
case VehicleType.RidableHorse : itemContainer = (entity as RidableHorse)?.inventory;
break;
}
if (itemContainer == null) return;
var player = BasePlayer.FindAwakeOrSleeping(vehicle.playerID.ToString());
if (player == null) itemContainer.Drop(PREFAB_ITEM_DROP, entity.GetDropPosition(), entity.transform.rotation);
else if (itemContainer?.itemList?.Count > 0) {
foreach (var item in itemContainer.itemList.ToList()) player.GiveItem(item);
Print(player, Lang("RefundedVehicleFuel", player.UserIDString, configData.vehicleS[vehicle.vehicleType].displayName));
}
}
private bool IsInWater(BasePlayer player) {
var modelState = player.modelState;
return modelState != null && modelState.waterLevel > 0;
}
private bool IsBlocked(BasePlayer player) {
if (configData.settings.useRaidBlocker && IsRaidBlocked(player.UserIDString)) {
Print(player, Lang("RaidBlocked", player.UserIDString));
return true;
}
if (configData.settings.useCombatBlocker && IsCombatBlocked(player.UserIDString)) {
Print(player, Lang("CombatBlocked", player.UserIDString));
return true;
}
return false;
}
private bool IsRaidBlocked(string playerID) => (bool)(NoEscape?.Call("IsRaidBlocked", playerID) ?? false);
private bool IsCombatBlocked(string playerID) => (bool)(NoEscape?.Call("IsCombatBlocked", playerID) ?? false);
#region API
private bool HasVehicle(ulong playerID, string type) {
VehicleType vehicleType;
if (!Enum.TryParse(type, true, out vehicleType)) return false;
return storedData.playerData.ContainsKey(playerID) && storedData.playerData[playerID].ContainsKey(vehicleType);
}
private List<string> GetPlayerVehicles(ulong playerID) => storedData.playerData.ContainsKey(playerID) ? storedData.playerData[playerID].Keys.Select(x => x.ToString()).ToList() : new List<string>();
#endregion API
#endregion Helpers
#region Commands
#region Help
private void CmdLicenceHelp(BasePlayer player, string command, string[] args) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine(Lang("Help", player.UserIDString));
stringBuilder.AppendLine(Lang("HelpLicence1", player.UserIDString, configData.chatS.buyCommand));
stringBuilder.AppendLine(Lang("HelpLicence2", player.UserIDString, configData.chatS.spawnCommand));
stringBuilder.AppendLine(Lang("HelpLicence3", player.UserIDString, configData.chatS.recallCommand));
Print(player, stringBuilder.ToString());
}
#endregion Help
#region Buy
[ConsoleCommand("vl.buy")]
private void CCmdBuyVehicle(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null && arg.IsAdmin && arg.Args != null && arg.Args.Length == 2) {
player = RustCore.FindPlayer(arg.Args[1]);
if (player == null) {
Print(arg, $"Player '{arg.Args[1]}' not found");
return;
}
IsBuyOption(player, arg.Args[0].ToLower(), false);
return;
}
if (player != null) CmdBuyVehicle(player, string.Empty, arg.Args);
else Print(arg, $"The server console cannot use '{arg.cmd.FullName}'");
}
private void CmdBuyVehicle(BasePlayer player, string command, string[] args) {
if (args == null || args.Length < 1) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine(Lang("Help", player.UserIDString));
foreach (var entry in configData.vehicleS) {
if (entry.Value.purchasable && entry.Value.commands.Count > 0) {
var price = string.Join(", ", from p in entry.Value.price select $"{p.Value.displayName} x{p.Value.amount}");
stringBuilder.AppendLine(Lang("HelpBuy", player.UserIDString, configData.chatS.buyCommand, entry.Value.commands[0], entry.Value.displayName, price));
}
}
Print(player, stringBuilder.ToString());
return;
}
var arg = args[0].ToLower();
if (configData.settings.usePermission && !HasPermission(player, arg)) {
Print(player, Lang("NotAllowed", player.UserIDString));
return;
}
if (IsBlocked(player)) return;
if (!storedData.playerData.ContainsKey(player.userID)) storedData.playerData.Add(player.userID, new Dictionary<VehicleType, Vehicle>());
IsBuyOption(player, arg);
}
private bool IsBuyOption(BasePlayer player, string key, bool pay = true) {
foreach (var entry in configData.vehicleS) {
if (entry.Value.commands.Any(x => x.ToLower().Equals(key))) {
BuyVehicle(player, entry.Key, pay);
return true;
}
}
Print(player, Lang("OptionNotFound", player.UserIDString, key));
return false;
}
private void BuyVehicle(BasePlayer player, VehicleType vehicleType, bool pay = true) {
var vehicleSetting = configData.vehicleS[vehicleType];
if (!vehicleSetting.purchasable) {
Print(player, Lang("VehicleCannotBeBuyed", player.UserIDString, vehicleSetting.displayName));
return;
}
if (storedData.playerData[player.userID].ContainsKey(vehicleType)) {
Print(player, Lang("VehicleAlreadyPurchased", player.UserIDString, vehicleSetting.displayName));
return;
}
if (pay && !BuyVehicle(player, vehicleSetting)) return;
storedData.playerData[player.userID].Add(vehicleType, new Vehicle());
Print(player, Lang("VehiclePurchased", player.UserIDString, vehicleSetting.displayName, configData.chatS.spawnCommand));
SaveData();
}
private bool BuyVehicle(BasePlayer player, ConfigData.VehicleSetting vehicleSetting) {
if (permission.UserHasPermission(player.UserIDString, PERMISSION_BYPASS_COST)) return true;
if (!CanPay(player, vehicleSetting)) {
Print(player, Lang("NoMoney", player.UserIDString, vehicleSetting.displayName));
return false;
}
List<Item> collect = new List<Item>();
foreach (var entry in vehicleSetting.price) {
if (entry.Value.amount <= 0) continue;
var item = ItemManager.FindItemDefinition(entry.Key);
if (item != null) {
player.inventory.Take(collect, item.itemid, entry.Value.amount);
player.Command("note.inv", item.itemid, -entry.Value.amount);
}
else if (!CheckOrPay(entry.Key, entry.Value.amount, player.userID)) return false;
}
foreach (Item item in collect) item.Remove();
return true;
}
private bool CanPay(BasePlayer player, ConfigData.VehicleSetting vehicleSetting) {
foreach (var entry in vehicleSetting.price) {
if (entry.Value.amount <= 0) continue;
var item = ItemManager.FindItemDefinition(entry.Key);
if (item != null) {
int amount = player.inventory.GetAmount(item.itemid);
if (amount < entry.Value.amount) return false;
}
else if (!CheckOrPay(entry.Key, entry.Value.amount, player.userID, true)) return false;
}
return true;
}
private bool CheckOrPay(string key, int price, ulong playerID, bool check = false) {
switch (key.ToLower()) {
case "economics" : if (Economics == null) return false;
if (check) {
var b = Economics.CallHook("Balance", playerID);
if (b == null || (double)b < price) return false;
}
else {
var w = Economics.CallHook("Withdraw", playerID, (double)price);
if (w == null || !(bool)w) return false;
}
return true;
case "serverrewards" : if (ServerRewards == null) return false;
if (check) {
var c = ServerRewards.CallHook("CheckPoints", playerID);
if (c == null || (int)c < price) return false;
}
else {
var t = ServerRewards.CallHook("TakePoints", playerID, price);
if (t == null || !(bool)t) return false;
}
return true;
default : return true;
}
}
#endregion Buy
#region Spawn
[ConsoleCommand("vl.spawn")]
private void CCmdSpawnVehicle(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) Print(arg, $"The server console cannot use '{arg.cmd.FullName}'");
else CmdSpawnVehicle(player, string.Empty, arg.Args);
}
private void CmdSpawnVehicle(BasePlayer player, string command, string[] args) {
if (args == null || args.Length < 1) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine(Lang("Help", player.UserIDString));
foreach (var entry in configData.vehicleS) if (entry.Value.purchasable && entry.Value.commands.Count > 0) stringBuilder.AppendLine(Lang("HelpSpawn", player.UserIDString, configData.chatS.spawnCommand, entry.Value.commands[0], entry.Value.displayName));
Print(player, stringBuilder.ToString());
return;
}
var arg = args[0].ToLower();
if (configData.settings.usePermission && !HasPermission(player, arg)) {
Print(player, Lang("NotAllowed", player.UserIDString));
return;
}
if (IsBlocked(player)) return;
if (!storedData.playerData.ContainsKey(player.userID)) storedData.playerData.Add(player.userID, new Dictionary<VehicleType, Vehicle>());
IsSpawnOption(player, arg);
}
private bool IsSpawnOption(BasePlayer player, string key) {
foreach (var entry in configData.vehicleS) {
if (entry.Value.commands.Any(x => x.ToLower().Equals(key))) {
if (entry.Key == VehicleType.Rowboat || entry.Key == VehicleType.RHIB ? CanSpawn(player, entry.Key, true) : CanSpawn(player, entry.Key)) SpawnVehicle(player, entry.Key);
return true;
}
}
Print(player, Lang("OptionNotFound", player.UserIDString, key));
return false;
}
private bool CanSpawn(BasePlayer player, VehicleType vehicleType, bool checkWater = false) {
var vehicleSetting = configData.vehicleS[vehicleType];
if (player.IsBuildingBlocked()) {
Print(player, Lang("BuildindBlocked", player.UserIDString, vehicleSetting.displayName));
return false;
}
if (!storedData.playerData[player.userID].ContainsKey(vehicleType)) {
Print(player, Lang("VehicleNotYetPurchased", player.UserIDString, vehicleSetting.displayName));
return false;
}
var vehicle = storedData.playerData[player.userID][vehicleType];
if (vehicle.entityID != 0) {
Print(player, Lang("AlreadyVehicleOut", player.UserIDString, vehicleSetting.displayName, configData.chatS.recallCommand));
return false;
}
if (checkWater && !IsInWater(player)) {
Print(player, Lang("NotInWater", player.UserIDString, vehicleSetting.displayName));
return false;
}
var cooldown = GetCooldown(player, vehicleType, vehicleSetting.cooldown);
if (cooldown > 0) {
var timeleft = Math.Ceiling(cooldown - (CurrentTime - vehicle.lastDeath));
if (timeleft > 0) {
Print(player, Lang("VehicleOnCooldown", player.UserIDString, timeleft.ToString(), vehicleSetting.displayName));
return false;
}
}
return true;
}
private double GetCooldown(BasePlayer player, VehicleType vehicleType, double cooldown) {
foreach (var entry in configData.permCooldown) {
if (permission.UserHasPermission(player.UserIDString, entry.Key) && entry.Value.ContainsKey(vehicleType) && cooldown > entry.Value[vehicleType]) cooldown = entry.Value[vehicleType];
}
return cooldown;
}
private void SpawnVehicle(BasePlayer player, VehicleType vehicleType) {
var prefab = GetVehiclePrefab(vehicleType);
if (string.IsNullOrEmpty(prefab)) return;
var vehicleSetting = configData.vehicleS[vehicleType];
Vector3 position = Vector3.zero;
if (configData.settings.spawnLookingAt) position = player.transform.position + player.eyes.HeadForward() * vehicleSetting.distance;
else {
var circle = UnityEngine.Random.insideUnitCircle * vehicleSetting.distance;
position = player.transform.position + new Vector3(circle.x, 0, circle.y);
}
position = GetGroundPosition(position);
var entity = GameManager.server.CreateEntity(prefab, position + new Vector3(0f, 1.8f, 0f), player.transform.rotation);
if (entity == null) return;
entity.enableSaving = false;
entity.OwnerID = player.userID;
entity.Spawn();
if (configData.settings.noServerGibs && entity is BaseVehicle) (entity as BaseVehicle).serverGibs.guid = string.Empty;
if (configData.settings.noFireBall && entity is BaseHelicopterVehicle) (entity as BaseHelicopterVehicle).fireBall.guid = string.Empty;
var vehicle = new Vehicle {
playerID = player.userID, vehicleType = vehicleType, entityID = entity.net.ID, lastDismount = CurrentTime
}
;
vehiclesCache.Add(entity, vehicle);
storedData.playerData[player.userID][vehicleType] = vehicle;
Print(player, Lang("VehicleSpawned", player.UserIDString, vehicleSetting.displayName));
}
private Vector3 GetGroundPosition(Vector3 position) {
position.y += 100f;
RaycastHit hitInfo;
if (Physics.Raycast(position, Vector3.down, out hitInfo, 200f, LAYER_GROUND)) position.y = hitInfo.point.y;
else position.y = TerrainMeta.HeightMap.GetHeight(position);
return position;
}
#endregion Spawn
#region Recall
[ConsoleCommand("vl.recall")]
private void CCmdRecallVehicle(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) Print(arg, $"The server console cannot use '{arg.cmd.FullName}'");
else CmdRecallVehicle(player, string.Empty, arg.Args);
}
private void CmdRecallVehicle(BasePlayer player, string command, string[] args) {
if (args == null || args.Length < 1) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine(Lang("Help", player.UserIDString));
foreach (var entry in configData.vehicleS) if (entry.Value.purchasable && entry.Value.commands.Count > 0) stringBuilder.AppendLine(Lang("HelpRecall", player.UserIDString, configData.chatS.recallCommand, entry.Value.commands[0], entry.Value.displayName));
Print(player, stringBuilder.ToString());
return;
}
var arg = args[0].ToLower();
if (configData.settings.usePermission && !HasPermission(player, arg)) {
Print(player, Lang("NotAllowed", player.UserIDString));
return;
}
if (IsBlocked(player)) return;
if (!storedData.playerData.ContainsKey(player.userID)) storedData.playerData.Add(player.userID, new Dictionary<VehicleType, Vehicle>());
IsReallOption(player, arg);
}
private bool IsReallOption(BasePlayer player, string key) {
foreach (var entry in configData.vehicleS) {
if (entry.Value.commands.Any(x => x.ToLower().Equals(key))) {
RemoveVehicle(player, entry.Key);
return true;
}
}
Print(player, Lang("OptionNotFound", player.UserIDString, key));
return false;
}
private void RemoveVehicle(BasePlayer player, VehicleType vehicleType) {
string vehicleName = configData.vehicleS[vehicleType].displayName;
if (!storedData.playerData[player.userID].ContainsKey(vehicleType)) {
Print(player, Lang("VehicleNotYetPurchased", player.UserIDString, vehicleName));
return;
}
if (storedData.playerData[player.userID][vehicleType].entityID != 0) {
foreach (var entry in vehiclesCache.ToList()) {
if (entry.Value.playerID == player.userID && entry.Value.vehicleType == vehicleType) {
if (entry.Key != null && !entry.Key.IsDestroyed) {
if (configData.settings.checkAnyMounted && VehicleAnyMounted(entry.Key)) {
Print(player, Lang("PlayerMountedOnVehicle", player.UserIDString, vehicleName));
return;
}
RefundFuel(entry.Key, entry.Value);
entry.Key.Kill(BaseNetworkable.DestroyMode.Gib);
}
Print(player, Lang("VehicleRecalled", player.UserIDString, vehicleName));
return;
}
}
}
Print(player, Lang("VehicleNotOut", player.UserIDString, vehicleName));
}
#endregion Recall
#endregion Commands
#region ConfigurationFile
private ConfigData configData;
private class ConfigData {
[JsonProperty(PropertyName = "Settings")]
public Settings settings = new Settings();
public class Settings {
[JsonProperty(PropertyName = "Interval to check vehicle for wipe (Seconds)")]
public float checkVehiclesTime = 300;
[JsonProperty(PropertyName = "Prevent other players from mounting vehicle")]
public bool preventMounting = true;
[JsonProperty(PropertyName = "Prevent mounting on driver's seat only")]
public bool blockDriverSeat = true;
[JsonProperty(PropertyName = "Check if any player mounted when recalling a vehicle")]
public bool checkAnyMounted = true;
[JsonProperty(PropertyName = "Spawn vehicle in the direction you are looking at")]
public bool spawnLookingAt = true;
[JsonProperty(PropertyName = "Use Clans")]
public bool useClans = true;
[JsonProperty(PropertyName = "Use Friends")]
public bool useFriends = true;
[JsonProperty(PropertyName = "Use Permission")]
public bool usePermission = true;
[JsonProperty(PropertyName = "Vehicle No Decay")]
public bool noDecay = false;
[JsonProperty(PropertyName = "Vehicle No Fire Ball")]
public bool noFireBall = true;
[JsonProperty(PropertyName = "Vehicle No Server Gibs")]
public bool noServerGibs = true;
[JsonProperty(PropertyName = "Remove Vehicles On Crash")]
public bool removeVehicleOnCrash = false;
[JsonProperty(PropertyName = "Not Refund Fuel On Crash")]
public bool notRefundFuelOnCrash = false;
[JsonProperty(PropertyName = "Clear Vehicle Data On Map Wipe")]
public bool clearVehicleOnWipe = false;
[JsonProperty(PropertyName = "Use Raid Blocker (Need NoEscape Plugin)")]
public bool useRaidBlocker = false;
[JsonProperty(PropertyName = "Use Combat Blocker (Need NoEscape Plugin)")]
public bool useCombatBlocker = false;
}
[JsonProperty(PropertyName = "Chat Settings")]
public ChatSettings chatS = new ChatSettings();
public class ChatSettings {
[JsonProperty(PropertyName = "Help Chat Command")]
public string helpCommand = "license";
[JsonProperty(PropertyName = "Buy Chat Command")]
public string buyCommand = "buy";
[JsonProperty(PropertyName = "Spawn Chat Command")]
public string spawnCommand = "spawn";
[JsonProperty(PropertyName = "Recall Chat Command")]
public string recallCommand = "recall";
[JsonProperty(PropertyName = "Chat Prefix")]
public string prefix = "[VehicleLicence]: ";
[JsonProperty(PropertyName = "Chat Prefix Color")]
public string prefixColor = "#B366FF";
[JsonProperty(PropertyName = "Chat SteamID Icon")]
public ulong steamIDIcon = 76561198924840872;
}
[JsonProperty(PropertyName = "Cooldown Permission Settings", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, Dictionary<VehicleType, float>> permCooldown = new Dictionary<string, Dictionary<VehicleType, float>>() {
["vehiclelicence.vip"]
= new Dictionary<VehicleType, float> {
[VehicleType.Rowboat]
= 90f, [VehicleType.RHIB] = 150f, [VehicleType.Sedan] = 450f, [VehicleType.HotAirBalloon] = 450f, [VehicleType.MiniCopter] = 900f, [VehicleType.TransportHelicopter] = 1200f, [VehicleType.Chinook] = 1500f, [VehicleType.RidableHorse] = 1500f,
}
}
;
[JsonProperty(PropertyName = "Vehicle Settings")]
public Dictionary<VehicleType, VehicleSetting> vehicleS = new Dictionary<VehicleType, VehicleSetting>() {
[VehicleType.Rowboat]
= new VehicleSetting {
displayName = "Row Boat", purchasable = true, cooldown = 180, distance = 3, commands = new List<string> {
"row", "rowboat"
}
, price = new Dictionary<string, VehicleSetting.ItemInfo> {
["scrap"]
= new VehicleSetting.ItemInfo {
amount = 500, displayName = "Scrap"
}
}
}
, [VehicleType.RHIB] = new VehicleSetting {
displayName = "RHIB", purchasable = true, cooldown = 300, distance = 10, commands = new List<string> {
"rhib"
}
, price = new Dictionary<string, VehicleSetting.ItemInfo> {
["scrap"]
= new VehicleSetting.ItemInfo {
amount = 1000, displayName = "Scrap"
}
}
}
, [VehicleType.Sedan] = new VehicleSetting {
displayName = "Sedan", purchasable = true, cooldown = 180, distance = 5, commands = new List<string> {
"car", "sedan"
}
, price = new Dictionary<string, VehicleSetting.ItemInfo> {
["scrap"]
= new VehicleSetting.ItemInfo {
amount = 300, displayName = "Scrap"
}
}
}
, [VehicleType.HotAirBalloon] = new VehicleSetting {
displayName = "Hot Air Balloon", purchasable = true, cooldown = 900, distance = 20, commands = new List<string> {
"hab", "hotairballoon"
}
, price = new Dictionary<string, VehicleSetting.ItemInfo> {
["scrap"]
= new VehicleSetting.ItemInfo {
amount = 5000, displayName = "Scrap"
}
}
}
, [VehicleType.MiniCopter] = new VehicleSetting {
displayName = "Mini Copter", purchasable = true, cooldown = 1800, distance = 8, commands = new List<string> {
"mini", "minicopter"
}
, price = new Dictionary<string, VehicleSetting.ItemInfo> {
["scrap"]
= new VehicleSetting.ItemInfo {
amount = 10000, displayName = "Scrap"
}
}
}
, [VehicleType.TransportHelicopter] = new VehicleSetting {
displayName = "Transport Copter", purchasable = true, cooldown = 2400, distance = 10, commands = new List<string> {
"scrapcopter", "transportcopter"
}
, price = new Dictionary<string, VehicleSetting.ItemInfo> {
["scrap"]
= new VehicleSetting.ItemInfo {
amount = 20000, displayName = "Scrap"
}
}
}
, [VehicleType.Chinook] = new VehicleSetting {
displayName = "Chinook", purchasable = true, cooldown = 3000, distance = 20, commands = new List<string> {
"ch47", "chinook"
}
, price = new Dictionary<string, VehicleSetting.ItemInfo> {
["scrap"]
= new VehicleSetting.ItemInfo {
amount = 30000, displayName = "Scrap"
}
}
}
, [VehicleType.RidableHorse] = new VehicleSetting {
displayName = "Ridable Horse", purchasable = true, cooldown = 3000, distance = 5, commands = new List<string> {
"horse", "ridablehorse"
}
, price = new Dictionary<string, VehicleSetting.ItemInfo> {
["scrap"]
= new VehicleSetting.ItemInfo {
amount = 700, displayName = "Scrap"
}
}
}
}
;
public class VehicleSetting {
[JsonProperty(PropertyName = "Purchasable")]
public bool purchasable = true;
[JsonProperty(PropertyName = "Vehicle Display Name")]
public string displayName = string.Empty;
[JsonProperty(PropertyName = "Distance To Spawn")]
public float distance = 5f;
[JsonProperty(PropertyName = "Cooldown (Seconds)")]
public double cooldown = 180;
[JsonProperty(PropertyName = "Time Before Vehicle Wipe (Seconds)")]
public double wipeTime = 1800;
[JsonProperty(PropertyName = "Commands")]
public List<string> commands = new List<string>();
[JsonProperty(PropertyName = "Price")]
public Dictionary<string, ItemInfo> price = new Dictionary<string, ItemInfo>();
public class ItemInfo {
public int amount;
public string displayName;
}
}
}
protected override void LoadConfig() {
base.LoadConfig();
try {
configData = Config.ReadObject<ConfigData>();
if (configData == null) LoadDefaultConfig();
}
catch {
PrintError("The configuration file is corrupted");
LoadDefaultConfig();
}
SaveConfig();
}
protected override void LoadDefaultConfig() {
PrintWarning("Creating a new configuration file");
configData = new ConfigData();
}
protected override void SaveConfig() => Config.WriteObject(configData);
#endregion ConfigurationFile
#region DataFile
private StoredData storedData;
private class StoredData {
public Dictionary<ulong, Dictionary<VehicleType, Vehicle>> playerData = new Dictionary<ulong, Dictionary<VehicleType, Vehicle>>();
}
private class Vehicle {
public double lastDeath;
[JsonIgnore]
public uint entityID;
[JsonIgnore]
public ulong playerID;
[JsonIgnore]
public double lastDismount;
[JsonIgnore]
public VehicleType vehicleType;
}
private void LoadData() {
try {
storedData = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name);
}
catch {
ClearData();
}
}
private void ClearData() {
storedData = new StoredData();
SaveData();
}
private void OnNewSave(string filename) {
if (configData.settings.clearVehicleOnWipe) ClearData();
else {
foreach (var entry in storedData.playerData.ToList()) if (entry.Value.Count <= 0) storedData.playerData.Remove(entry.Key);
SaveData();
}
}
private void SaveData() => Interface.Oxide.DataFileSystem.WriteObject(Name, storedData);
#endregion DataFile
#region LanguageFile
private void Print(BasePlayer player, string message) => Player.Message(player, message, $"<color={configData.chatS.prefixColor}>{configData.chatS.prefix}</color>", configData.chatS.steamIDIcon);
private void Print(ConsoleSystem.Arg arg, string message) {
var player = arg.Player();
if (player == null) Puts(message);
else PrintToConsole(player, message);
}
private string Lang(string key, string id = null, params object[] args) => string.Format(lang.GetMessage(key, this, id), args);
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
["Help"]
= "These are the available commands:", ["HelpLicence1"] = "<color=#4DFF4D>/{0}</color> -- To buy a vehicle", ["HelpLicence2"] = "<color=#4DFF4D>/{0}</color> -- To spawn a vehicle", ["HelpLicence3"] = "<color=#4DFF4D>/{0}</color> -- To recall a vehicle", ["HelpBuy"] = "<color=#4DFF4D>/{0} {1}</color> -- To buy a {2}, price: <color=#FF1919>{3}</color>", ["HelpSpawn"] = "<color=#4DFF4D>/{0} {1}</color>
// ... [Code preview truncated due to length. Full source in attached file] ...