Иконка ресурса

FREE WipeRaidTimer : 2.0.0 2.0.0

Нет прав для скачивания
C#:
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Game.Rust.Cui;
using Rust;
using Rust.Ai.Gen2;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Wipe Raid Timer", "Sigilo", "2.0.0")]
    [Description("Protects cupboard-covered buildings and deployables from raiding for a configurable time after wipe")]
    public class WipeRaidTimer : RustPlugin
    {
        private const string BypassPermission = "wiperaidtimer.bypass";
        private const string UiTimerBarName = "wiperaidtimer.timerbar";

        private const string StatusBarBackgroundColor = "0.1 0.1 0.1 0.7";
        private const string StatusBarBorderColor = "0.3 0.3 0.3 0.7";
        private const string StatusBarFillColor = "0.7 0.2 0.2 0.9";
        private const string StatusBarTextColor = "1 1 1 0.9";
        private const string StatusBarAnchorMin = "0.10 0.03";
        private const string StatusBarAnchorMax = "0.28 0.05";

        private readonly HashSet<string> deployablePrefabPaths = new HashSet<string>(StringComparer.Ordinal);
        private readonly Dictionary<ulong, float> notificationTimes = new Dictionary<ulong, float>();
        private readonly Dictionary<uint, BuildingQualificationCacheEntry> buildingQualificationCache =
            new Dictionary<uint, BuildingQualificationCacheEntry>();

        private ConfigData configData;
        private Timer blockExpiryTimer;
        private Timer uiUpdateTimer;
        private bool blockActive;
        private bool serverInitialized;

        #region Oxide Hooks

        private void Init()
        {
            permission.RegisterPermission(BypassPermission, this);

            // Damage handling is enabled only while protection is active.
            Unsubscribe(nameof(OnEntityTakeDamage));
        }

        private void OnServerInitialized()
        {
            serverInitialized = true;
            BuildDeployablePrefabCache();
            RefreshBlockState();

            Puts($"Loaded with {deployablePrefabPaths.Count} player-deployable prefabs cached. " +
                 $"Raid protection is {(blockActive ? "active" : "inactive")}.");
        }

        private void OnNewSave(string filename)
        {
            if (!serverInitialized)
            {
                return;
            }

            NextTick(RefreshBlockState);
        }

        private void OnPlayerConnected(BasePlayer player)
        {
            if (player == null)
            {
                return;
            }

            NextTick(() => UpdateStatusBar(player));
        }

        private void OnPlayerDisconnected(BasePlayer player)
        {
            if (player == null)
            {
                return;
            }

            CuiHelper.DestroyUi(player, UiTimerBarName);
            notificationTimes.Remove(player.userID);
        }

        private object OnEntityTakeDamage(BaseEntity entity, HitInfo info)
        {
            if (!blockActive || entity == null || entity.IsDestroyed || info?.damageTypes == null)
            {
                return null;
            }

            float totalDamage = info.damageTypes.Total();
            if (totalDamage <= 0f || IsAllowedDecayDamage(info, totalDamage) || !IsProtectedTarget(entity))
            {
                return null;
            }

            BuildingPrivlidge privilege = FindCoveringPrivilege(entity);
            if (privilege == null || privilege.IsDestroyed)
            {
                return null;
            }

            if (!IsQualifiedProtectedBuilding(privilege))
            {
                return null;
            }

            if (configData.Protection.RequireDeployableOwnerAuthorization &&
                IsGenericPlayerDeployable(entity) &&
                !IsDeployableOwnerAuthorized(entity, privilege))
            {
                return null;
            }

            BasePlayer attacker;
            ulong attackerId = ResolveResponsiblePlayer(info, out attacker);

            if (attackerId.IsSteamId())
            {
                if (HasBypassPermission(attackerId, attacker))
                {
                    return null;
                }

                if (configData.Protection.AllowCupboardAuthorizedDamage &&
                    ((configData.Protection.AllowCupboardOwnerDamage && privilege.OwnerID == attackerId) ||
                     privilege.IsAuthed(attackerId)))
                {
                    return null;
                }
            }
            else if (!configData.Protection.BlockUnattributedDamage)
            {
                return null;
            }

            info.damageTypes.Clear();
            info.DidHit = false;
            info.DoHitEffects = false;

            NotifyBlockedAttacker(attacker, entity, privilege, totalDamage);
            return true;
        }

        private void Unload()
        {
            blockActive = false;
            Unsubscribe(nameof(OnEntityTakeDamage));

            blockExpiryTimer?.Destroy();
            blockExpiryTimer = null;

            uiUpdateTimer?.Destroy();
            uiUpdateTimer = null;

            notificationTimes.Clear();
            buildingQualificationCache.Clear();
            DestroyAllStatusBars();
        }

        #endregion

        #region Protection Lifecycle

        private void RefreshBlockState()
        {
            int remainingSeconds = GetRemainingBlockTime();
            if (remainingSeconds > 0)
            {
                ActivateProtection(remainingSeconds);
            }
            else
            {
                DeactivateProtection();
            }
        }

        private void ActivateProtection(int remainingSeconds)
        {
            bool wasActive = blockActive;
            blockActive = true;
            Subscribe(nameof(OnEntityTakeDamage));

            blockExpiryTimer?.Destroy();
            blockExpiryTimer = timer.Once(Math.Max(1f, remainingSeconds + 0.25f), RefreshBlockState);

            if (!wasActive)
            {
                StartUiUpdates();
            }

            UpdateAllStatusBars();
        }

        private void DeactivateProtection()
        {
            bool wasActive = blockActive;
            blockActive = false;
            Unsubscribe(nameof(OnEntityTakeDamage));

            blockExpiryTimer?.Destroy();
            blockExpiryTimer = null;

            uiUpdateTimer?.Destroy();
            uiUpdateTimer = null;

            notificationTimes.Clear();
            buildingQualificationCache.Clear();
            DestroyAllStatusBars();

            if (wasActive)
            {
                Puts("Wipe raid protection has expired.");
            }
        }

        private void StartUiUpdates()
        {
            uiUpdateTimer?.Destroy();
            uiUpdateTimer = null;

            if (!configData.StatusBar.Enabled)
            {
                return;
            }

            uiUpdateTimer = timer.Every(configData.StatusBar.RefreshIntervalSeconds, UpdateAllStatusBars);
        }

        #endregion

        #region Target Classification and Privilege

        private void BuildDeployablePrefabCache()
        {
            deployablePrefabPaths.Clear();

            foreach (ItemDefinition definition in ItemManager.GetItemDefinitions())
            {
                if (!definition.TryGetComponent(out ItemModDeployable deployable) || deployable?.entityPrefab == null)
                {
                    continue;
                }

                string prefabPath = deployable.entityPrefab.resourcePath;
                if (!string.IsNullOrEmpty(prefabPath))
                {
                    deployablePrefabPaths.Add(prefabPath);
                }
            }
        }

        private bool IsProtectedTarget(BaseEntity entity)
        {
            if (entity is BasePlayer || entity.IsNpc || entity is BaseNpc || entity is BaseNPC2 ||
                entity is ResourceEntity || entity is BaseVehicle || entity is RidableHorse ||
                HasVehicleParent(entity))
            {
                return false;
            }

            if (entity is BuildingBlock buildingBlock)
            {
                if (buildingBlock.grade == BuildingGrade.Enum.Twigs)
                {
                    return configData.Protection.ProtectTwigBuildingBlocks;
                }

                return configData.Protection.ProtectBuildingBlocks;
            }

            if (entity is Door)
            {
                return configData.Protection.ProtectDoors;
            }

            if (entity is BaseLock)
            {
                return configData.Protection.ProtectDoors || configData.Protection.ProtectPlayerDeployables;
            }

            if (entity is BuildingPrivlidge)
            {
                return configData.Protection.ProtectPlayerDeployables;
            }

            return configData.Protection.ProtectPlayerDeployables && IsGenericPlayerDeployable(entity);
        }

        private bool IsGenericPlayerDeployable(BaseEntity entity)
        {
            return !string.IsNullOrEmpty(entity.PrefabName) &&
                   deployablePrefabPaths.Contains(entity.PrefabName);
        }

        private bool IsDeployableOwnerAuthorized(BaseEntity entity, BuildingPrivlidge privilege)
        {
            BaseEntity current = entity;

            for (int depth = 0; current != null && depth < 4; depth++)
            {
                ulong ownerId = current.OwnerID;
                if (ownerId.IsSteamId())
                {
                    return IsOwnerAuthorized(ownerId, privilege);
                }

                current = current.GetParentEntity();
            }

            return false;
        }

        private bool IsQualifiedProtectedBuilding(BuildingPrivlidge privilege)
        {
            BuildingManager.Building building = privilege.GetBuilding();
            if (building == null)
            {
                return false;
            }

            float now = Time.realtimeSinceStartup;
            if (buildingQualificationCache.TryGetValue(building.ID, out BuildingQualificationCacheEntry cached) &&
                cached.ExpiresAt > now)
            {
                return cached.IsQualified;
            }

            bool isQualified = BuildingMeetsMinimumRequirements(building, privilege);
            if (buildingQualificationCache.Count >= 2048)
            {
                buildingQualificationCache.Clear();
            }

            buildingQualificationCache[building.ID] = new BuildingQualificationCacheEntry
            {
                IsQualified = isQualified,
                ExpiresAt = now + configData.BaseQualification.CacheSeconds
            };

            return isQualified;
        }

        private bool BuildingMeetsMinimumRequirements(
            BuildingManager.Building building,
            BuildingPrivlidge privilege)
        {
            int foundationCount = 0;
            foreach (BuildingBlock block in building.buildingBlocks)
            {
                if (block == null || block.IsDestroyed || block.grade == BuildingGrade.Enum.Twigs ||
                    !IsOwnerAuthorized(block.OwnerID, privilege) ||
                    !block.ShortPrefabName.StartsWith("foundation", StringComparison.Ordinal))
                {
                    continue;
                }

                if (++foundationCount >= configData.BaseQualification.MinimumNonTwigFoundations)
                {
                    break;
                }
            }

            if (foundationCount < configData.BaseQualification.MinimumNonTwigFoundations)
            {
                return false;
            }

            int doorCount = 0;
            foreach (Door door in building.doors)
            {
                if (door == null || door.IsDestroyed || !IsOwnerAuthorized(door.OwnerID, privilege))
                {
                    continue;
                }

                if (++doorCount >= configData.BaseQualification.MinimumDoors)
                {
                    return true;
                }
            }

            return doorCount >= configData.BaseQualification.MinimumDoors;
        }

        private bool IsOwnerAuthorized(ulong ownerId, BuildingPrivlidge privilege)
        {
            return ownerId.IsSteamId() &&
                   (privilege.OwnerID == ownerId || privilege.IsAuthed(ownerId));
        }

        private class BuildingQualificationCacheEntry
        {
            public bool IsQualified;
            public float ExpiresAt;
        }

        private bool HasVehicleParent(BaseEntity entity)
        {
            BaseEntity parent = entity.GetParentEntity();

            for (int depth = 0; parent != null && depth < 4; depth++)
            {
                if (parent is BaseVehicle || parent is RidableHorse)
                {
                    return true;
                }

                parent = parent.GetParentEntity();
            }

            return false;
        }

        private BuildingPrivlidge FindCoveringPrivilege(BaseEntity entity)
        {
            if (entity is BuildingPrivlidge cupboard && !cupboard.IsDestroyed)
            {
                return cupboard;
            }

            if (entity is DecayEntity decayEntity)
            {
                BuildingManager.Building building = decayEntity.GetBuilding();
                BuildingPrivlidge dominatingPrivilege = building?.GetDominatingBuildingPrivilege();

                if (dominatingPrivilege != null && !dominatingPrivilege.IsDestroyed)
                {
                    return dominatingPrivilege;
                }
            }

            BaseEntity boundsEntity = entity;
            if (entity is BaseLock)
            {
                BaseEntity parent = entity.GetParentEntity();
                if (parent != null)
                {
                    boundsEntity = parent;
                }
            }

            return boundsEntity.GetBuildingPrivilege(
                boundsEntity.WorldSpaceBounds(),
                true,
                configData.Protection.PrivilegeLookupCacheSeconds
            );
        }

        private bool IsAllowedDecayDamage(HitInfo info, float totalDamage)
        {
            if (!configData.Protection.AllowDecayDamage)
            {
                return false;
            }

            float decayDamage = info.damageTypes.Get(DamageType.Decay);
            return decayDamage > 0f && decayDamage >= totalDamage - 0.01f;
        }

        #endregion

        #region Damage Attribution

        private ulong ResolveResponsiblePlayer(HitInfo info, out BasePlayer player)
        {
            player = info.InitiatorPlayer;
            if (IsHumanPlayer(player))
            {
                return player.userID;
            }

            ulong userId = ResolvePlayerFromEntity(info.Initiator, out player);
            if (userId.IsSteamId())
            {
                return userId;
            }

            userId = ResolvePlayerFromEntity(info.Weapon, out player);
            if (userId.IsSteamId())
            {
                return userId;
            }

            return ResolvePlayerFromEntity(info.WeaponPrefab, out player);
        }

        private ulong ResolvePlayerFromEntity(BaseEntity source, out BasePlayer player)
        {
            player = null;
            BaseEntity current = source;

            for (int depth = 0; current != null && depth < 6; depth++)
            {
                if (current is BasePlayer directPlayer && IsHumanPlayer(directPlayer))
                {
                    player = directPlayer;
                    return directPlayer.userID;
                }

                if (current is BaseVehicle vehicle)
                {
                    BasePlayer driver = vehicle.GetDriver();
                    if (IsHumanPlayer(driver))
                    {
                        player = driver;
                        return driver.userID;
                    }
                }

                if (current.OwnerID.IsSteamId())
                {
                    player = BasePlayer.FindAwakeOrSleepingByID(current.OwnerID);
                    return current.OwnerID;
                }

                BaseEntity creator = current.creatorEntity;
                if (creator != null && creator != current)
                {
                    current = creator;
                    continue;
                }

                BaseEntity parent = current.GetParentEntity();
                if (parent == null || parent == current)
                {
                    break;
                }

                current = parent;
            }

            return 0UL;
        }

        private bool IsHumanPlayer(BasePlayer player)
        {
            return player != null && !player.IsNpc && player.userID.IsSteamId();
        }

        private bool HasBypassPermission(ulong userId, BasePlayer player)
        {
            return userId.IsSteamId() &&
                   ((configData.Protection.AllowAdminBypass && player != null && player.IsAdmin) ||
                    permission.UserHasPermission(userId.ToString(CultureInfo.InvariantCulture), BypassPermission));
        }

        #endregion

        #region Notifications

        private void NotifyBlockedAttacker(
            BasePlayer attacker,
            BaseEntity target,
            BuildingPrivlidge privilege,
            float attemptedDamage)
        {
            if ((!configData.Notifications.NotifyBlockedAttacker && !configData.DebugLogging) ||
                !IsHumanPlayer(attacker) || !attacker.IsConnected)
            {
                return;
            }

            float now = Time.realtimeSinceStartup;
            if (notificationTimes.TryGetValue(attacker.userID, out float lastNotification) &&
                now - lastNotification < configData.Notifications.CooldownSeconds)
            {
                return;
            }

            notificationTimes[attacker.userID] = now;

            if (configData.Notifications.NotifyBlockedAttacker)
            {
                attacker.ChatMessage(GetMessage(
                    "DamageBlocked",
                    attacker.UserIDString,
                    FormatTimeString(GetRemainingBlockTime())
                ));
            }

            if (configData.DebugLogging)
            {
                Puts($"Blocked {attemptedDamage:0.##} damage from {attacker.displayName} ({attacker.userID}) " +
                     $"to {target.ShortPrefabName} under cupboard {privilege.net?.ID.Value ?? 0UL}.");
            }
        }

        #endregion

        #region Status Bar UI

        private void UpdateAllStatusBars()
        {
            if (!blockActive || !configData.StatusBar.Enabled)
            {
                DestroyAllStatusBars();
                return;
            }

            foreach (BasePlayer player in BasePlayer.activePlayerList)
            {
                UpdateStatusBar(player);
            }
        }

        private void UpdateStatusBar(BasePlayer player)
        {
            if (player == null || !player.IsConnected)
            {
                return;
            }

            if (!blockActive || !configData.StatusBar.Enabled || HasBypassPermission(player.userID, player))
            {
                CuiHelper.DestroyUi(player, UiTimerBarName);
                return;
            }

            int remainingTime = GetRemainingBlockTime();
            if (remainingTime <= 0)
            {
                CuiHelper.DestroyUi(player, UiTimerBarName);
                return;
            }

            float progress = configData.BlockTimeAfterWipe > 0
                ? Mathf.Clamp01((float)remainingTime / configData.BlockTimeAfterWipe)
                : 0f;

            string progressAnchor = progress.ToString("0.####", CultureInfo.InvariantCulture);
            string label = GetMessage(
                "TimerLabel",
                player.UserIDString,
                FormatTimeString(remainingTime)
            );

            CuiElementContainer elements = new CuiElementContainer();

            elements.Add(new CuiElement
            {
                Name = UiTimerBarName,
                Parent = "Hud",
                Components =
                {
                    new CuiImageComponent { Color = StatusBarBackgroundColor },
                    new CuiRectTransformComponent
                    {
                        AnchorMin = StatusBarAnchorMin,
                        AnchorMax = StatusBarAnchorMax
                    },
                    new CuiOutlineComponent
                    {
                        Color = StatusBarBorderColor,
                        Distance = "1 1"
                    }
                }
            });

            elements.Add(new CuiElement
            {
                Name = $"{UiTimerBarName}.fill",
                Parent = UiTimerBarName,
                Components =
                {
                    new CuiImageComponent { Color = StatusBarFillColor },
                    new CuiRectTransformComponent
                    {
                        AnchorMin = "0 0",
                        AnchorMax = $"{progressAnchor} 1"
                    }
                }
            });

            elements.Add(new CuiElement
            {
                Name = $"{UiTimerBarName}.text",
                Parent = UiTimerBarName,
                Components =
                {
                    new CuiTextComponent
                    {
                        Text = label,
                        Align = TextAnchor.MiddleCenter,
                        FontSize = 12,
                        Color = StatusBarTextColor,
                        Font = "robotocondensed-regular.ttf"
                    },
                    new CuiRectTransformComponent
                    {
                        AnchorMin = "0 0",
                        AnchorMax = "1 1"
                    }
                }
            });

            CuiHelper.DestroyUi(player, UiTimerBarName);
            CuiHelper.AddUi(player, elements);
        }

        private void DestroyAllStatusBars()
        {
            foreach (BasePlayer player in BasePlayer.activePlayerList)
            {
                if (player != null)
                {
                    CuiHelper.DestroyUi(player, UiTimerBarName);
                }
            }
        }

        #endregion

        #region Time and Messages

        private int GetRemainingBlockTime()
        {
            if (configData == null || configData.BlockTimeAfterWipe <= 0)
            {
                return 0;
            }

            double elapsedSeconds = Math.Max(0d, (DateTime.UtcNow - SaveRestore.SaveCreatedTime).TotalSeconds);
            return Math.Max(0, (int)Math.Ceiling(configData.BlockTimeAfterWipe - elapsedSeconds));
        }

        private string FormatTimeString(int seconds)
        {
            seconds = Math.Max(0, seconds);
            List<string> components = new List<string>(4);

            int days = seconds / 86400;
            seconds %= 86400;
            if (days > 0)
            {
                components.Add($"{days}d");
            }

            int hours = seconds / 3600;
            seconds %= 3600;
            if (hours > 0)
            {
                components.Add($"{hours}h");
            }

            int minutes = seconds / 60;
            seconds %= 60;
            if (minutes > 0)
            {
                components.Add($"{minutes}m");
            }

            if (seconds > 0 || components.Count == 0)
            {
                components.Add($"{seconds}s");
            }

            return string.Join(" ", components);
        }

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["DamageBlocked"] = "This structure is protected from raiding for <color=red>{0}</color>.",
                ["TimerLabel"] = "Raid Protection: {0}"
            }, this);
        }

        private string GetMessage(string key, string playerId, params object[] args)
        {
            return string.Format(lang.GetMessage(key, this, playerId), args);
        }

        #endregion

        #region Configuration

        private class ConfigData
        {
            [JsonProperty("Block Time After Wipe (seconds)")]
            public int BlockTimeAfterWipe { get; set; } = 86400;

            [JsonProperty("Protection")]
            public ProtectionSettings Protection { get; set; } = new ProtectionSettings();

            [JsonProperty("Base Qualification")]
            public BaseQualificationSettings BaseQualification { get; set; } = new BaseQualificationSettings();

            [JsonProperty("Blocked Hit Notifications")]
            public NotificationSettings Notifications { get; set; } = new NotificationSettings();

            [JsonProperty("Status Bar")]
            public StatusBarSettings StatusBar { get; set; } = new StatusBarSettings();

            [JsonProperty("Debug Logging")]
            public bool DebugLogging { get; set; }
        }

        private class ProtectionSettings
        {
            [JsonProperty("Protect Building Blocks")]
            public bool ProtectBuildingBlocks { get; set; } = true;

            [JsonProperty("Protect Twig Building Blocks")]
            public bool ProtectTwigBuildingBlocks { get; set; } = false;

            [JsonProperty("Protect Doors")]
            public bool ProtectDoors { get; set; } = true;

            [JsonProperty("Protect Player Deployables")]
            public bool ProtectPlayerDeployables { get; set; } = true;

            [JsonProperty("Require Deployable Owner Authorization")]
            public bool RequireDeployableOwnerAuthorization { get; set; } = true;

            [JsonProperty("Allow Cupboard Authorized Damage")]
            public bool AllowCupboardAuthorizedDamage { get; set; } = true;

            [JsonProperty("Allow Cupboard Owner Damage")]
            public bool AllowCupboardOwnerDamage { get; set; } = true;

            [JsonProperty("Allow Admin Bypass")]
            public bool AllowAdminBypass { get; set; } = true;

            [JsonProperty("Block Unattributed Damage")]
            public bool BlockUnattributedDamage { get; set; } = true;

            [JsonProperty("Allow Decay Damage")]
            public bool AllowDecayDamage { get; set; } = true;

            [JsonProperty("Privilege Lookup Cache Seconds")]
            public float PrivilegeLookupCacheSeconds { get; set; } = 2f;
        }

        private class BaseQualificationSettings
        {
            [JsonProperty("Minimum Non-Twig Foundations")]
            public int MinimumNonTwigFoundations { get; set; } = 2;

            [JsonProperty("Minimum Doors")]
            public int MinimumDoors { get; set; } = 1;

            [JsonProperty("Cache Seconds")]
            public float CacheSeconds { get; set; } = 5f;
        }

        private class NotificationSettings
        {
            [JsonProperty("Notify Blocked Attacker")]
            public bool NotifyBlockedAttacker { get; set; } = true;

            [JsonProperty("Cooldown Seconds")]
            public float CooldownSeconds { get; set; } = 5f;
        }

        private class StatusBarSettings
        {
            [JsonProperty("Enabled")]
            public bool Enabled { get; set; } = true;

            [JsonProperty("Refresh Interval Seconds")]
            public float RefreshIntervalSeconds { get; set; } = 10f;
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();

            try
            {
                JObject rawConfig = Config.ReadObject<JObject>() ?? new JObject();
                bool legacyConfig = rawConfig["Blocked Items (List of items blocked during wipe timer)"] != null ||
                                    rawConfig["Block Attack Helicopter"] != null ||
                                    rawConfig["Block MLRS"] != null ||
                                    rawConfig["Block Drones (Set drone.maxcontrolrange to 1 during raid block)"] != null;

                configData = rawConfig.ToObject<ConfigData>() ?? new ConfigData();

                if (legacyConfig)
                {
                    PrintWarning("Migrated legacy item-blocking configuration. Attack helicopters, raid items, " +
                                 "MLRS, and drones are no longer restricted by default.");
                }
            }
            catch (Exception exception)
            {
                PrintError($"Configuration is invalid; using defaults. {exception.Message}");
                configData = new ConfigData();
            }

            ValidateConfig();
            SaveConfig();
        }

        private void ValidateConfig()
        {
            configData ??= new ConfigData();
            configData.Protection ??= new ProtectionSettings();
            configData.BaseQualification ??= new BaseQualificationSettings();
            configData.Notifications ??= new NotificationSettings();
            configData.StatusBar ??= new StatusBarSettings();

            configData.BlockTimeAfterWipe = Math.Max(0, configData.BlockTimeAfterWipe);
            configData.Protection.PrivilegeLookupCacheSeconds = Mathf.Clamp(
                configData.Protection.PrivilegeLookupCacheSeconds,
                0.1f,
                10f
            );
            configData.BaseQualification.MinimumNonTwigFoundations = Math.Max(
                1,
                configData.BaseQualification.MinimumNonTwigFoundations
            );
            configData.BaseQualification.MinimumDoors = Math.Max(
                1,
                configData.BaseQualification.MinimumDoors
            );
            configData.BaseQualification.CacheSeconds = Mathf.Clamp(
                configData.BaseQualification.CacheSeconds,
                1f,
                30f
            );
            configData.Notifications.CooldownSeconds = Mathf.Clamp(
                configData.Notifications.CooldownSeconds,
                0.5f,
                60f
            );
            configData.StatusBar.RefreshIntervalSeconds = Mathf.Clamp(
                configData.StatusBar.RefreshIntervalSeconds,
                1f,
                60f
            );
        }

        protected override void LoadDefaultConfig()
        {
            configData = new ConfigData();
        }

        protected override void SaveConfig()
        {
            Config.WriteObject(configData, true);
        }

        #endregion

        #region API

        private Dictionary<string, int> API_GetRemainingBlockTime()
        {
            return new Dictionary<string, int>
            {
                ["RemainingSeconds"] = GetRemainingBlockTime()
            };
        }

        #endregion
    }
}

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху