using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Spawn heli", "SpooksAU", "2.12.3")]
[Description("Spawn a heli!")]
class Spawnheli : RustPlugin
{
private SaveData _data;
private PluginConfig _config;
/* EDIT PERMISSIONS HERE */
private readonly string _Spawnheli = "Spawnheli.heli";
private readonly string _noCooldown = "Spawnheli.nocd";
private readonly string _noHeli = "Spawnheli.noHeli";
private readonly string _fetchheli = "Spawnheli.fheli";
private readonly string _noFuel = "Spawnheli.unlimitedfuel";
private readonly string _noDecay = "Spawnheli.nodecay";
private readonly string _permissionFuelFormat = "Spawnheli.fuel.{0}";
#region Hooks
private void Init()
{
permission.RegisterPermission(_Spawnheli, this);
permission.RegisterPermission(_noCooldown, this);
permission.RegisterPermission(_noHeli, this);
permission.RegisterPermission(_fetchheli, this);
permission.RegisterPermission(_noFuel, this);
permission.RegisterPermission(_noDecay, this);
foreach (var perm in _config.spawnPermissionCooldowns)
permission.RegisterPermission(perm.Key, this);
foreach (var perm in _config.fetchPermissionCooldowns)
{
// Allow server owners to use the same permission for spawn and fetch.
if (!permission.PermissionExists(perm.Key))
{
permission.RegisterPermission(perm.Key, this);
}
}
foreach (var fuelAmount in _config.fuelAmountsRequiringPermission)
permission.RegisterPermission(GetFuelPermission(fuelAmount), this);
if (!Interface.Oxide.DataFileSystem.ExistsDatafile(Name))
Interface.Oxide.DataFileSystem.GetDatafile(Name).Save();
LoadSaveData();
if (!_config.ownerOnly)
Unsubscribe(nameof(CanMountEntity));
if (!_config.destroyOnDisconnect)
{
Unsubscribe(nameof(OnPlayerDisconnected));
Unsubscribe(nameof(OnEntityDismounted));
}
}
void OnServerInitialized()
{
foreach (var heli in BaseNetworkable.serverEntities.OfType<ScrapTransportHelicopter>())
{
if (IsPlayerOwned(heli) && heli.OwnerID != 0 && permission.UserHasPermission(heli.OwnerID.ToString(), _noFuel))
{
EnableUnlimitedFuel(heli);
}
}
}
void Unload() => WriteSaveData();
void OnServerSave() => WriteSaveData();
void OnNewSave()
{
_data.playerheli.Clear();
_data.spawnCooldowns.Clear();
_data.fetchCooldowns.Clear();
WriteSaveData();
}
void OnEntityKill(ScrapTransportHelicopter heli)
{
if (_data.playerheli.ContainsValue(heli.net.ID.Value))
{
string key = _data.playerheli.FirstOrDefault(x => x.Value == heli.net.ID.Value).Key;
ulong result;
ulong.TryParse(key, out result);
BasePlayer player = BasePlayer.FindByID(result);
if (player != null)
player.ChatMessage(lang.GetMessage("heli_destroyed", this, player.UserIDString));
_data.playerheli.Remove(key);
}
}
object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
{
if (entity == null || info == null || entity.OwnerID == 0)
return null;
if (_data.playerheli.ContainsValue(entity.net.ID.Value))
if (permission.UserHasPermission(entity.OwnerID.ToString(), _noDecay) && info.damageTypes.Has(Rust.DamageType.Decay))
return true;
return null;
}
object CanMountEntity(BasePlayer player, BaseVehicleMountPoint entity)
{
if (player == null || entity == null)
return null;
var heli = entity.GetParentEntity() as ScrapTransportHelicopter;
if (heli == null || heli is ScrapTransportHelicopter || heli.OwnerID == 0 || !IsPlayerOwned(heli)) return null;
if (heli.OwnerID != player.userID)
{
if (player.Team != null && player.Team.members.Contains(heli.OwnerID))
return null;
player.ChatMessage(lang.GetMessage("heli_canmount", this, player.UserIDString));
return false;
}
return null;
}
void OnPlayerDisconnected(BasePlayer player)
{
// Temporary workaround for OnPlayerDisconnected not subscribing to Oxide issue.
if (!_config.destroyOnDisconnect)
return;
if (player == null)
return;
ulong heliNetId;
if (!_data.playerheli.TryGetValue(player.UserIDString, out heliNetId))
return;
var heli = BaseNetworkable.serverEntities.Find(new NetworkableId(heliNetId)) as ScrapTransportHelicopter;
if (heli == null)
return;
NextTick(() =>
{
if (heli == null)
return;
// Despawn ScrapTransportHelicopter when the owner disconnects
// If mounted, we will despawn it later when all players dismount
if (!heli.AnyMounted())
heli.Kill();
});
}
void OnEntityDismounted(BaseVehicleSeat seat)
{
if (seat == null)
return;
var heli = seat.GetParentEntity() as ScrapTransportHelicopter;
if (heli == null || heli.OwnerID == 0 || !IsPlayerOwned(heli) || heli.AnyMounted())
return;
// Despawn ScrapTransportHelicopter when fully dismounted, if the owner player has disconnected
var ownerPlayer = BasePlayer.FindByID(heli.OwnerID);
if (ownerPlayer == null || !ownerPlayer.IsConnected)
heli.Kill();
}
void CanLootEntity(BasePlayer player, StorageContainer container)
{
if (container == null || !container.IsLocked())
return;
var heli = container.GetParentEntity() as ScrapTransportHelicopter;
if (heli == null || !IsPlayerOwned(heli))
return;
if (permission.UserHasPermission(heli.OwnerID.ToString(), _noFuel))
player.ChatMessage(lang.GetMessage("heli_unlimited_fuel", this, player.UserIDString));
}
#endregion
#region Commands
[ChatCommand("myheli")]
private void SpawnCommand(BasePlayer player, string command, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, _Spawnheli))
{
player.ChatMessage(lang.GetMessage("heli_perm", this, player.UserIDString));
return;
}
var heli = FindPlayerheli(player);
if (heli != null)
{
if (_config.autoFetch && permission.UserHasPermission(player.UserIDString, _fetchheli))
{
FetchInternal(player, heli);
}
else
{
player.ChatMessage(lang.GetMessage("heli_current", this, player.UserIDString));
}
return;
}
if (SpawnWasBlocked(player))
return;
if (!VerifyOffCooldown(player, _config.spawnPermissionCooldowns, _config.defaultSpawnCooldown, _data.spawnCooldowns))
return;
Spawnhelicopter(player);
}
[ChatCommand("fheli")]
private void FetchCommand(BasePlayer player, string command, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, _fetchheli))
{
player.ChatMessage(lang.GetMessage("heli_perm", this, player.UserIDString));
return;
}
var heli = FindPlayerheli(player);
if (heli == null)
{
player.ChatMessage(lang.GetMessage("heli_notcurrent", this, player.UserIDString));
return;
}
FetchInternal(player, heli);
}
[ChatCommand("noheli")]
private void DespawnCommand(BasePlayer player, string command, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, _noHeli))
{
player.ChatMessage(lang.GetMessage("heli_perm", this, player.UserIDString));
return;
}
var heli = FindPlayerheli(player);
if (heli == null)
{
player.ChatMessage(lang.GetMessage("heli_notcurrent", this, player.UserIDString));
return;
}
if (heli.AnyMounted() && !_config.canDespawnWhileOccupied)
{
player.ChatMessage(lang.GetMessage("heli_mounted", this, player.UserIDString));
return;
}
if (IsheliBeyondMaxDistance(player, heli))
{
player.ChatMessage(lang.GetMessage("heli_current_distance", this, player.UserIDString));
return;
}
if (DespawnWasBlocked(player, heli))
return;
BaseNetworkable.serverEntities.Find(new NetworkableId(_data.playerheli[player.UserIDString]))?.Kill();
}
[ConsoleCommand("Spawnheli.give")]
private void GiveheliConsole(ConsoleSystem.Arg arg)
{
if (arg.IsClientside || !arg.IsRcon)
return;
var args = arg.Args;
if (args == null || args.Length == 0)
{
Puts("Syntax: Spawnheli.give <name or steamid>");
return;
}
var player = BasePlayer.Find(args[0]);
if (player == null)
{
PrintError($"No player found matching '{args[0]}'");
return;
}
if (args.Length > 1)
{
float x, y, z;
if (args.Length < 4 ||
!float.TryParse(args[1], out x) ||
!float.TryParse(args[2], out y) ||
!float.TryParse(args[3], out z))
{
Puts($"Syntax: Spawnheli.give <name or steamid> <x> <y> <z>");
return;
}
GiveScrapTransportHelicopter (player, new Vector3(x, y, z), useCustomPosition: true);
}
else
{
GiveScrapTransportHelicopter (player);
}
}
#endregion
#region Helpers/Functions
private bool VerifyOffCooldown(BasePlayer player, Dictionary<string, float> cooldownPerms, float defaultCooldown, Dictionary<string, DateTime> cooldownMap)
{
DateTime cooldownStart;
if (!cooldownMap.TryGetValue(player.UserIDString, out cooldownStart) || permission.UserHasPermission(player.UserIDString, _noCooldown))
{
return true;
}
DateTime lastSpawned = cooldownMap[player.UserIDString];
TimeSpan timeRemaining = CeilingTimeSpan(lastSpawned.AddSeconds(GetPlayerCooldownSeconds(player, cooldownPerms, defaultCooldown)) - DateTime.Now);
if (timeRemaining.TotalSeconds <= 0)
{
cooldownMap.Remove(player.UserIDString);
return true;
}
player.ChatMessage(string.Format(lang.GetMessage("heli_timeleft_new", this, player.UserIDString), timeRemaining.ToString("g")));
return false;
}
private bool SpawnWasBlocked(BasePlayer player)
{
object hookResult = Interface.CallHook("OnMyheliSpawn", player);
return hookResult is bool && (bool)hookResult == false;
}
private bool FetchWasBlocked(BasePlayer player, ScrapTransportHelicopter heli)
{
object hookResult = Interface.CallHook("OnMyheliFetch", player, heli);
return hookResult is bool && (bool)hookResult == false;
}
private bool DespawnWasBlocked(BasePlayer player, ScrapTransportHelicopter heli)
{
object hookResult = Interface.CallHook("OnMyheliDespawn", player, heli);
return hookResult is bool && (bool)hookResult == false;
}
private TimeSpan CeilingTimeSpan(TimeSpan timeSpan) =>
new TimeSpan((long)Math.Ceiling(1.0 * timeSpan.Ticks / 10000000) * 10000000);
private bool IsheliBeyondMaxDistance(BasePlayer player, ScrapTransportHelicopter heli) =>
_config.noHeliDistance >= 0 && GetDistance(player, heli) > _config.noHeliDistance;
private Vector3 GetFixedPositionForPlayer(BasePlayer player)
{
Vector3 forward = player.GetNetworkRotation() * Vector3.forward;
forward.y = 0;
return player.transform.position + forward.normalized * _config.fixedSpawnDistance + Vector3.up * 2f;
}
private Quaternion GetFixedRotationForPlayer(BasePlayer player) =>
Quaternion.Euler(0, player.GetNetworkRotation().eulerAngles.y - _config.fixedSpawnRotationAngle, 0);
private ScrapTransportHelicopter FindPlayerheli(BasePlayer player)
{
ulong heliNetId;
if (!_data.playerheli.TryGetValue(player.UserIDString, out heliNetId))
return null;
var heli = BaseNetworkable.serverEntities.Find(new NetworkableId(heliNetId)) as ScrapTransportHelicopter;
// Fix a potential data file desync where the heli doesn't exist anymore
// Desyncs should be rare but are not possible to 100% prevent
// They can happen if the heli is destroyed while the plugin is unloaded
// Or if someone edits the data file manually
if (heli == null)
_data.playerheli.Remove(player.UserIDString);
return heli;
}
private void FetchInternal(BasePlayer player, ScrapTransportHelicopter heli)
{
if (!_config.canFetchBuildlingBlocked && player.IsBuildingBlocked())
{
player.ChatMessage(lang.GetMessage("heli_buildingblocked", this, player.UserIDString));
return;
}
bool isMounted = heli.AnyMounted();
if (isMounted && (!_config.canFetchWhileOccupied || player.GetMountedVehicle() == heli))
{
player.ChatMessage(lang.GetMessage("heli_mounted", this, player.UserIDString));
return;
}
if (IsheliBeyondMaxDistance(player, heli))
{
player.ChatMessage(lang.GetMessage("heli_current_distance", this, player.UserIDString));
return;
}
if (FetchWasBlocked(player, heli))
return;
if (!VerifyOffCooldown(player, _config.fetchPermissionCooldowns, _config.defaultFetchCooldown, _data.fetchCooldowns))
return;
if (isMounted)
{
// heli.DismountAllPlayers() doesn't work so we have to enumerate the mount points
foreach (var mountPoint in heli.mountPoints)
mountPoint.mountable?.DismountAllPlayers();
}
if (_config.repairOnFetch)
{
heli.SetHealth(Math.Max(heli.Health(), _config.spawnHealth));
}
heli.rigidBody.velocity = Vector3.zero;
heli.transform.SetPositionAndRotation(GetFixedPositionForPlayer(player), GetFixedRotationForPlayer(player));
heli.UpdateNetworkGroup();
heli.SendNetworkUpdateImmediate();
if (!permission.UserHasPermission(player.UserIDString, _noCooldown))
{
_data.fetchCooldowns[player.UserIDString] = DateTime.Now;
}
}
private void Spawnhelicopter(BasePlayer player)
{
if (!_config.canSpawnBuildingBlocked && player.IsBuildingBlocked())
{
player.ChatMessage(lang.GetMessage("heli_buildingblocked", this, player.UserIDString));
return;
}
Vector3 position;
if (_config.useFixedSpawnDistance)
{
position = GetFixedPositionForPlayer(player);
}
else
{
RaycastHit hit;
if (!Physics.Raycast(player.eyes.HeadRay(), out hit, Mathf.Infinity,
LayerMask.GetMask("Construction", "Default", "Deployed", "Resource", "Terrain", "Water", "World")))
{
player.ChatMessage(lang.GetMessage("heli_terrain", this, player.UserIDString));
return;
}
if (hit.distance > _config.maxSpawnDistance)
{
player.ChatMessage(lang.GetMessage("heli_sdistance", this, player.UserIDString));
return;
}
position = hit.point + Vector3.up * 2f;
}
ScrapTransportHelicopter heli = GameManager.server.CreateEntity(_config.assetPrefab, position, GetFixedRotationForPlayer(player)) as ScrapTransportHelicopter;
if (heli == null) return;
heli.OwnerID = player.userID;
heli.startHealth = _config.spawnHealth;
heli.Spawn();
// Credit Original MyScrapTransportHelicopter Plugin
if (permission.UserHasPermission(player.UserIDString, _noFuel))
EnableUnlimitedFuel(heli);
else
AddInitialFuel(heli, player.UserIDString);
_data.playerheli.Add(player.UserIDString, heli.net.ID.Value);
if (!permission.UserHasPermission(player.UserIDString, _noCooldown))
{
_data.spawnCooldowns[player.UserIDString] = DateTime.Now;
}
}
private void GiveScrapTransportHelicopter (BasePlayer player, Vector3 customPosition = default(Vector3), bool useCustomPosition = false)
{
if (FindPlayerheli(player) != null)
{
player.ChatMessage(lang.GetMessage("heli_current", this, player.UserIDString));
return;
}
var position = useCustomPosition ? customPosition : GetFixedPositionForPlayer(player);
var rotation = useCustomPosition ? Quaternion.identity : GetFixedRotationForPlayer(player);
ScrapTransportHelicopter heli = GameManager.server.CreateEntity(_config.assetPrefab, position, GetFixedRotationForPlayer(player)) as ScrapTransportHelicopter;
if (heli == null) return;
heli.OwnerID = player.userID;
heli.Spawn();
_data.playerheli.Add(player.UserIDString, heli.net.ID.Value);
if (permission.UserHasPermission(player.UserIDString, _noFuel))
EnableUnlimitedFuel(heli);
else
AddInitialFuel(heli, player.UserIDString);
}
private float GetPlayerCooldownSeconds(BasePlayer player, Dictionary<string, float> cooldownPerms, float defaultCooldown)
{
var grantedCooldownPerms = cooldownPerms
.Where(entry => permission.UserHasPermission(player.UserIDString, entry.Key));
return grantedCooldownPerms.Any()
? grantedCooldownPerms.Min(entry => entry.Value)
: defaultCooldown;
}
private void AddInitialFuel(ScrapTransportHelicopter ScrapTransportHelicopter, string userId)
{
var fuelAmount = GetPlayerAllowedFuel(userId);
if (fuelAmount == 0)
return;
StorageContainer fuelContainer = ScrapTransportHelicopter.GetFuelSystem().GetFuelContainer();
if (fuelAmount < 0)
{
// Value of -1 is documented to represent max stack size
fuelAmount = fuelContainer.allowedItem.stackable;
}
fuelContainer.inventory.AddItem(fuelContainer.allowedItem, fuelAmount);
}
private void EnableUnlimitedFuel(ScrapTransportHelicopter ScrapTransportHelicopter)
{
var fuelSystem = ScrapTransportHelicopter.GetFuelSystem();
fuelSystem.cachedHasFuel = true;
fuelSystem.nextFuelCheckTime = float.MaxValue;
fuelSystem.GetFuelContainer().SetFlag(BaseEntity.Flags.Locked, true);
}
private float GetDistance(BasePlayer player, ScrapTransportHelicopter heli)
{
float distance = Vector3.Distance(player.transform.position, heli.transform.position);
return distance;
}
private bool IsPlayerOwned(ScrapTransportHelicopter heli)
{
if (heli != null && _data.playerheli.ContainsValue(heli.net.ID.Value))
return true;
return false;
}
private string GetFuelPermission(int fuelAmount) => String.Format(_permissionFuelFormat, fuelAmount);
#endregion
#region Data & Configuration
private int GetPlayerAllowedFuel(string userIdString)
{
if (_config.fuelAmountsRequiringPermission == null || _config.fuelAmountsRequiringPermission.Length == 0)
return _config.fuelAmount;
for (var i = _config.fuelAmountsRequiringPermission.Length - 1; i >= 0; i--)
{
var fuelAmount = _config.fuelAmountsRequiringPermission[i];
if (permission.UserHasPermission(userIdString, String.Format(_permissionFuelFormat, fuelAmount)))
return fuelAmount;
}
return _config.fuelAmount;
}
private SaveData LoadSaveData()
{
_data = Interface.Oxide.DataFileSystem.ReadObject<SaveData>(Name);
if (_data == null)
{
PrintWarning($"Data file {Name}.json is invalid. Creating new data file.");
_data = new SaveData();
WriteSaveData();
}
return _data;
}
private void WriteSaveData() =>
Interface.Oxide.DataFileSystem.WriteObject(Name, _data);
class SaveData
{
[JsonProperty("playerheli")]
public Dictionary<string, ulong> playerheli = new Dictionary<string, ulong>();
[JsonProperty("spawnCooldowns")]
public Dictionary<string, DateTime> spawnCooldowns = new Dictionary<string, DateTime>();
[JsonProperty("cooldown")]
private Dictionary<string, DateTime> deprecatedCooldown
{
set { spawnCooldowns = value; }
}
[JsonProperty("fetchCooldowns")]
public Dictionary<string, DateTime> fetchCooldowns = new Dictionary<string, DateTime>();
}
class PluginConfig : SerializableConfiguration
{
[JsonProperty("AssetPrefab")]
public string assetPrefab = "assets/content/vehicles/scrap heli carrier/scraptransporthelicopter.prefab";
[JsonProperty("CanDespawnWhileOccupied")]
public bool canDespawnWhileOccupied = false;
[JsonProperty("CanFetchWhileOccupied")]
public bool canFetchWhileOccupied = false;
[JsonProperty("CanSpawnBuildingBlocked")]
public bool canSpawnBuildingBlocked = false;
[JsonProperty("CanFetchBuildingBlocked")]
public bool canFetchBuildlingBlocked = true;
[JsonProperty("AutoFetch")]
public bool autoFetch = false;
[JsonProperty("RepairOnFetch")]
public bool repairOnFetch = false;
[JsonProperty("FuelAmount")]
public int fuelAmount = 0;
[JsonProperty("FuelAmountsRequiringPermission")]
public int[] fuelAmountsRequiringPermission = new int[0];
[JsonProperty("MaxNoHeliDistance")]
public float noHeliDistance = -1;
[JsonProperty("MaxSpawnDistance")]
public float maxSpawnDistance = 5f;
[JsonProperty("UseFixedSpawnDistance")]
public bool useFixedSpawnDistance = true;
[JsonProperty("FixedSpawnDistance")]
public float fixedSpawnDistance = 3;
[JsonProperty("FixedSpawnRotationAngle")]
public float fixedSpawnRotationAngle = 135;
[JsonProperty("OwnerAndTeamCanMount")]
public bool ownerOnly = false;
[JsonProperty("DefaultSpawnCooldown")]
public float defaultSpawnCooldown = 3600f;
[JsonProperty("DefaultCooldown")]
private float deprecatedDefaultSpawnCooldown
{
set { defaultSpawnCooldown = value; }
}
[JsonProperty("PermissionSpawnCooldowns", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, float> spawnPermissionCooldowns = new Dictionary<string, float>()
{
["Spawnheli.tier1"] = 600f,
["Spawnheli.tier2"] = 300f,
["Spawnheli.tier3"] = 60f,
};
[JsonProperty("PermissionCooldowns")]
private Dictionary<string, float> deprecatedSpawnPermissionCooldowns
{
set { spawnPermissionCooldowns = value; }
}
[JsonProperty("DefaultFetchCooldown")]
public float defaultFetchCooldown = 0;
[JsonProperty("PermissionFetchCooldowns", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, float> fetchPermissionCooldowns = new Dictionary<string, float>()
{
["Spawnheli.fetch.tier1"] = 600f,
["Spawnheli.fetch.tier2"] = 300f,
["Spawnheli.fetch.tier3"] = 60f,
};
[JsonProperty("SpawnHealth")]
public float spawnHealth = 750f;
[JsonProperty("DestroyOnDisconnect")]
public bool destroyOnDisconnect = false;
}
private PluginConfig GetDefaultConfig() => new PluginConfig();
#endregion
#region Localization
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["heli_destroyed"] = "Your ScrapTransportHelicopter has been destroyed!",
["heli_perm"] = "You do not have permission to use this command!",
["heli_current"] = "You already have a ScrapTransportHelicopter!",
["heli_notcurrent"] = "You do not have a ScrapTransportHelicopter!",
["heli_buildingblocked"] = "Нельзя вызывать в чужой зоне строительства!",
["heli_timeleft_new"] = "You have <color=red>{0}</color> until your cooldown ends",
["heli_sdistance"] = "You're trying to spawn the ScrapTransportHelicopter too far away!",
["heli_terrain"] = "Trying to spawn ScrapTransportHelicopter outside of terrain!",
["heli_mounted"] = "A player is currenty mounted on the ScrapTransportHelicopter!",
["heli_current_distance"] = "The ScrapTransportHelicopter is too far!",
["heli_canmount"] = "You are not the owner of this ScrapTransportHelicopter or in the owner's team!",
["heli_unlimited_fuel"] = "Не нуждается в заправке!",
["heli_location_restricted"] = "Не можете быть вызвать здесь!",
}, this, "en");
lang.RegisterMessages(new Dictionary<string, string>
{
["heli_destroyed"] = "Ваш Транспортный вертрлёт был уничтожен!",
["heli_perm"] = "У вас нет разрешения на использование этой команды!",
["heli_current"] = "У вас уже есть Транспортный вертрлёт!",
["heli_notcurrent"] = "У вас нет Транспортного вертрлёта!",
["heli_timeleft_new"] = "У вас есть <color=red>{0}</color>, пока ваше время восстановления не закончится.",
["heli_sdistance"] = "Вы пытаетесь вызвать Транспортный вертрлёт, но вы слишком далеко указываете!",
["heli_terrain"] = "Попытка вызвать Транспортный вертрлёт вне поверхности!",
["heli_mounted"] = "Игрок в данный момент сидит в Транспортном вертрлёте или вы слишком далеко!",
["heli_current_distance"] = "Транспортный вертрлёт слишком далеко!",
["heli_canmount"] = "Вы не являетесь владельцем этого Транспортного вертрлёта или не в команде владельца!"
}, this, "ru");
lang.RegisterMessages(new Dictionary<string, string>
{
["heli_destroyed"] = "Ihr helikopter wurde zerstört!",
["heli_perm"] = "Du habe keine keine berechtigung diesen befehl zu verwenden!",
["heli_current"] = "Du hast bereits einen helikopter!",
["heli_notcurrent"] = "Du hast keine helikopter!",
["heli_timeleft_new"] = "Du hast <color=red>{0}</color>, bis ihre abklingzeit ende",
["heli_sdistance"] = "Du bist versuchen den helikopter zu weit weg zu spawnen!",
["heli_terrain"] = "Du versucht laichen einen helikopter außerhalb des geländes!",
["heli_mounted"] = "Ein Spieler ist gerade am helikopter montiert oder es ist zu weit!",
["heli_current_distance"] = "Der helikopter ist zu weit!",
["heli_rcon"] = "Dieser Befehl kann nur von RCON ausgeführt werden!",
["heli_canmount"] = "Sie sind nicht der Besitzer dieses ScrapTransportHelicopter s oder im Team des Besitzers!"
}, this, "de");
}
#endregion
#region Configuration Boilerplate
internal class SerializableConfiguration
{
public string ToJson() => JsonConvert.SerializeObject(this);
public Dictionary<string, object> ToDictionary() => JsonHelper.Deserialize(ToJson()) as Dictionary<string, object>;
}
internal static class JsonHelper
{
public static object Deserialize(string json) => ToObject(JToken.Parse(json));
private static object ToObject(JToken token)
{
switch (token.Type)
{
case JTokenType.Object:
return token.Children<JProperty>()
.ToDictionary(prop => prop.Name,
prop => ToObject(prop.Value));
case JTokenType.Array:
return token.Select(ToObject).ToList();
default:
return ((JValue)token).Value;
}
}
}
private bool MaybeUpdateConfig(SerializableConfiguration config)
{
var currentWithDefaults = config.ToDictionary();
var currentRaw = Config.ToDictionary(x => x.Key, x => x.Value);
return MaybeUpdateConfigDict(currentWithDefaults, currentRaw);
}
private bool MaybeUpdateConfigDict(Dictionary<string, object> currentWithDefaults, Dictionary<string, object> currentRaw)
{
bool changed = false;
foreach (var key in currentWithDefaults.Keys)
{
object currentRawValue;
if (currentRaw.TryGetValue(key, out currentRawValue))
{
var defaultDictValue = currentWithDefaults[key] as Dictionary<string, object>;
var currentDictValue = currentRawValue as Dictionary<string, object>;
if (defaultDictValue != null)
{
// Don't update nested keys since the cooldown tiers might be customized
if (currentDictValue == null)
{
currentRaw[key] = currentWithDefaults[key];
changed = true;
}
}
}
else
{
currentRaw[key] = currentWithDefaults[key];
changed = true;
}
}
return changed;
}
protected override void LoadDefaultConfig() => _config = GetDefaultConfig();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
_config = Config.ReadObject<PluginConfig>();
if (_config == null)
{
throw new JsonException();
}
if (MaybeUpdateConfig(_config))
{
PrintWarning("Configuration appears to be outdated; updating and saving");
SaveConfig();
}
}
catch (Exception e)
{
PrintError(e.Message);
PrintWarning($"Configuration file {Name}.json is invalid; using defaults");
LoadDefaultConfig();
}
}
protected override void SaveConfig()
{
Puts($"Configuration changes saved to {Name}.json");
Config.WriteObject(_config, true);
}
#endregion
}
}