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

FREE TruePVE : 2.3.721 2.3.721

Нет прав для скачивания
C#:
using Facepunch;
using Facepunch.Math;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Rust;
using Rust.Ai.Gen2;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
/*
Fixed issue where game does not assign `creatorPlayer` to bee grenades, bee catapult bombs, and supply signals when dropped or catapulted
Fixed issues with the TwigDamage flag options and included in documentation
Fixed `Prevent hackable crate timer from resetting when attacked`
Added `Allow Raiding In Deep Sea` (false) to include looting containers, can still be blocked by other plugins. Does not enable PVP, enable both options for that. 
Added additional functionality to `Allow PVP Damage In Deep Sea` to allow looting players, corpses and backpacks, as well as allowing traps and turrets to target and kill players. You must enable the raiding option for raiding to be allowed. Other plugins can block this behavior.
Added `Use Clans` (true) - several features depend on the functionality of these options to determine if a player is an ally of another player.
Added `Use Friends` (true)
Added `Use Teams` (true)
Added `ExcludePlayerBoatFromImmortalFlags` flag (boat station will not be included until a performance friendly implementation is possible)
Added support for player-made boats to several existing options
Renamed `Require Owner Online` to `Block Damage When Owner Is Online` retroactively for clarity
Renamed "Apply To Twig" retroactively
Added `Vehicles can hurt NPC players (true = ignore this option)` and stopped applying vehicle rules to mounted players which prevented players from hurting npcs by hitting them with a vehicle
Reimplemented `Allow Killing Sleepers (TC Auth Only)` with new options
Updated `Allow Killing Sleepers (TC Auth Only)` to support player-made boats
*/
namespace Oxide.Plugins {
    [Info("TruePVE", "nivex", "2.3.721")]
    [Description("Improvement of the default Rust PVE behavior")]
    // Thanks to the original author, ignignokt84.
    internal class TruePVE : RustPlugin {
#region Variables
        // config/data container
        private Configuration config = new();
        [PluginReference]
        Plugin ZoneManager, LiteZones, Clans, Friends, AbandonedBases, RaidableBases;
        public string usageString;
        public enum Command {
            def, sched, trace, usage, enable, sleepers, map, unmap
        }
        ;
        public enum DamageResult {
            None, Allow, Block
        }
        [Flags]
        public enum RuleFlags : ulong {
            None = 0, AdminsHurtSleepers = 1uL << 1, AdminsHurtPlayers = 1uL << 2, AnimalsIgnoreSleepers = 1uL << 3, AuthorizedDamage = 1uL << 4, AuthorizedDamageRequiresOwnership = 1uL << 5, CupboardOwnership = 1uL << 6, FriendlyFire = 1uL << 7, HeliDamageLocked = 1uL << 8, HumanNPCDamage = 1uL << 9, LockedBoxesImmortal = 1uL << 10, LockedDoorsImmortal = 1uL << 11, NoPlayerDamageToCar = 1uL << 12, NoPlayerDamageToMini = 1uL << 13, NoPlayerDamageToScrap = 1uL << 14, NoHeliDamage = 1uL << 15, NoHeliDamagePlayer = 1uL << 16, NoHeliDamageQuarry = 1uL << 17, NoHeliDamageRidableHorses = 1uL << 18, NoHeliDamageSleepers = 1uL << 19, NoMLRSDamage = 1uL << 20, NpcsCanHurtAnything = 1uL << 21, PlayerSamSitesIgnorePlayers = 1uL << 22, ProtectedSleepers = 1uL << 23, TrapsIgnorePlayers = 1uL << 24, TrapsIgnoreScientist = 1uL << 25, TurretsIgnorePlayers = 1uL << 26, TurretsIgnoreScientist = 1uL << 27, StaticTurretsIgnoreScientist = 1uL << 28, TwigDamage = 1uL << 29, TwigDamageRequiresOwnership = 1uL << 30, VehiclesTakeCollisionDamageWithoutDriver = 1uL << 31, SamSitesIgnoreMLRS = 1uL << 32, SelfDamage = 1uL << 33, StaticSamSitesIgnorePlayers = 1uL << 34, StaticTurretsIgnorePlayers = 1uL << 35, SafeZoneTurretsIgnorePlayers = 1uL << 36, SuicideBlocked = 1uL << 37, NoHeliDamageBuildings = 1uL << 38, WoodenDamage = 1uL << 39, WoodenDamageRequiresOwnership = 1uL << 40, AuthorizedDamageCheckPrivilege = 1uL << 41, ExcludeTugboatFromImmortalFlags = 1uL << 42, ExcludePlayerBoatFromImmortalFlags = 1uL << 43, LockedVehiclesImmortal = 1uL << 44, TurretsIgnoreBradley = 1uL << 45, AuthorizedFarmableDamage = 1uL << 46, HopperCannotTargetEnemyLoot = 1uL << 47, VehiclesTakeCollisionDamage = 1uL << 48,
        }
        private bool IsUnloading;
        private Timer scheduleUpdateTimer;
        // timer to check for schedule updates
        private bool shareRedirectDudEnabled;
        // undocumented. UAYOR.
        private RuleSet dudRuleSet;
        // dud ruleset when no locations are shared
        private RuleSet currentRuleSet;
        // current ruleset
        private string currentBroadcastMessage;
        // current broadcast message
        private bool useZones;
        // internal useZones flag
        private const string Any = "any";
        // constant "any" string for rules
        private const string AllZones = "allzones";
        // constant "allzones" string for mappings
        private const string PermCanMap = "truepve.canmap";
        // permission for mapping command
        private bool animalsIgnoreSleepers;
        // toggle flag to protect sleepers
        private bool trace;
        // trace flag
        private const string traceFile = "ruletrace";
        // tracefile name
        private const float traceTimeout = 300f;
        // auto-disable trace after 300s (5m)
        private Timer traceTimer;
        // trace timeout timer
        private bool tpveEnabled = true;
        // toggle flag for damage handling
        private List<DamageType> _damageTypes = new() {
            DamageType.Arrow, DamageType.Blunt, DamageType.Bullet, DamageType.Explosion, DamageType.Cold, DamageType.Heat, DamageType.Generic, DamageType.Slash, DamageType.Stab, DamageType.Cannon
        }
        ;
        private uint maincannonshell = 3032863244;
        private uint trainbarricade = 1221760186;
        private uint trainbarricadeheavy = 1363243026;
        private uint loot_trash = 3279100614;
        private uint giftbox_loot = 2216891097;
        private uint campfire = 4160694184;
        private uint oilfireballsmall = 3550347674;
        private uint heli_napalm = 184893264;
        private uint rocket_heli_napalm = 200672762;
        private uint rocket_heli = 129320027;
        private bool excludeAllZones;
        private readonly List<ulong> _waiting = new();
        private readonly HashSet<string> _deployables = new();
        private readonly HashSet<string> exclusionLocationsSet = new(StringComparer.OrdinalIgnoreCase);
        private readonly Dictionary<ulong, List<PlayerExclusion>> playerDelayExclusions = new();
        private readonly Dictionary<string, RuleSet> ruleSetByNameDictionary = new(StringComparer.OrdinalIgnoreCase);
        private class PlayerExclusion : Pool.IPooled {
            public Plugin plugin;
            public float time;
            public bool IsExpired => Time.time > time;
            public void EnterPool() {
                plugin = null;
                time = 0f;
            }
            public void LeavePool() {
            }
        }
#endregion
#region Loading/Unloading
        protected new static void Puts(string format, params object[] args) {
            if (!string.IsNullOrEmpty(format)) {
                Interface.Oxide.LogInfo("[{0}] {1}", "TruePVE", (args.Length != 0) ? string.Format(format, args) : format);
            }
        }
        private void Unload() {
            bool save = false;
            if (_removeMappingTimer is {
                Destroyed : false
            }
            ) {
                _removeMappingTimer.Destroy();
                SaveConfig();
                save = true;
            }
            if (_auMappingTimer is {
                Destroyed : false
            }
            ) {
                _auMappingTimer.Destroy();
                if (!save) SaveConfig();
            }
            IsUnloading = true;
            scheduleUpdateTimer?.Destroy();
            SaveData();
        }
        private void OnPluginLoaded(Plugin plugin) {
            if (plugin.Name == "ZoneManager") ZoneManager = plugin;
            if (plugin.Name == "LiteZones") LiteZones = plugin;
            if (ZoneManager != null || LiteZones != null) SetUseZones();
        }
        private void OnPluginUnloaded(Plugin plugin) {
            if (plugin.Name == "ZoneManager") ZoneManager = null;
            if (plugin.Name == "LiteZones") LiteZones = null;
            if (ZoneManager == null && LiteZones == null) useZones = false;
        }
        private void OnCreatedDynamicPVP() => SetUseZones();
        private void OnDeletedDynamicPVP() => SetUseZones();
        protected void SetUseZones() {
            useZones = config != null && config.mappings != null && config.options != null && config.options.useZones && (LiteZones != null || ZoneManager != null);
            if (useZones && config.mappings.Count == 1) {
                foreach (var mapping in config.mappings) {
                    useZones = !mapping.Key.Equals(config.defaultRuleSet);
                }
            }
        }
        private void Init() {
            if (config.AllowKillingSleepersHoursOffline <= 0f) {
                Unsubscribe(nameof(OnPlayerSleep));
                Unsubscribe(nameof(OnPlayerSleepEnded));
            }
            if (!config.laptop) {
                Unsubscribe(nameof(OnCrateLaptopAttack));
            }
            if (!config.options.Loot.NoShieldDrop) {
                Unsubscribe(nameof(OnPlayerActiveShieldDrop));
            }
            if (!config.options.Loot.NoActiveItemDrop) {
                Unsubscribe(nameof(OnPlayerDropActiveItem));
            }
            if (!config.options.Loot.NoRustBackpackDrop) {
                Unsubscribe(nameof(OnBackpackDrop));
            }
            if (!config.BlockSprayCanInSafeZones) {
                Unsubscribe(nameof(OnSprayCreate));
            }
            Unsubscribe(nameof(CanLootEntity));
            Unsubscribe(nameof(OnCodeEntered));
            Unsubscribe(nameof(CanChangeCode));
            Unsubscribe(nameof(OnCupboardAuthorize));
            Unsubscribe(nameof(CanHelicopterStrafeTarget));
            Unsubscribe(nameof(CanWaterBallSplash));
            Unsubscribe(nameof(OnEntityMarkHostile));
            Unsubscribe(nameof(OnEntitySpawned));
            Unsubscribe(nameof(OnEntityEnter));
            Unsubscribe(nameof(OnTurretTarget));
            Unsubscribe(nameof(OnTimedExplosiveExplode));
            Unsubscribe(nameof(OnWallpaperRemove));
            Unsubscribe(nameof(OnEntityTakeDamage));
            Unsubscribe(nameof(OnPlayerConnected));
            Unsubscribe(nameof(OnSamSiteTarget));
            Unsubscribe(nameof(OnTrapTrigger));
            Unsubscribe(nameof(OnMlrsFire));
            // register console commands automagically
            foreach (Command command in Enum.GetValues(typeof(Command))) {
                AddCovalenceCommand($"tpve.{command}", nameof(CommandDelegator));
            }
            // register chat commands
            AddCovalenceCommand("tpve_prod", nameof(CommandDelegator));
            AddCovalenceCommand("tpve_enable", nameof(CommandDelegator));
            AddCovalenceCommand("tpve", nameof(CommandDelegator));
            permission.RegisterPermission(PermCanMap, this);
            // build usage string for console (without sizing)
            usageString = WrapColor("orange", GetMessage("Header_Usage")) + $" - {Version}{Environment.NewLine}" + WrapColor("cyan", $"tpve.{Command.map}") + $" - {GetMessage("Cmd_Usage_mapzone")}{Environment.NewLine}" + WrapColor("cyan", $"tpve.{Command.unmap}") + $" - {GetMessage("Cmd_Usage_unmapzone")}{Environment.NewLine}" + WrapColor("cyan", $"tpve.{Command.def}") + $" - {GetMessage("Cmd_Usage_def")}{Environment.NewLine}" + WrapColor("cyan", $"tpve.{Command.trace}") + $" - {GetMessage("Cmd_Usage_trace")}{Environment.NewLine}" + WrapColor("cyan", $"tpve.{Command.sched} [enable|disable]") + $" - {GetMessage("Cmd_Usage_sched")}{Environment.NewLine}" + WrapColor("cyan", $"/tpve_prod") + $" - {GetMessage("Cmd_Usage_prod")}{Environment.NewLine}" + WrapColor("cyan", $"/tpve map") + $" - {GetMessage("Cmd_Usage_map")}";
            LoadData();
        }
        private bool IsAnimalsIgnoringSleepers() => animalsIgnoreSleepers || config.ruleSets.Exists(ruleSet => ruleSet.HasFlag(RuleFlags.AnimalsIgnoreSleepers));
        private bool IsAnimalsIgnoringSleepers(RuleSet ruleSet) => animalsIgnoreSleepers || ruleSet.HasFlag(RuleFlags.AnimalsIgnoreSleepers);
        private void OnServerInitialized(bool isStartup) {
            isServerStartingUp = false;
            // check for server pve setting
            if (ConVar.Server.pve) WarnPve();
            // load configuration
            config.Init(this);
            currentRuleSet = config.GetDefaultRuleSet();
            dudRuleSet = config.GetDudRuleSet();
            if (currentRuleSet == null) Puts(GetMessage("Warning_NoRuleSet"), config.defaultRuleSet);
            SetUseZones();
            if (config.schedule.enabled) {
                TimerLoop(true);
            }
            if (!IsAnimalsIgnoringSleepers()) {
                Unsubscribe(nameof(OnNpcTarget));
            }
            if (config.PreventSafeZoneStrafing) {
                Subscribe(nameof(CanHelicopterStrafeTarget));
            }
            if (config.PreventThrowingWaterInFreezingBiome || config.BlockRadioactiveWaterDamage) {
                Subscribe(nameof(CanWaterBallSplash));
            }
            if (currentRuleSet == null) {
                return;
            }
            if (config.ruleSets.Exists(ruleSet => (ruleSet._flags & (RuleFlags.SafeZoneTurretsIgnorePlayers | RuleFlags.StaticTurretsIgnorePlayers | RuleFlags.StaticTurretsIgnoreScientist | RuleFlags.TrapsIgnorePlayers | RuleFlags.TrapsIgnoreScientist | RuleFlags.TurretsIgnorePlayers | RuleFlags.TurretsIgnoreScientist | RuleFlags.TurretsIgnoreBradley)) != 0)) {
                Subscribe(nameof(OnEntityEnter));
                Subscribe(nameof(OnTurretTarget));
            }
            if (config.ruleSets.Exists(ruleSet => (ruleSet._flags & (RuleFlags.SamSitesIgnoreMLRS | RuleFlags.PlayerSamSitesIgnorePlayers | RuleFlags.StaticSamSitesIgnorePlayers)) != 0)) {
                Subscribe(nameof(OnSamSiteTarget));
            }
            if (config.ruleSets.Exists(ruleSet => (ruleSet._flags & (RuleFlags.TrapsIgnorePlayers | RuleFlags.TrapsIgnoreScientist)) != 0)) {
                Subscribe(nameof(OnTrapTrigger));
            }
            if (config.schedule.enabled && config.schedule.broadcast && !string.IsNullOrEmpty(currentBroadcastMessage)) {
                Subscribe(nameof(OnPlayerConnected));
            }
            if (config.options.disableBaseOvenSplash) {
                ServerMgr.Instance.StartCoroutine(OvenCo());
            }
            if (config.options.disableHostility) {
                Subscribe(nameof(OnEntityMarkHostile));
            }
            RuleSet ruleSet = currentRuleSet;
            if (config.options.handleDamage && ruleSet != null && !ruleSet.IsEmpty() && ruleSet.enabled) {
                Subscribe(nameof(OnEntityTakeDamage));
                tpveEnabled = true;
            }
            if (config.wallpaper) {
                Subscribe(nameof(OnWallpaperRemove));
                Subscribe(nameof(OnTimedExplosiveExplode));
            }
            if (config.options.Loot.ProtectTC) {
                Subscribe(nameof(OnCupboardAuthorize));
                Subscribe(nameof(CanLootEntity));
                Subscribe(nameof(CanChangeCode));
            }
            else if (config.options.Loot.Lifts) {
                Subscribe(nameof(CanLootEntity));
            }
            else if (config.options.Loot.Sleepers) {
                Subscribe(nameof(CanLootEntity));
            }
            else if (config.options.Loot.Corpses) {
                Subscribe(nameof(CanLootEntity));
            }
            else if (config.options.Loot.Backpacks) {
                Subscribe(nameof(CanLootEntity));
            }
            Subscribe(nameof(OnEntitySpawned));
            Subscribe(nameof(OnMlrsFire));
            BuildPrefabIds();
            AllowLocksOnContainers();
            RemoveTemporaryZones();
            InitDeepSea();
        }
        private IEnumerator OvenCo() {
            int checks = 0;
            foreach (var ent in BaseNetworkable.serverEntities) {
                if (++checks > 500) {
                    checks = 0;
                    yield return null;
                }
                if (ent is BaseOven oven) {
                    oven.disabledBySplash = false;
                }
            }
        }
        private void BuildPrefabIds() {
            if (StringPool.toNumber.TryGetValue("assets/prefabs/npc/m2bradley/maincannonshell.prefab", out var prefab1)) maincannonshell = prefab1;
            if (StringPool.toNumber.TryGetValue("assets/content/props/train_tunnels/trainbarricade.prefab", out var prefab2)) trainbarricade = prefab2;
            if (StringPool.toNumber.TryGetValue("assets/content/props/train_tunnels/trainbarricadeheavy.prefab", out var prefab3)) trainbarricadeheavy = prefab3;
            if (StringPool.toNumber.TryGetValue("assets/bundled/prefabs/radtown/loot_trash.prefab", out var prefab4)) loot_trash = prefab4;
            if (StringPool.toNumber.TryGetValue("assets/prefabs/misc/xmas/giftbox/giftbox_loot.prefab", out var prefab5)) giftbox_loot = prefab5;
            if (StringPool.toNumber.TryGetValue("assets/prefabs/deployable/campfire/campfire.prefab", out var prefab6)) campfire = prefab6;
            if (StringPool.toNumber.TryGetValue("assets/bundled/prefabs/oilfireballsmall.prefab", out var prefab7)) oilfireballsmall = prefab7;
            if (StringPool.toNumber.TryGetValue("assets/bundled/prefabs/napalm.prefab", out var prefab8)) heli_napalm = prefab8;
            if (StringPool.toNumber.TryGetValue("assets/prefabs/npc/patrol helicopter/rocket_heli_napalm.prefab", out var prefab9)) rocket_heli_napalm = prefab9;
            if (StringPool.toNumber.TryGetValue("assets/prefabs/npc/patrol helicopter/rocket_heli.prefab", out var prefab10)) rocket_heli = prefab10;
        }
#endregion
#region Data
        private void OnNewSave() {
            data = new();
            SaveData();
        }
        private class StoredData {
            public Dictionary<ulong, int> LastSeen = new();
            public DateTime LastRunTime = DateTime.MinValue;
        }
        private StoredData data = new();
        private void LoadData() {
            try {
                data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name);
            }
            catch (Exception ex) {
                Puts(ex.ToString());
            }
            data ??= new();
            data.LastSeen ??= new();
            if (data.LastRunTime != DateTime.MinValue && DateTime.Now.Subtract(data.LastRunTime).TotalHours >= 24) {
                if (config.AllowKillingSleepersHoursOffline > 0f && data.LastSeen.Count > 0) {
                    Puts("Last seen data wiped due to plugin not being loaded for {0} day(s).", DateTime.Now.Subtract(data.LastRunTime).Days);
                }
                data = new();
                data.LastRunTime = DateTime.Now;
            }
            if (config.AllowKillingSleepersHoursOffline <= 0f) {
                if (data.LastSeen.Count > 0) {
                    data.LastSeen.Clear();
                    SaveData();
                }
            }
        }
        private void SaveData() {
            data.LastRunTime = DateTime.Now;
            Interface.Oxide.DataFileSystem.WriteObject(Name, data);
        }
        private void OnPlayerSleep(BasePlayer player) {
            if (player.IsConnected) {
                data.LastSeen.Remove(player.userID);
            }
            else if (!player.IsBuildingAuthed(true)) {
                data.LastSeen[player.userID] = Epoch.Current;
            }
        }
        private void OnPlayerSleepEnded(BasePlayer player) {
            data.LastSeen.Remove(player.userID);
        }
        public bool CanKillOfflinePlayer(BasePlayer player, out double timeLeft) {
            timeLeft = 0;
            if (player.IsConnected || !player.IsSleeping()) {
                data.LastSeen.Remove(player.userID);
                return false;
            }
            if (!data.LastSeen.TryGetValue(player.userID, out var lastSeen)) {
                return false;
            }
            if (player.IsBuildingAuthed(true)) {
                data.LastSeen.Remove(player.userID);
                return false;
            }
            double timeOffline = Epoch.Current - lastSeen;
            double allowedOfflineTime = config.AllowKillingSleepersHoursOffline * 3600.0;
            timeLeft = (allowedOfflineTime - timeOffline) / 3600.0;
            return timeOffline > allowedOfflineTime;
        }
#endregion Data
#region Command Handling
        // delegation method for commands
        private void CommandDelegator(IPlayer user, string command, string[] args) {
            // return if user doesn't have access to run console command
            if (!user.IsAdmin) return;
            if (args.Length > 0 && (args[0] == "map" || args[0] == "unmap")) {
                if (user.HasPermission(PermCanMap)) {
                    CommandMap(user, command, args);
                }
                else user.Reply("You do not have the required truepve.canmap permission.");
                return;
            }
            if (args.Contains("pvp")) {
                if (currentRuleSet.rules.Remove("players cannot hurt players")) {
                    currentRuleSet.rules.Add("players can hurt players");
                }
                else if (currentRuleSet.rules.Remove("player can hurt players")) {
                    currentRuleSet.rules.Add("player cannot hurt players");
                }
                Puts("PVP toggled {0}", currentRuleSet.rules.Contains("players can hurt players") ? "on" : "off");
                SaveConfig();
                return;
            }
            if (command == "tpve_prod") {
                HandleProd(user);
                return;
            }
            if (command == "tpve_enable") {
                tpveEnabled = !tpveEnabled;
                ValidateCurrentDamageHook();
                Message(user, "Enable", tpveEnabled);
                return;
            }
            if (command == "tpve" && args.Length != 0) command = args[0];
            else command = command.Replace("tpve.", string.Empty);
            if (!Enum.TryParse(command, out Command @enum)) {
                user.Reply($"Invalid argument: {command}");
                return;
            }
            switch (@enum) {
                case Command.sleepers : HandleSleepers(user);
                return;
                case Command.def : HandleDef(user);
                return;
                case Command.sched : HandleScheduleSet(user, args);
                return;
                case Command.trace : HandleTrace(user);
                return;
                case Command.enable : tpveEnabled = !tpveEnabled;
                ValidateCurrentDamageHook();
                Message(user, "Enable", tpveEnabled);
                return;
                case Command.map : case Command.unmap : if (user.HasPermission(PermCanMap)) {
                    CommandMap(user, command, args);
                }
                else user.Reply("You do not have the required truepve.canmap permission.");
                return;
                case Command.usage : default : ShowUsage(user);
                return;
            }
        }
        protected void HandleTrace(IPlayer user) {
            if (!IsTraceEnabled(user)) {
                return;
            }
            if (user.IsServer) {
                traceDistance = 0f;
            }
            else traceDistance = config.options.MaxTraceDistance;
            trace = !trace;
            if (!trace) {
                tracePlayer = null;
                traceEntity = null;
            }
            else tracePlayer = user.Object as BasePlayer;
            Message(user, "Notify_TraceToggle", new object[] {
                trace ? "on" : "off"
            }
            );
            traceTimer?.Destroy();
            if (trace) {
                traceTimer = timer.In(traceTimeout, () => trace = false);
            }
        }
        private bool IsTraceEnabled(IPlayer user) {
            if (config.options.PlayerConsole || config.options.ServerConsole) {
                return true;
            }
            Message(user, "`Trace To Player Console` or `Trace To Server Console` must be enabled in the config!");
            return false;
        }
        private void HandleSleepers(IPlayer user) {
            if (animalsIgnoreSleepers) {
                animalsIgnoreSleepers = false;
                if (!IsAnimalsIgnoringSleepers()) {
                    Unsubscribe(nameof(OnNpcTarget));
                }
                user.Reply("Sleepers are no longer protected from animals.");
            }
            else {
                animalsIgnoreSleepers = true;
                Subscribe(nameof(OnNpcTarget));
                user.Reply("Sleepers are now protected from animals.");
            }
        }
        // handle setting defaults
        private void HandleDef(IPlayer user) {
            config.options = new();
            Message(user, "Notify_DefConfigLoad");
            LoadDefaultData();
            Message(user, "Notify_DefDataLoad");
            CheckData();
            SaveConfig();
        }
        // handle prod command (raycast to determine what player is looking at)
        private void HandleProd(IPlayer user) {
            var player = user.Object as BasePlayer;
            if (player == null || !player.IsAdmin) {
                Message(user, "Error_NoPermission");
                return;
            }
            if (!GetRaycastTarget(player, out var entity)) {
                SendReply(player, WrapSize(12, WrapColor("red", GetMessage("Error_NoEntityFound", player.UserIDString))));
                return;
            }
            Message(player, "Notify_ProdResult", entity.GetType(), entity.ShortPrefabName);
        }
        private void CommandMap(IPlayer user, string command, string[] args) {
            // assume args[0] is the command (beyond /tpve)
            if (args.Length > 0) command = args[0];
            // shift arguments
            args = args.Length > 1 ? args[1..] : Array.Empty<string>();
            if (command != "map" && command != "unmap") {
                Message(user, "Error_InvalidCommand");
            }
            else if (args.Length == 0)  // ideally this would just update from/to for the next if statement instead of reusing duplicated code
            {
                BasePlayer player = user.Object as BasePlayer;
                if (player != null) {
                    using var locs = GetLocationKeys(player);
                    if (locs.Count > 0) {
                        string from = locs[0];
                        // mapping name
                        string to = "exclude";
                        if (command == "map") {
                            if (config.mappings.TryGetValue(from, out string old)) {
                                Message(user, "Notify_MappingUpdated", from, old, to);
                                // update existing mapping
                            }
                            else Message(user, "Notify_MappingCreated", from, to);
                            // add new mapping
                            config.mappings[from] = to;
                        }
                        else if (command == "unmap") {
                            if (config.mappings.Remove(from, out to)) {
                                Message(user, "Notify_MappingDeleted", from, to);
                                config.mappings.Remove(from);
                            }
                            else {
                                Message(user, "Error_InvalidMapping", from, to);
                                return;
                            }
                        }
                        SaveConfig();
                        TryBuildExclusionMappings();
                        return;
                    }
                }
                Message(user, "Error_InvalidParamForCmd", command);
            }
            else {
                string from = args[0];
                // mapping name
                string to = args.Length == 2 ? args[1] : null;
                // target ruleSet/exclude, otherwise delete mapping
                if (to != null) {
                    if (to != "exclude" && !config.ruleSets.Exists(r => r.name == to)) {
                        // target ruleset must exist, or be "exclude"
                        Message(user, "Error_InvalidMapping", from, to);
                        return;
                    }
                    if (config.mappings.TryGetValue(from, out string old)) {
                        Message(user, "Notify_MappingUpdated", from, old, to);
                        // update existing mapping
                    }
                    else Message(user, "Notify_MappingCreated", from, to);
                    // add new mapping
                    config.mappings[from] = to;
                    SaveConfig();
                    TryBuildExclusionMappings();
                }
                else {
                    if (config.HasMapping(from)) {
                        Message(user, "Notify_MappingDeleted", from, config.mappings[from]);
                        config.mappings.Remove(from);
                        // remove mapping
                        SaveConfig();
                        TryBuildExclusionMappings();
                    }
                    else Message(user, "Error_NoMappingToDelete", from);
                }
            }
        }
        // handles schedule enable/disable
        private void HandleScheduleSet(IPlayer user, string[] args) {
            if (args.Length < 2) {
                Message(user, "Error_InvalidParamForCmd", $"tpve.{args[0]}");
                Message(user, $"Syntax: tpve.{args[0]} <enable|disable>");
                return;
            }
            if (!config.schedule.valid) {
                Message(user, "Notify_InvalidSchedule");
            }
            else if (args[1] == "enable") {
                if (config.schedule.enabled) return;
                config.schedule.enabled = true;
                TimerLoop();
                Message(user, "Notify_SchedSetEnabled");
            }
            else if (args[1] == "disable") {
                if (!config.schedule.enabled) return;
                config.schedule.enabled = false;
                if (scheduleUpdateTimer != null) scheduleUpdateTimer.Destroy();
                Message(user, "Notify_SchedSetDisabled");
            }
            else {
                Message(user, "Error_InvalidParameter", args[1]);
                Message(user, $"Syntax: tpve.{args[0]} <enable|disable>");
            }
        }
#endregion
#region Configuration/Data
        private bool _playersTriggerOption, _playersHurtOption, _canKillOfflinePlayerEnabled, _pvpReflectionEnabled, _allowKillingSleepersEnabled, _buildingBlockHandlerEnabled;
        // load config
        protected override void LoadConfig() {
            base.LoadConfig();
            canSaveConfig = false;
            try {
                config = Config.ReadObject<Configuration>();
                if (config == null) LoadDefaultConfig();
                canSaveConfig = true;
                CheckData();
                SaveConfig();
            }
            catch (Exception ex) {
                Puts(ex.ToString());
                LoadDefaultConfig();
            }
        }
        protected override void LoadDefaultConfig() {
            config = new() {
                configVersion = Version.ToString(), options = new()
            }
            ;
            LoadDefaultData();
            Puts("Loaded default config.");
        }
        private bool canSaveConfig = true;
        // save data
        protected override void SaveConfig() {
            if (canSaveConfig) {
                Config.WriteObject(config);
            }
        }
        // check rulesets and groups
        private void CheckData() {
            if (string.IsNullOrEmpty(config.defaultRuleSet)) {
                config.defaultRuleSet = "default";
                Puts("Loaded default ruleset (no default ruleset was configured)");
            }
            if (config.mappings.IsNullOrEmpty()) {
                BuildDefaultMappings();
                Puts("Loaded default mappings (no mappings were configured)");
            }
            if (config.schedule == null) {
                Puts("Loaded default schedule (schedule was null)");
                BuildDefaultSchedule();
            }
            if (config.groups.IsNullOrEmpty()) {
                BuildDefaultGroups();
                Puts("Loaded default entity groups (no entity groups were configured)");
            }
            if (config.ruleSets.IsNullOrEmpty()) {
                BuildDefaultRuleset();
                Puts("Loaded default rulesets (no rulesets were configured)");
            }
            if (config.options.BlockHandler._Online != null) {
                config.options.BlockHandler.BlockWhenOnline = config.options.BlockHandler._Online.Value;
                config.options.BlockHandler._Online = null;
            }
            if (config.options.BlockHandler._Twig != null) {
                config.options.BlockHandler.Twig = config.options.BlockHandler._Twig.Value;
                config.options.BlockHandler._Twig = null;
            }
            TryUpdateConfig();
            config.configVersion = Version.ToString();
            CheckMappings();
            BuildRuleSetDictionary();
            BuildExclusionMappings();
            _allowKillingSleepersEnabled = config.AllowKillingSleepersAlly || config.AllowKillingSleepers || config.EntityCountRequired || config.AllowKillingSleepersIds.Exists(x => x.IsSteamId());
            _buildingBlockHandlerEnabled = config.options.BlockHandler.Any;
            _pvpReflectionEnabled = config.options.Reflect.Any;
            _canKillOfflinePlayerEnabled = config.AllowKillingSleepersHoursOffline > 0;
            _playersTriggerOption = config.PlayersTriggerTraps || config.PlayersTriggerTurrets;
            _playersHurtOption = config.PlayersHurtTraps || config.PlayersHurtTurrets;
        }
        private void TryUpdateConfig() {
            if (!TryParseVersionNumber(config.configVersion, out var vn) || vn >= Version) return;
            Dictionary<string, string> updates = new(StringComparer.OrdinalIgnoreCase) {
                ["npcs"]
                = "SnakeHazard", ["npcs"] = "ScientistNPC2", ["dispensers"] = "VineSwingingTree"
            }
            ;
            for (int i = 0; i < config.groups.Count; i++) {
                var group = config.groups[i];
                if (string.IsNullOrWhiteSpace(group.members)) {
                    continue;
                }
                if (string.IsNullOrEmpty(group.name)) {
                    group.name = $"group{i}";
                    continue;
                }
                if (group.name == "ridablehorses" && group.members.Contains("RidableHorse2")) {
                    group.members = ReplaceMember(group.members, "RidableHorse2", "RidableHorse");
                    continue;
                }
                if (group.members.Contains("heli_napalm")) {
                    group.members = ReplaceMember(group.members, "heli_napalm", "napalm");
                    var fire = config.groups.Find(x => x.name == "fire");
                    if (fire != null && fire.members.Contains("FireBall") && !fire.exclusions.Contains("napalm")) {
                        fire.exclusions = fire.exclusions.Length == 0 ? "napalm" : fire.exclusions + ", napalm";
                    }
                }
                if (updates.TryGetValue(group.name, out var update) && !ContainedInGroups(update)) {
                    group.members = $"{group.members.TrimEnd(',', ' ')}{", "}{update}";
                    continue;
                }
            }
        }
        private static string ReplaceMember(string members, string oldValue, string newValue) {
            if (string.IsNullOrWhiteSpace(members)) return members;
            string[] parts = members.Split(',');
            for (int i = 0; i < parts.Length; i++) {
                string member = parts[i].Trim();
                if (member == oldValue) {
                    parts[i] = newValue;
                }
                else {
                    parts[i] = member;
                }
            }
            return string.Join(",", parts);
        }
        private bool ContainedInGroups(string member) => config.groups.Exists(g => g.members.Contains(member, CompareOptions.OrdinalIgnoreCase) || g.exclusions.Contains(member, CompareOptions.OrdinalIgnoreCase));
        private bool TryParseVersionNumber(string input, out VersionNumber version) {
            version = default;
            if (string.IsNullOrWhiteSpace(input)) return false;
            var parts = input.Split('.');
            if (parts.Length != 3) return false;
            if (int.TryParse(parts[0], out int major) && int.TryParse(parts[1], out int minor) && int.TryParse(parts[2], out int patch)) {
                version = new(major, minor, patch);
                return true;
            }
            return false;
        }
        // rebuild mappings
        private bool CheckMappings() {
            bool dirty = false;
            foreach (RuleSet ruleSet in config.ruleSets) {
                if (!config.mappings.ContainsValue(ruleSet.name)) {
                    config.mappings[ruleSet.name] = ruleSet.name;
                    dirty = true;
                }
            }
            return dirty;
        }
        protected void BuildRuleSetDictionary() {
            ruleSetByNameDictionary.Clear();
            foreach (RuleSet ruleSet in config.ruleSets) {
                if (ruleSet.enabled) {
                    ruleSetByNameDictionary[ruleSet.name] = ruleSet;
                }
            }
        }
        protected void TryBuildExclusionMappings() {
            if (!config.mappings.TryGetValue(AllZones, out var val) || !val.Equals("exclude", StringComparison.OrdinalIgnoreCase)) {
                BuildExclusionMappings();
            }
        }
        protected void BuildExclusionMappings() {
            excludeAllZones = false;
            exclusionLocationsSet.Clear();
            if (config.mappings.TryGetValue(AllZones, out var val) && val.Equals("exclude", StringComparison.OrdinalIgnoreCase)) {
                excludeAllZones = true;
                return;
            }
            foreach (var (key, value) in config.mappings) {
                if (!value.Equals("exclude", StringComparison.OrdinalIgnoreCase)) {
                    foreach (var ruleSet in config.ruleSets) {
                        if (ruleSet.name.Equals(value, StringComparison.OrdinalIgnoreCase) && ruleSet.IsEmpty()) {
                            exclusionLocationsSet.Add(key);
                        }
                    }
                }
                else {
                    exclusionLocationsSet.Add(key);
                }
            }
            foreach (var group in config.groups) {
                foreach (var exclusion in group._exclusionSet) {
                    if (!string.IsNullOrWhiteSpace(exclusion)) {
                        exclusionLocationsSet.Add(exclusion.Trim());
                    }
                }
            }
        }
        // load default data to mappings, rulesets, and groups
        protected bool LoadDefaultData() {
            BuildDefaultMappings();
            BuildDefaultSchedule();
            BuildDefaultGroups();
            BuildDefaultRuleset();
            return true;
        }
        protected void BuildDefaultSchedule() {
            config.schedule = new();
        }
        protected void BuildDefaultMappings() {
            config.mappings ??= new();
            config.mappings.Clear();
            config.defaultRuleSet = "default";
            config.mappings[config.defaultRuleSet] = config.defaultRuleSet;
            // create mapping for ruleset
        }
        protected void BuildDefaultGroups() {
            config.groups ??= new();
            config.groups.Clear();
            config.groups.Add(new("barricades") {
                members = "door_barricade_a, door_barricade_a_large, door_barricade_b, door_barricade_dbl_a, door_barricade_dbl_a_large, door_barricade_dbl_b, door_barricade_dbl_b_large, gingerbread_barricades_house, gingerbread_barricades_snowman, gingerbread_barricades_tree, wooden_crate_gingerbread", exclusions = "barricade.concrete, barricade.sandbags, barricade.stone"
            }
            );
            config.groups.Add(new("barricades2") {
                members = "spikes_static, barricade.metal, barricade.wood, barricade.woodwire, spikes.floor, icewall, GraveyardFence",
            }
            );
            config.groups.Add(new("dispensers") {
                members = "BaseCorpse, HelicopterDebris, PlayerCorpse, NPCPlayerCorpse, HorseCorpse, SkyLantern, Pinata"
            }
            );
            config.groups.Add(new("fire") {
                members = "FireBall, FlameExplosive, FlameThrower, BaseOven, FlameTurret, oilfireball2", exclusions = "napalm"
            }
            );
            config.groups.Add(new("guards") {
                members = "bandit_guard, scientistpeacekeeper, sentry.scientist.static, sentry.bandit.static"
            }
            );
            config.groups.Add(new("heli") {
                members = "PatrolHelicopter, oilfireballsmall, rocket_heli, rocket_heli_napalm, napalm"
            }
            );
            config.groups.Add(new("highwalls") {
                members = "SimpleBuildingBlock, wall.external.high.ice, gates.external.high.stone, gates.external.high.wood"
            }
            );
            config.groups.Add(new("ridablehorses") {
                members = "RidableHorse"
            }
            );
            config.groups.Add(new("cars") {
                members = "BasicCar, ModularCar, BaseModularVehicle, BaseVehicleModule, VehicleModuleEngine, VehicleModuleSeating, VehicleModuleStorage, VehicleModuleTaxi, ModularCarSeat, Bike"
            }
            );
            config.groups.Add(new("mini") {
                members = "minicopter.entity"
            }
            );
            config.groups.Add(new("scrapheli") {
                members = "ScrapTransportHelicopter"
            }
            );
            config.groups.Add(new("ch47") {
                members = "ch47.entity"
            }
            );
            config.groups.Add(new("npcs") {
                members = "ch47scientists.entity, BradleyAPC, maincannonshell, CustomScientistNpc, SnakeHazard, ScarecrowNPC, HumanNPC, NPCPlayer, ScientistNPC, ScientistNPC2, TunnelDweller, SimpleShark, UnderwaterDweller, ZombieNPC"
            }
            );
            config.groups.Add(new("players") {
                members = "BasePlayer, FrankensteinPet, BeeGrenade"
            }
            );
            config.groups.Add(new("resources") {
                members = "ResourceEntity, TreeEntity, OreResourceEntity, LootContainer, NaturalBeehive, VineSwingingTree", exclusions = "hobobarrel.deployed"
            }
            );
            config.groups.Add(new("snowmobiles") {
                members = "snowmobile, tomahasnowmobile"
            }
            );
            config.groups.Add(new("traps") {
                members = "AutoTurret, BearTrap, FlameTurret, Landmine, GunTrap, ReactiveTarget, TeslaCoil, spikes.floor"
            }
            );
            config.groups.Add(new("junkyard") {
                members = "magnetcrane.entity, carshredder.entity"
            }
            );
            config.groups.Add(new("tugboats") {
                members = "Tugboat"
            }
            );
            config.groups.Add(new("heliturrets") {
                members = "turret_attackheli"
            }
            );
            config.groups.Add(new("ramhead") {
                members = "BatteringRamHead"
            }
            );
            config.groups.Add(new("siege") {
                members = "SiegeTower, Catapult, Ballista, Cannon, BallistaGun, BatteringRam, ConstructableEntity"
            }
            );
            config.groups.Add(new("bees") {
                members = "BeeSwarmAI, Beehive, BeeSwarmMaster, NaturalBeehive"
            }
            );
            config.groups.Add(new("farm") {
                members = "simplechicken.entity, FarmableAnimal, ChickenCoop"
            }
            );
        }
        protected void BuildDefaultRuleset() {
            config.ruleSets ??= new();
            config.ruleSets.Clear();
            // create default ruleset
            RuleSet defaultRuleSet = new(config.defaultRuleSet) {
                _flags = RuleFlags.AuthorizedDamage | RuleFlags.AuthorizedDamageRequiresOwnership | RuleFlags.AuthorizedFarmableDamage | RuleFlags.HopperCannotTargetEnemyLoot | RuleFlags.HumanNPCDamage | RuleFlags.LockedBoxesImmortal | RuleFlags.LockedDoorsImmortal | RuleFlags.PlayerSamSitesIgnorePlayers | RuleFlags.TrapsIgnorePlayers | RuleFlags.TurretsIgnorePlayers, flags = "AuthorizedDamage, AuthorizedDamageRequiresOwnership, AuthorizedFarmableDamage, HopperCannotTargetEnemyLoot, HumanNPCDamage, LockedBoxesImmortal, LockedDoorsImmortal, PlayerSamSitesIgnorePlayers, TrapsIgnorePlayers, TurretsIgnorePlayers"
            }
            ;
            // create rules and add to ruleset
            defaultRuleSet.AddRule(this, "anything can hurt dispensers");
            defaultRuleSet.AddRule(this, "anything can hurt resources");
            defaultRuleSet.AddRule(this, "anything can hurt barricades");
            defaultRuleSet.AddRule(this, "anything can hurt traps");
            defaultRuleSet.AddRule(this, "anything can hurt heli");
            defaultRuleSet.AddRule(this, "anything can hurt npcs");
            defaultRuleSet.AddRule(this, "anything can hurt players");
            defaultRuleSet.AddRule(this, "nothing can hurt ch47");
            defaultRuleSet.AddRule(this, "nothing can hurt cars");
            defaultRuleSet.AddRule(this, "nothing can hurt mini");
            defaultRuleSet.AddRule(this, "not
// ... [Code preview truncated due to length. Full source in attached file] ...

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