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

FREE GuardedCrate : 2.0.13 2.0.13

Нет прав для скачивания
C#:
/*
 * Copyright (c) 2025 Bazz3l
 * 
 * Guarded Crate cannot be copied, edited and/or (re)distributed without the express permission of Bazz3l.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * 
 */
using System.Collections.Generic;
using System.Globalization;
using System.Collections;
using System.Text;
using System.Linq;
using System;
using System.Diagnostics;
using Oxide.Core.Plugins;
using Oxide.Core;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using GuardedCrateExt;
using UnityEngine;
using Rust;
namespace Oxide.Plugins {
    [Info("Guarded Crate", "Bazz3l", "2.0.13")]
    [Description("Spawn custom locked crate events guarded by scientists, eliminate the threats to gain high value loot before the time runs out.")]
    internal class GuardedCrate : RustPlugin {
        [PluginReference]
        private Plugin NpcSpawn, Clans, ZoneManager, HackableLock, GUIAnnouncements = null;
#region Fields
        private const string PERM_USE = "guardedcrate.use";
        private const string MARKER_PREFAB = "assets/prefabs/tools/map/genericradiusmarker.prefab";
        private const string CRATE_PREFAB = "assets/prefabs/deployable/chinooklockedcrate/codelockedhackablecrate.prefab";
        private const string PLANE_PREFAB = "assets/prefabs/npc/cargo plane/cargo_plane.prefab";
        private readonly object _objFalse = false;
        private readonly object _objTrue = true;
        private StoredData _storedData;
        private static ConfigData _configData;
        private static GuardedCrate _plugin;
        private static Func<string, string, string> _getMessage = null;
        private static Func<ulong, string> _tryFetchTag = null;
        private static Func<Vector3, JObject, ScientistNPC> _trySpawnNpc = null;
        private static Func<BasePlayer, HackableLockedCrate, object> _tryLockHackable = null;
        private static Func<string, string, string, BasePlayer, float, object> _sendGuiNotification = null;
#endregion
#region Config
        protected override void LoadDefaultConfig() {
            _configData = ConfigData.DefaultConfig();
            _configData.Version = Version;
        }
        protected override void LoadConfig() {
            base.LoadConfig();
            try {
                _configData = Config.ReadObject<ConfigData>();
                if (_configData == null) throw new JsonException();
                bool hasChanged = false;
                if (string.IsNullOrEmpty(_configData.CommandName)) {
                    _configData.CommandName = "gcrate";
                    _configData.Version = Version;
                    hasChanged = true;
                }
                if (_configData.AutoStartDuration <= 0.0f) {
                    _configData.AutoStartDuration = 1800f;
                    _configData.Version = Version;
                    hasChanged = true;
                }
                if (_configData.MessageSettings == null) {
                    _configData.MessageSettings = ConfigData.GetDefaultMessageSettings();
                    _configData.Version = Version;
                    hasChanged = true;
                }
                if (_configData.ZoneManagerSettings == null) {
                    _configData.ZoneManagerSettings = ConfigData.GetDefaultZoneManagerSettings();
                    _configData.Version = Version;
                    hasChanged = true;
                }
                if (_configData.Events == null || _configData.Events.Count == 0) {
                    _configData.Events = ConfigData.GetDefaultEvents();
                    _configData.Version = Version;
                    hasChanged = true;
                }
                if (_configData.BlockedTopologies == null) {
                    _configData.BlockedTopologies = ConfigData.GetDefaultTopologies();
                    _configData.Version = Version;
                    hasChanged = true;
                }
                if (_configData.EventSelectionSettings == null) {
                    _configData.EventSelectionSettings = ConfigData.GetDefaultEventSelectionSettings();
                    _configData.Version = Version;
                    hasChanged = true;
                }
                _configData.EventSelectionSettings ??= ConfigData.GetDefaultEventSelectionSettings();
                _configData.Events ??= ConfigData.GetDefaultEvents();
                _configData.EventSelectionSettings.EnsureEntries(_configData.Events.Select(x => x.EventName));
                if (hasChanged) {
                    SaveConfig();
                    PrintWarning("Config has been updated.");
                }
            }
            catch (Exception e) {
                LoadDefaultConfig();
                PrintWarning("Loaded default config.");
            }
        }
        protected override void SaveConfig() => Config.WriteObject(_configData, true);
        private class ConfigData {
            [JsonProperty("CommandName: used to manage and setup events.")]
            public string CommandName;
            [JsonProperty("EnableAutoStart: enabling auto start will spawn events at a specified duration.")]
            public bool AutoStartEnabled;
            [JsonProperty("AutoStartDuration: auto start duration specify in seconds.")]
            public float AutoStartDuration;
            [JsonProperty("MessageSettings: message notification settings")]
            public MessageSettings MessageSettings;
            [JsonProperty("ZoneManagerSettings: zone manager settings")]
            public ZoneMangerSettings ZoneManagerSettings;
            [JsonConverter(typeof(EnumCollectionStringConverter<TerrainBiome.Enum>))]
            [JsonProperty("BlockedTopologies: specifying topologies will prevent events spawning in these areas")]
            public List<TerrainBiome.Enum> BlockedTopologies;
            [JsonProperty("EventSelectionSettings: controls which event tiers can be auto-selected and how often")]
            public EventSelectionSettings EventSelectionSettings;
            [JsonProperty("Events: full event profiles and guard tuning")]
            public List<EventEntry> Events;
            [JsonProperty("VersionNumber: current version of the plugin, do not change this!!!")]
            public VersionNumber? Version;
            public static ConfigData DefaultConfig() {
                return new ConfigData {
                    CommandName = "gcrate", AutoStartEnabled = true, AutoStartDuration = 3600, MessageSettings = ConfigData.GetDefaultMessageSettings(), ZoneManagerSettings = ConfigData.GetDefaultZoneManagerSettings(), BlockedTopologies = ConfigData.GetDefaultTopologies(), EventSelectionSettings = ConfigData.GetDefaultEventSelectionSettings(), Events = ConfigData.GetDefaultEvents(),
                }
                ;
            }
            public static MessageSettings GetDefaultMessageSettings() {
                return new MessageSettings {
                    EnableToast = false, EnableChat = true, EnableChatPrefix = true, ChatIcon = 76561199542824781, EnableGuiAnnouncements = false, GuiAnnouncementsBgColor = "Purple", GuiAnnouncementsTextColor = "White"
                }
                ;
            }
            public static ZoneMangerSettings GetDefaultZoneManagerSettings() {
                return new ZoneMangerSettings {
                    EnabledIgnoredZones = false, IgnoredZones = new List<string>()
                }
                ;
            }
            public static List<TerrainBiome.Enum> GetDefaultTopologies() {
                return new List<TerrainBiome.Enum> {
                    TerrainBiome.Enum.Arctic, TerrainBiome.Enum.Jungle
                }
                ;
            }
            public static EventSelectionSettings GetDefaultEventSelectionSettings() {
                return new EventSelectionSettings {
                    UseWeightedRandomSelection = true, EventWeights = new List<EventWeightEntry> {
                        new() {
                            EventName = "Easy", Weight = 50f, Enabled = true
                        }
                        , new() {
                            EventName = "Medium", Weight = 30f, Enabled = true
                        }
                        , new() {
                            EventName = "Hard", Weight = 15f, Enabled = true
                        }
                        , new() {
                            EventName = "Elite", Weight = 5f, Enabled = true
                        }
                        ,
                    }
                }
                ;
            }
            public static List<EventEntry> GetDefaultEvents() {
                return DefaultEvents() .Select(defaultEvent => CreateDefaultEvent(defaultEvent.Name, defaultEvent.HackSeconds, defaultEvent.MarkerColor, defaultEvent.Guards, defaultEvent.Weapon, defaultEvent.Health)) .ToList();
            }
        }
        private class EventSelectionSettings {
            [JsonProperty("use weighted random selection for automatic event picks")]
            public bool UseWeightedRandomSelection;
            [JsonProperty("event weight entries")]
            public List<EventWeightEntry> EventWeights;
            [JsonIgnore]
            public bool HasValidWeights => EventWeights != null && EventWeights.Count > 0 && EventWeights.Any(x => x != null && x.Enabled && x.Weight > 0f);
            public void EnsureEntries(IEnumerable<string> eventNames) {
                EventWeights ??= new List<EventWeightEntry>();
                foreach (string eventName in eventNames) {
                    if (string.IsNullOrWhiteSpace(eventName)) continue;
                    if (EventWeights.Any(x => x != null && x.EventName.Equals(eventName, StringComparison.OrdinalIgnoreCase))) continue;
                    EventWeights.Add(new EventWeightEntry {
                        EventName = eventName, Weight = 1f, Enabled = true
                    }
                    );
                }
            }
        }
        private class EventWeightEntry {
            [JsonProperty("event name")]
            public string EventName;
            [JsonProperty("selection weight")]
            public float Weight;
            [JsonProperty("enabled")]
            public bool Enabled;
        }
        private class GuardSpawnSettings {
            [JsonProperty("use random guard amount between min and max")]
            public bool UseRandomAmount;
            [JsonProperty("minimum guard amount")]
            public int MinAmount;
            [JsonProperty("maximum guard amount")]
            public int MaxAmount;
            public void Normalize(int fallbackAmount = 1) {
                if (fallbackAmount < 1) fallbackAmount = 1;
                if (MinAmount <= 0 && MaxAmount <= 0) {
                    MinAmount = fallbackAmount;
                    MaxAmount = fallbackAmount;
                }
                if (MinAmount <= 0) MinAmount = MaxAmount > 0 ? MaxAmount : fallbackAmount;
                if (MaxAmount <= 0) MaxAmount = MinAmount > 0 ? MinAmount : fallbackAmount;
                if (MinAmount > MaxAmount) (MinAmount, MaxAmount) = (MaxAmount, MinAmount);
                if (MinAmount < 1) MinAmount = 1;
                if (MaxAmount < 1) MaxAmount = 1;
            }
            public int GetSpawnAmount() {
                Normalize();
                return UseRandomAmount ? UnityEngine.Random.Range(MinAmount, MaxAmount + 1) : MaxAmount;
            }
            public static GuardSpawnSettings CreateFixed(int amount) {
                amount = Mathf.Max(1, amount);
                return new GuardSpawnSettings {
                    UseRandomAmount = false, MinAmount = amount, MaxAmount = amount
                }
                ;
            }
        }
        private class MessageSettings {
            [JsonProperty("enable toast message")]
            public bool EnableToast;
            [JsonProperty("enable chat message")]
            public bool EnableChat;
            [JsonProperty("enable chat prefix")]
            public bool EnableChatPrefix;
            [JsonProperty("custom chat message icon (steam64)")]
            public ulong ChatIcon;
            [JsonProperty("enable gui announcements plugin from umod.org")]
            public bool EnableGuiAnnouncements;
            [JsonProperty("gui announcements text color")]
            public string GuiAnnouncementsTextColor;
            [JsonProperty("gui announcements background color")]
            public string GuiAnnouncementsBgColor;
        }
        private class ZoneMangerSettings {
            [JsonProperty("enable ignored zones")]
            public bool EnabledIgnoredZones;
            [JsonProperty("ignore these zone ids, leave empty to exclude all zones")]
            public List<string> IgnoredZones;
        }
        private static List<EventEntry> CloneEvents(IEnumerable<EventEntry> source) {
            return source?.Select(CloneEventEntry).Where(x => x != null).ToList() ?? new List<EventEntry>();
        }
        private static EventEntry CloneEventEntry(EventEntry source) {
            if (source == null) return null;
            return new EventEntry {
                EventName = source.EventName, EventDuration = source.EventDuration, EnableLockToPlayer = source.EnableLockToPlayer, EnableClanTag = source.EnableClanTag, EnableAutoHack = source.EnableAutoHack, HackableCrateHackSeconds = source.HackableCrateHackSeconds, HackableCrateFallDrag = source.HackableCrateFallDrag, EnableMarker = source.EnableMarker, MapMarkerColor1 = source.MapMarkerColor1, MapMarkerColor2 = source.MapMarkerColor2, MapMarkerRadius = source.MapMarkerRadius, MapMarkerOpacity = source.MapMarkerOpacity, EnableLootTable = source.EnableLootTable, LootMinAmount = source.LootMinAmount, LootMaxAmount = source.LootMaxAmount, EnableEliminateGuards = source.EnableEliminateGuards, GuardAmount = source.GuardAmount, GuardSpawnSettings = CloneGuardSpawnSettings(source.GuardSpawnSettings, source.GuardAmount), GuardConfig = CloneGuardConfig(source.GuardConfig), LootTable = source.LootTable?.Select(CloneItemEntry).ToList() ?? new List<ItemEntry>()
            }
            ;
        }
        private static ItemEntry CloneItemEntry(ItemEntry source) {
            if (source == null) return null;
            return new ItemEntry {
                DisplayName = source.DisplayName, Shortname = source.Shortname, SkinID = source.SkinID, MinAmount = source.MinAmount, MaxAmount = source.MaxAmount
            }
            ;
        }
        private static GuardSpawnSettings CloneGuardSpawnSettings(GuardSpawnSettings source, int fallbackAmount) {
            GuardSpawnSettings clone = source != null ? new GuardSpawnSettings {
                UseRandomAmount = source.UseRandomAmount, MinAmount = source.MinAmount, MaxAmount = source.MaxAmount
            }
            : GuardSpawnSettings.CreateFixed(fallbackAmount);
            clone.Normalize(fallbackAmount);
            return clone;
        }
        private static GuardConfig CloneGuardConfig(GuardConfig source) {
            if (source == null) return null;
            GuardConfig clone = new GuardConfig {
                Name = source.Name, WearItems = source.WearItems?.Select(x => new GuardConfig.WearEntry {
                    ShortName = x.ShortName, SkinID = x.SkinID
                }
                ).ToList() ?? new List<GuardConfig.WearEntry>(), BeltItems = source.BeltItems?.Select(x => new GuardConfig.BeltEntry {
                    ShortName = x.ShortName, SkinID = x.SkinID, Amount = x.Amount, Ammo = x.Ammo, Mods = x.Mods != null ? new List<string>(x.Mods) : new List<string>()
                }
                ).ToList() ?? new List<GuardConfig.BeltEntry>(), Kit = source.Kit, Health = source.Health, RoamRange = source.RoamRange, ChaseRange = source.ChaseRange, SenseRange = source.SenseRange, AttackRangeMultiplier = source.AttackRangeMultiplier, CheckVisionCone = source.CheckVisionCone, VisionCone = source.VisionCone, DamageScale = source.DamageScale, TurretDamageScale = source.TurretDamageScale, AimConeScale = source.AimConeScale, Speed = source.Speed, SleepDistance = source.SleepDistance, MemoryDuration = source.MemoryDuration, HostileTargetsOnly = source.HostileTargetsOnly, DisableRadio = source.DisableRadio, CanRunAwayWater = source.CanRunAwayWater, CanSleep = source.CanSleep, CanRaid = source.CanRaid, Stationary = source.Stationary, AboveOrUnderGround = source.AboveOrUnderGround
            }
            ;
            if (source.Parsed != null) clone.CacheConfig();
            return clone;
        }
        private void SyncConfiguredEventsToData(bool save = true) {
            if (_configData?.Events == null || _configData.Events.Count == 0) return;
            _storedData ??= new StoredData();
            _storedData.CrateEventEntries = CloneEvents(_configData.Events);
            if (_configData.EventSelectionSettings != null) {
                _configData.EventSelectionSettings.EnsureEntries(_storedData.EventNames);
                if (save) SaveConfig();
            }
            if (save) SaveData();
        }
        private void SyncEventToConfig(EventEntry sourceEvent, bool save = true) {
            if (sourceEvent == null || _configData?.Events == null) return;
            int index = _configData.Events.FindIndex(x => x != null && x.EventName.Equals(sourceEvent.EventName, StringComparison.OrdinalIgnoreCase));
            if (index < 0) {
                _configData.Events.Add(CloneEventEntry(sourceEvent));
            }
            else {
                _configData.Events[index] = CloneEventEntry(sourceEvent);
            }
            if (_configData.EventSelectionSettings != null) _configData.EventSelectionSettings.EnsureEntries(_configData.Events.Select(x => x.EventName));
            if (save) SaveConfig();
        }
#endregion
#region Storage
        private static (string Name, float HackSeconds, string MarkerColor, int Guards, string Weapon, float Health)[] DefaultEvents() {
            return new (string Name, float HackSeconds, string MarkerColor, int Guards, string Weapon, float Health)[] {
                ("Easy", 60f, "#32A844", 8, "smg.mp5", 200f), ("Medium", 120f, "#EDDF45", 10, "smg.mp5", 250f), ("Hard", 180f, "#3060D9", 12, "rifle.ak", 300f), ("Elite", 180f, "#3060D9", 20, "rifle.ak", 300f)
            }
            ;
        }
        private void LoadDefaultData() {
            _storedData = new StoredData();
            foreach (var defaultEvent in DefaultEvents()) {
                _storedData.CrateEventEntries.Add(CreateDefaultEvent(defaultEvent.Name, defaultEvent.HackSeconds, defaultEvent.MarkerColor, defaultEvent.Guards, defaultEvent.Weapon, defaultEvent.Health) );
            }
            _configData?.EventSelectionSettings?.EnsureEntries(_storedData.EventNames);
            SaveConfig();
            SaveData();
        }
        private void LoadData() {
            try {
                _storedData = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name);
                if (_storedData is not {
                    IsValid : true
                }
                ) throw new Exception();
                foreach (EventEntry eventEntry in _storedData.CrateEventEntries) eventEntry?.Normalize();
            }
            catch {
                _storedData = new StoredData();
                PrintWarning("Loaded default data.");
            }
            SyncConfiguredEventsToData();
        }
        private void SaveData() {
            if (_storedData is not {
                IsValid : true
            }
            ) return;
            Interface.Oxide.DataFileSystem.WriteObject(Name, _storedData);
        }
        private class StoredData {
            public List<EventEntry> CrateEventEntries = new();
            [JsonIgnore]
            private string[] _eventNames;
            [JsonIgnore]
            public string[] EventNames {
                get {
                    return _eventNames ??= CrateEventEntries .Select(x => x.EventName) .ToArray();
                }
            }
            [JsonIgnore]
            public bool IsValid => CrateEventEntries != null && CrateEventEntries.Count > 0;
            public EventEntry FindEventByName(string eventName) {
                return CrateEventEntries.Find(x => x.EventName.Equals(eventName, StringComparison.OrdinalIgnoreCase));
            }
        }
        private class EventEntry {
            [JsonProperty("event display name)")]
            public string EventName;
            [JsonProperty("event duration")]
            public float EventDuration;
            [JsonProperty("enable lock to player when completing the event")]
            public bool EnableLockToPlayer;
            [JsonProperty("enable clan tag")]
            public bool EnableClanTag;
            [JsonProperty("enable auto hacking of crate when an event is finished")]
            public bool EnableAutoHack;
            [JsonProperty("hackable locked crate")]
            public float HackableCrateHackSeconds;
            [JsonProperty("hackable crate fall drag")]
            public float HackableCrateFallDrag;
            [JsonProperty("enable marker")]
            public bool EnableMarker;
            [JsonProperty("marker color 1")]
            public string MapMarkerColor1;
            [JsonProperty("marker color 2")]
            public string MapMarkerColor2;
            [JsonProperty("marker radius")]
            public float MapMarkerRadius;
            [JsonProperty("marker opacity")]
            public float MapMarkerOpacity;
            [JsonProperty("enable loot table")]
            public bool EnableLootTable;
            [JsonProperty("min loot items")]
            public int LootMinAmount;
            [JsonProperty("max loot items")]
            public int LootMaxAmount;
            [JsonProperty("enable eliminate all guards before looting")]
            public bool EnableEliminateGuards;
            [JsonProperty("guard spawn amount")]
            public int GuardAmount;
            [JsonProperty("guard spawn settings")]
            public GuardSpawnSettings GuardSpawnSettings;
            [JsonProperty("guard spawn config")]
            public GuardConfig GuardConfig;
            public void Normalize() {
                if (GuardAmount < 1) GuardAmount = 1;
                GuardSpawnSettings ??= GuardedCrate.GuardSpawnSettings.CreateFixed(GuardAmount);
                GuardSpawnSettings.Normalize(GuardAmount);
                GuardAmount = GuardSpawnSettings.MaxAmount;
            }
            [JsonProperty("create loot items")]
            public List<ItemEntry> LootTable;
        }
        private class ItemEntry {
            public string DisplayName;
            public string Shortname;
            public ulong SkinID = 0UL;
            public int MinAmount;
            public int MaxAmount;
            public static List<ItemEntry> SaveItems(ItemContainer container) {
                List<ItemEntry> items = new List<ItemEntry>();
                foreach (Item item in container.itemList) {
                    items.Add(new ItemEntry {
                        DisplayName = item.name, Shortname = item.info.shortname, SkinID = item.skin, MinAmount = item.amount, MaxAmount = item.amount,
                    }
                    );
                }
                return items;
            }
            public Item CreateItem() {
                Item item = ItemManager.CreateByName(Shortname, UnityEngine.Random.Range(MinAmount, MaxAmount), SkinID);
                item.name = DisplayName;
                item.MarkDirty();
                return item;
            }
        }
        private class GuardConfig {
            public string Name;
            public List<WearEntry> WearItems;
            public List<BeltEntry> BeltItems;
            public string Kit;
            public float Health;
            public float RoamRange;
            public float ChaseRange;
            public float SenseRange;
            public float AttackRangeMultiplier;
            public bool CheckVisionCone;
            public float VisionCone;
            public float DamageScale;
            public float TurretDamageScale;
            public float AimConeScale;
            public float Speed;
            public float SleepDistance;
            public float MemoryDuration;
            public bool HostileTargetsOnly;
            public bool DisableRadio;
            public bool CanRunAwayWater;
            public bool CanSleep;
            public bool CanRaid;
            public bool Stationary;
            public bool AboveOrUnderGround;
            [JsonIgnore]
            public string[] Kits;
            [JsonIgnore]
            public JObject Parsed;
            public class BeltEntry {
                public string ShortName;
                public ulong SkinID;
                public int Amount;
                public string Ammo;
                public List<string> Mods;
                public static List<BeltEntry> SaveItems(ItemContainer container) {
                    List<BeltEntry> items = new List<BeltEntry>();
                    foreach (Item item in container.itemList) {
                        BeltEntry beltEntry = new BeltEntry {
                            ShortName = item.info.shortname, SkinID = item.skin, Amount = item.amount, Mods = new List<string>()
                        }
                        ;
                        if (item.GetHeldEntity() is BaseProjectile projectile && projectile?.primaryMagazine != null && projectile.primaryMagazine.ammoType != null) beltEntry.Ammo = projectile.primaryMagazine.ammoType.shortname;
                        if (item?.contents?.itemList != null) {
                            foreach (Item itemContent in item.contents.itemList) beltEntry.Mods.Add(itemContent.info.shortname);
                        }
                        items.Add(beltEntry);
                    }
                    return items;
                }
            }
            public class WearEntry {
                public string ShortName;
                public ulong SkinID;
                public static List<WearEntry> SaveItems(ItemContainer container) {
                    List<WearEntry> items = new List<WearEntry>();
                    foreach (Item item in container.itemList) {
                        items.Add(new WearEntry {
                            ShortName = item.info.shortname, SkinID = item.skin
                        }
                        );
                    }
                    return items;
                }
            }
            public void CacheConfig() {
                Kits = Kit?.Split(',') ?? Array.Empty<string>();
                Parsed = new JObject {
                    ["Name"]
                    = Name, ["WearItems"] = new JArray {
                        WearItems.Select(x => new JObject {
                            ["ShortName"]
                            = x.ShortName, ["SkinID"] = x.SkinID
                        }
                        )
                    }
                    , ["BeltItems"] = new JArray {
                        BeltItems.Select(x => new JObject {
                            ["ShortName"]
                            = x.ShortName, ["Amount"] = x.Amount, ["SkinID"] = x.SkinID, ["Mods"] = new JArray {
                                x.Mods
                            }
                            , ["Ammo"] = x.Ammo
                        }
                        )
                    }
                    , ["Kit"] = Kit, ["Health"] = Health, ["RoamRange"] = RoamRange, ["ChaseRange"] = ChaseRange, ["SenseRange"] = SenseRange, ["ListenRange"] = SenseRange / 2f, ["AttackRangeMultiplier"] = AttackRangeMultiplier, ["CheckVisionCone"] = CheckVisionCone, ["VisionCone"] = VisionCone, ["HostileTargetsOnly"] = HostileTargetsOnly, ["DamageScale"] = DamageScale, ["TurretDamageScale"] = TurretDamageScale, ["AimConeScale"] = AimConeScale, ["DisableRadio"] = DisableRadio, ["CanRunAwayWater"] = CanRunAwayWater, ["CanSleep"] = CanSleep, ["SleepDistance"] = SleepDistance, ["Speed"] = Speed, ["AreaMask"] = !AboveOrUnderGround ? 1 : 25, ["AgentTypeID"] = !AboveOrUnderGround ? -1372625422 : 0, ["HomePosition"] = string.Empty, ["MemoryDuration"] = MemoryDuration, ["States"] = new JArray {
                        Stationary ? new HashSet<string> {
                            "IdleState", "CombatStationaryState"
                        }
                        : CanRaid ? new HashSet<string> {
                            "RoamState", "CombatState", "ChaseState", "RaidState"
                        }
                        : new HashSet<string> {
                            "RoamState", "CombatState", "ChaseState"
                        }
                    }
                }
                ;
            }
        }
        private static EventEntry CreateDefaultEvent(string name, float hackSeconds, string markerColor, int guardAmount, string weaponShortName, float guardHealth) {
            return new EventEntry {
                EventName = name, EventDuration = 1800f, EnableAutoHack = true, HackableCrateHackSeconds = hackSeconds, HackableCrateFallDrag = 1f, EnableLockToPlayer = true, EnableClanTag = true, EnableMarker = true, MapMarkerColor1 = markerColor, MapMarkerColor2 = "#000000", MapMarkerOpacity = 0.6f, MapMarkerRadius = 0.7f, EnableLootTable = false, LootMinAmount = 6, LootMaxAmount = 10, LootTable = new List<ItemEntry>(), EnableEliminateGuards = true, GuardAmount = guardAmount, GuardSpawnSettings = GuardSpawnSettings.CreateFixed(guardAmount), GuardConfig = CreateDefaultGuard($"{name} Guard", weaponShortName, guardHealth)
            }
            ;
        }
        private static GuardConfig CreateDefaultGuard(string name, string shortname, float health) {
            return new GuardConfig {
                Name = name, WearItems = new List<GuardConfig.WearEntry> {
                    new() {
                        ShortName = "hazmatsuit_scientist_peacekeeper", SkinID = 0UL
                    }
                }
                , BeltItems = new List<GuardConfig.BeltEntry> {
                    new() {
                        ShortName = shortname, Amount = 1, SkinID = 0UL, Mods = new List<string>()
                    }
                    , new() {
                        ShortName = "syringe.medical", Amount = 10, SkinID = 0UL, Mods = new List<string>()
                    }
                }
                , Kit = "", Health = health, RoamRange = 5f, ChaseRange = 40f, SenseRange = 80f, AttackRangeMultiplier = 8f, VisionCone = 180f, CheckVisionCone = false, DamageScale = 1f, TurretDamageScale = 0.25f, AimConeScale = shortname == "rifle.ak" ? 0.15f : 0.25f, SleepDistance = 100f, Speed = 8.5f, MemoryDuration = 30f
            }
            ;
        }
#endregion
#region Lang
        protected override void LoadDefaultMessages() {
            lang.RegisterMessages(new Dictionary<string, string> {
                {
                    LangKeys.NoPermission, "Sorry you don't have permission to do that."
                }
                , {
                    LangKeys.Prefix, "<color=#8a916f>Guarded Crate</color>:\n"
                }
                , {
                    LangKeys.FailedToStartEvent, "Failed to start event."
                }
                , {
                    LangKeys.StartEvent, "Event starting."
                }
                , {
                    LangKeys.ClearEvents, "Cleaning up all running events."
                }
                , {
                    LangKeys.EventStart, "Special delivery is on its way to <color=#e7cf85>{0}</color> watch out it is heavily contested by guards, severity level <color=#e7cf85>{1}</color>.\nBe fast before the event ends in <color=#e7cf85>{2}</color>."
                }
                , {
                    LangKeys.EventCompleted, "<color=#e7cf85>{1}</color> has cleared the event at <color=#e7cf85>{0}</color>."
                }
                , {
                    LangKeys.EventEnded, "Event ended at <color=#e7cf85>{0}</color>; You were not fast enough; better luck next time!"
                }
                , {
                    LangKeys.EventNotFound, "Event not found, please make sure you have typed the correct name."
                }
                , {
                    LangKeys.EventIntersecting, "Another event is intersecting this position."
                }
                , {
                    LangKeys.EventPositionInvalid, "Event position invalid."
                }
                , {
                    LangKeys.EliminateGuards, "The crate is still contested eliminate all guards to gain access to high-valued loot."
                }
                , {
                    LangKeys.EventUpdated, "Event updated, please reload the plugin to take effect."
                }
                , {
                    LangKeys.InvalidGuardAmount, "Invalid guard amount must be between {0} - {1}."
                }
                , {
                    LangKeys.HelpStartEvent, "<color=#e7cf85>/{0}</color> start \"<color=#e7cf85><{1}></color>\", start an event of a specified type."
                }
                , {
                    LangKeys.HelpStopEvent, "<color=#e7cf85>/{0}</color> stop, stop all currently running events.\n\n"
                }
                , {
                    LangKeys.HelpHereEvent, "<color=#e7cf85>/{0}</color> here \"<color=#e7cf85><event-name></color>\", start an event at your position\n\n"
                }
                , {
                    LangKeys.HelpPositionEvent, "<color=#e7cf85>/{0}</color> position \"<color=#e7cf85><event-name></color>\" \"<color=#e7cf85>x y z</color>\", start an event at a specified position.\n\n"
                }
                , {
                    LangKeys.HelpLootEvent, "<color=#e7cf85>/{0}</color> loot \"<color=#e7cf85><event-name></color>\", create loot items that you wish to spawn in the crate, add the items to your inventory and run the command.\n\n"
                }
                , {
                    LangKeys.HelpDragEvent, "<color=#e7cf85>/{0}</color> drag \"<color=#e7cf85><event-name></color>\", specify the amount of drag the crate should have while falling.\n\n"
                }
                , {
                    LangKeys.HelpGuardAmount, "<color=#e7cf85>/{0}</color> amount \"<color=#e7cf85><event-name></color>\", specify the guard amount to spawn.\n\n"
                }
                , {
                    LangKeys.HelpGuardLoadout, "<color=#e7cf85>/{0}</color> loadout \"<color=#e7cf85><event-name></color>\", set guard loadout using items in your inventory."
                }
            }
            , this);
        }
        private static class LangKeys {
            public const string Prefix = "Prefix";
            public const string NoPermission = "NoPermission";
            public const string FailedToStartEvent = "FailedToStartEvent";
            public const string ClearEvents = "ClearEvents";
            public const string StartEvent = "StartEvent";
            public const string EventCompleted = "EventCompleted";
            public const string EventStart = "EventStart";
            public const string EventEnded = "EventClear";
            public const string EventNotFound = "EventNotFound";
            public const string EventPositionInvalid = "EventPosInvalid";
            public const string EventIntersecting = "EventNearby";
            public const string EventUpdated = "EventUpdated";
            public const string EliminateGuards = "EliminateGuards";
            public const string InvalidGuardAmount = "InvalidGuardAmount";
            public const string HelpStartEvent = "HelpStartEvent";
            public const string HelpStopEvent = "HelpStopEvent";
            public const string HelpHereEvent = "HelpHereEvent";
            public const string HelpPositionEvent = "HelpPositionEvent";
            public const string HelpLootEvent = "HelpLootEvent";
            public const string HelpDragEvent = "HelpDragEvent";
            public const string HelpGuardAmount = "HelpGuardAmount";
            public const string HelpGuardLoadout = "HelpGuardLoadout";
        }
#endregion
#region Oxide Hooks
        private void OnServerInitialized() {
            SpawnManager.Instance.FindSpawnPoints();
            if (!string.IsNullOrEmpty(_configData.CommandName)) {
                cmd.AddConsoleCommand(_configData.CommandName, this, nameof(EventConsoleCommands));
                cmd.AddChatCommand(_configData.CommandName, this, nameof(EventCommandCommands));
            }
            if (_configData.AutoStartEnabled && _configData.AutoStartDuration > 0) timer.Every(_configData.AutoStartDuration, () => TryStartEvent(null));
        }
        private void Init() {
            permission.RegisterPermission(PERM_USE, this);
            _plugin = this;
            _getMessage = (key, userId) => lang.GetMessage(key, this, userId);
            _tryFetchTag = TryFetchTag;
            _trySpawnNpc = TrySpawnNpc;
            _tryLockHackable = TryLockHackable;
            _sendGuiNotification = SendGuiNotification;
            LoadData();
            SpawnManager.Initialize(_configData.BlockedTopologies);
        }
        private void Unload() {
            try {
                EventManager.OnUnload();
                SpawnManager.OnUnload();
                EntityCache.OnUnload();
            }
            finally {
                _plugin = null;
                _configData = null;
            }
        }
        private void OnEntityDeath(ScientistNPC scientist, HitInfo info) {
            NetworkableId id = scientist.net?.ID ?? default;
            EntityCache.FindEntityEventInstance(id)?.OnGuardKilled(scientist, info?.InitiatorPlayer);
            EntityCache.RemoveEntity(id);
        }
        private void OnEntityKill(ScientistNPC scientist) {
            NetworkableId id = scientist.net?.ID ?? default;
            EntityCache.FindEntityEventInstance(id)?.OnGuardKilled(scientist, null);
            EntityCache.RemoveEntity(id);
        }
        private void OnEntityKill(LootContainer container) {
            NetworkableId id = container.net?.ID ?? default;
            EntityCache.FindEntityEventInstance(id)?.OnCrateKilled(container);
            EntityCache.RemoveEntity(id);
        }
        private void OnEntityKill(CargoPlane plane) {
            NetworkableId id = plane.net?.ID ?? default;
            EntityCache.FindEntityEventInstance(id)?.OnPlaneKilled(plane);
            EntityCache.RemoveEntity(id);
        }
        private object CanHackCrate(BasePlayer player, HackableLockedCrate crate) {
            NetworkableId id = crate.net?.ID ?? default;
            return EntityCache.FindEntityEventInstance(id)?.CanHackCrate(player);
        }
#endregion
#region Spawn Manager
        private class SpawnManager : IDisposable {
            public static SpawnManager Instance {
                get;
                private set;
            }
            private const int BLOCKED_TERRAIN_TOPOLOGIES = (int)(TerrainTopology.Enum.Cliff | TerrainTopology.Enum.Cliffside | TerrainTopology.Enum.Beach | TerrainTopology.Enum.Beachside | TerrainTopology.Enum.Ocean | TerrainTopology.Enum.Oceanside | TerrainTopology.Enum.Monument | TerrainTopology.Enum.Building | TerrainTopology.Enum.River | TerrainTopology.Enum.Riverside | TerrainTopology.Enum.Lake | TerrainTopology.Enum.Lakeside);
            private readonly int _blockedLayerMasks = LayerMask.GetMask("Tree", "Construction", "Prevent_Building", "Vehicle_World", "Vehicle_Large", "Vehicle_Detailed");
            private readonly List<ZoneInfo> _ignoreZones = new();
            private readonly Queue<Vector3> _spawnPoints = new();
            private readonly Stopwatch _stopwatch = new();
            private TerrainBiome.Enum _blockedTerrainBiomes = 0;
            private bool _hasBlockedTerrainBiomes;
            private Coroutine _routine;
            private int _spawnPointAttempts;
            private int _spawnPointCount;
            public static void Initialize(List<TerrainBiome.Enum> blockedTerrainBiomes) {
                Instance = new SpawnManager();
                Instance._hasBlockedTerrainBiomes = blockedTerrainBiomes.Count > 0;
                foreach (TerrainBiome.Enum blockedTopology in blockedTerrainBiomes) Instance._blockedTerrainBiomes |= blockedTopology;
                Instance.GetIgnoredZones();
            }
            public static void OnUnload() {
                Instance.Dispose();
                Instance = null;
            }
            public void Dispose() {
                CleanupSpawnsRoutine();
                _ignoreZones.Clear();
                _spawnPoints.Clear();
            }
            private IEnumerator GenerateSpawnsRoutine(int maxSpawnPoints = 100, int maxSpawnAttempts = 5000, float frameBudget = 0.10f) {
                yield return CoroutineEx.waitForEndOfFrame;
                Interface.Oxide.LogDebug("GuardedCrate: generating spawn points.");
                float mapSizeX = TerrainMeta.Size.x / 2;
                float mapSizeZ = TerrainMeta.Size.z / 2;
                Vector3 spawnPoint = Vector3.zero;
                _spawnPointAttempts = 0;
                _spawnPointCount = 0;
                while (_spawnPointCount < maxSpawnPoints) {
                    _stopwatch.Reset();
                    _stopwatch.Start();
                    for (int index = _spawnPointCount; index < maxSpawnPoints; ++index) {
                        spawnPoint.x = UnityEngine.Random.Range(-mapSizeX, mapSizeX);
                        spawnPoint.z = UnityEngine.Random.Range(-mapSizeZ, mapSizeZ);
                        spawnPoint.y = WaterLevel.GetWaterOrTerrainSurface(spawnPoint, false, false);
                        ++_spawnPointAttempts;
                        if (TestSpawnPoint(ref spawnPoint)) {
                            _spawnPoints.Enqueue(spawnPoint);
                            ++_spawnPointCount;
                        }
                        if (_stopwatch.Elapsed.TotalMilliseconds >= frameBudget) break;
                    }
                    if (_spawnPointAttempts >= maxSpawnAttempts) break;
                    yield return CoroutineEx.waitForEndOfFrame;
                    yield return CoroutineEx.waitForEndOfFrame;
                }
                _stopwatch.Stop();
                Interface.Oxide.LogDebug("GuardedCrate: successfully found {0} spawn points.", _spawnPoints.Count);
                _routine = null;
            }
            private void CleanupSpawnsRoutine() {
                if (_routine == null) return;
                ServerMgr.Instance.StopCoroutine(_routine);
                _routine = null;
            }
            public void FindSpawnPoints() {
                if (_routine != null) return;
                _routine = ServerMgr.Instance.StartCoroutine(GenerateSpawnsRoutine());
            }
            public Vector3 GetSpawnPoint() {
                Vector3 spawnPoint = Vector3.zero;
                while (_spawnPoints.Count > 0) {
                    spawnPoint = _spawnPoints.Dequeue();
                    if (IsSpawnPointValid(spawnPoint)) break;
                }
                if (_routine == null && _spawnPoints.Count <= 2) {
                    FindSpawnPoints();
                    spawnPoint = Vector3.zero;
                }
                return spawnPoint;
            }
            private void GetIgnoredZones() {
                if (!_configData.ZoneManagerSettings.EnabledIgnoredZones) return;
                if (_plugin?.ZoneManager == null) return;
                ZoneMangerSettings zoneMangerSettings = _configData?.ZoneManagerSettings;
                if (!(zoneMangerSettings?.IgnoredZones?.Count > 0)) return;
                if (_plugin.ZoneManager.Call("GetZoneIDs") is not string[] zoneIds || zoneIds.Length == 0) return;
                foreach (string zoneId in zoneIds) {
                    if (!zoneMangerSettings.IgnoredZones.Contains(zoneId)) continue;
                    if (_plugin.ZoneManager.Call("GetZoneLocation", zoneId) is not Vector3 position) continue;
                    if (_plugin.ZoneManager.Call("GetZoneRadius", zoneId) is not float radius) continue;
                    _ignoreZones.Add(new ZoneInfo(position, radius));
                }
            }
            private bool TestSpawnPoint(ref Vector3 spawnPoint) {
                if (!Physics.Raycast(spawnPoint.WithY(spawnPoint.y + 100f), Vector3.down, out RaycastHit hit, 100f, 1218652417, QueryTriggerInteraction.Ignore)) return false;
                spawnPoint.y = hit.point.y;
                if (AntiHack.TestInsideTerrain(spawnPoint)) return false;
                if (AntiHack.IsInsideMesh(spawnPoint)) return false;
                if (IsInterceptingPosition(spawnPoint)) return false;
                if (IsInterceptingIgnored(spawnPoint)) return false;
                if (IsBlockedTerrainTopology(spawnPoint)) return false;
                if (IsBlockedTerrainBiome(spawnPoint)) return false;
                return IsSpawnPointValid(spawnPoint);
            }
            private bool IsSpawnPointValid(Vector3 spawnPoint) {
                if (WaterLevel.Test(spawnPoint, false, false)) return false;
                if (!IsValidCollider(spawnPoint)) return false;
                if (HasPlayersNearby(spawnPoint)) return false;
                return !HasBuildingNearby(spawnPoint);
            }
            private bool IsBlockedTerrainTopology(Vector3 spawnPoint) {
                return (TerrainMeta.TopologyMap.GetTopology(spawnPoint) & (int)BLOCKED_TERRAIN_TOPOLOGIES) != 0;
            }
            private bool IsBlockedTerrainBiome(Vector3 spawnPoint) {
                return _hasBlockedTerrainBiomes && TerrainMeta.BiomeMap.GetBiome(spawnPoint, (int)_blockedTerrainBiomes) != 0.0f;
            }
            private bool IsInterceptingIgnored(Vector3 spawnPoint) {
                foreach (ZoneInfo zoneInfo in _ignoreZones) {
                    if (zoneInfo.IsInBounds(spawnPoint)) return true;
                }
                return false;
            }
            private bool IsInterceptingPosition(Vector3 spawnPoint) {
                const float distance = 100f;
                foreach (Vector3 pos in _spawnPoints) {
                    if (Vector3.Distance(pos, spawnPoint) <= distance) return true;
                }
                return false;
            }
            private bool IsValidCollider(Vector3 spawnPoint) {
     
// ... [Code preview truncated due to length. Full source in attached file] ...

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