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

FREE TruePVE : 2.0.6 2.0.6

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Rust;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("TruePVE", "nivex", "2.0.6")]
    [Description("Improvement of the default Rust PVE behavior")]
    // Thanks to the original author, ignignokt84.class TruePVE : RustPlugin
    {
        #region Variables
        private static TruePVE Instance;

        // config/data container
        private Configuration config = new Configuration();

        [PluginReference]
        Plugin ZoneManager, LiteZones, Clans, Friends;

        // usage information string with formatting
        public string usageString;
        // valid commands
        private enum Command { def, sched, trace, usage, enable, sleepers };

        // default values array

        // flags for RuleSets
        [Flags]
        private enum RuleFlags
        {
            None = 0,
            SuicideBlocked = 1,
            AuthorizedDamage = 1 << 1,
            NoHeliDamage = 1 << 2,
            HeliDamageLocked = 1 << 3,
            NoHeliDamagePlayer = 1 << 4,
            HumanNPCDamage = 1 << 5,
            LockedBoxesImmortal = 1 << 6,
            LockedDoorsImmortal = 1 << 7,
            AdminsHurtSleepers = 1 << 8,
            ProtectedSleepers = 1 << 9,
            CupboardOwnership = 1 << 10,
            SelfDamage = 1 << 11,
            TwigDamage = 1 << 12,
            NoHeliDamageQuarry = 1 << 13,
            TrapsIgnorePlayers = 1 << 14,
            TurretsIgnorePlayers = 1 << 15,
            TurretsIgnoreScientist = 1 << 16,
            TrapsIgnoreScientist = 1 << 17,
            SamSitesIgnorePlayers = 1 << 18,
            TwigDamageRequiresOwnership = 1 << 19,
            AuthorizedDamageRequiresOwnership = 1 << 20,
            VehiclesTakeCollisionDamageWithoutDriver = 1 << 21,
            FriendlyFire = 1 << 22,
            AnimalsIgnoreSleepers = 1 << 23,
            NoHeliDamageRidableHorses = 1 << 24,
            NoHeliDamageSleepers = 1 << 25,
            SamSitesIgnoreMLRS = 1 << 26,
        }

        private Timer scheduleUpdateTimer;                              // timer to check for schedule updates        
        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 = false;                                     // 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 HashSet<string> _deployables = new HashSet<string>();
        private List<DamageType> damageTypes = new List<DamageType>
        {
            DamageType.Explosion,
            DamageType.Bullet,
            DamageType.Slash,
            DamageType.Stab,
            DamageType.Blunt
        };
        #endregion

        #region Loading/Unloading
        private void Loaded()
        {
            Instance = this;
            LoadDefaultMessages();
            // register console commands automagically
            foreach (Command command in Enum.GetValues(typeof(Command)))
            {
                AddCovalenceCommand($"tpve.{command}", nameof(CommandDelegator));
            }
            // register chat commands
            cmd.AddChatCommand("tpve_prod", this, nameof(HandleProd));
            cmd.AddChatCommand("tpve_enable", this, nameof(EnableToggle));
            cmd.AddChatCommand("tpve", this, nameof(ChatCommandDelegator));

            // build usage string for console (without sizing)
            usageString = WrapColor("orange", GetMessage("Header_Usage")) + "\n" +
                          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")}";
            permission.RegisterPermission(PermCanMap, this);
        }

        private void Unload()
        {
            if (scheduleUpdateTimer != null)
                scheduleUpdateTimer.Destroy();
            Instance = null;
        }

        private void OnPluginLoaded(Plugin plugin)
        {
            if (plugin.Name == "ZoneManager")
                ZoneManager = plugin;
            if (plugin.Name == "LiteZones")
                LiteZones = plugin;
            if (ZoneManager != null || LiteZones != null)
                useZones = config?.options.useZones ?? true;
        }

        private void OnPluginUnloaded(Plugin plugin)
        {
            if (plugin.Name == "ZoneManager")
                ZoneManager = null;
            if (plugin.Name == "LiteZones")
                LiteZones = null;
            if (ZoneManager == null && LiteZones == null)
                useZones = false;
            traceTimer?.Destroy();
        }

        private void Init()
        {
            Unsubscribe(nameof(CanBeTargeted));
            Unsubscribe(nameof(OnEntityTakeDamage));
            Unsubscribe(nameof(OnPlayerConnected));
            //Unsubscribe(nameof(OnSamSiteTargetScan));
            Unsubscribe(nameof(OnSamSiteTarget));
            Unsubscribe(nameof(OnTrapTrigger));
            Unsubscribe(nameof(OnNpcTarget));
        }

        private void OnServerInitialized(bool isStartup)
        {
            // check for server pve setting
            if (ConVar.Server.pve) WarnPve();
            // load configuration
            config.Init();
            currentRuleSet = config.GetDefaultRuleSet();
            if (currentRuleSet == null)
                PrintWarning(GetMessage("Warning_NoRuleSet"), config.defaultRuleSet);
            useZones = config.options.useZones && (LiteZones != null || ZoneManager != null);
            if (useZones && config.mappings.Count == 1 && config.mappings.First().Key.Equals(config.defaultRuleSet))
                useZones = false;
            if (config.schedule.enabled)
                TimerLoop(true);
            if (config.ruleSets.Any(ruleSet => ruleSet.HasFlag(RuleFlags.AnimalsIgnoreSleepers))) Subscribe(nameof(OnNpcTarget));
            if (currentRuleSet == null) return;
            InitializeDeployables();
            Subscribe(nameof(CanBeTargeted));
            Subscribe(nameof(OnEntityTakeDamage));
            Subscribe(nameof(OnPlayerConnected));
            //Subscribe(nameof(OnSamSiteTargetScan));
            Subscribe(nameof(OnSamSiteTarget));
            Subscribe(nameof(OnTrapTrigger));
        }
        #endregion

        #region Command Handling
        // delegation method for console commands
        private void CommandDelegator(IPlayer user, string command, string[] args)
        {
            // return if user doesn't have access to run console command
            if (!user.IsServer && !(user.Object as BasePlayer).IsAdmin) return;

            switch ((Command)Enum.Parse(typeof(Command), command.Replace("tpve.", string.Empty)))
            {
                case Command.sleepers:
                    HandleSleepers(user);
                    return;
                case Command.def:
                    HandleDef(user);
                    return;
                case Command.sched:
                    HandleScheduleSet(user, args);
                    return;
                case Command.trace:
                    trace = !trace;
                    if (!trace)
                    {
                        tracePlayer = null;
                        traceEntity = null;
                    }
                    else tracePlayer = user.Object as BasePlayer;
                    Message(user, "Notify_TraceToggle", new object[] { trace ? "on" : "off" });
                    if (trace)
                    {
                        traceTimer = timer.In(traceTimeout, () => trace = false);
                    }
                    else traceTimer?.Destroy();
                    return;
                case Command.enable:
                    tpveEnabled = !tpveEnabled;
                    Message(user, "Enable", tpveEnabled);
                    return;
                case Command.usage:
                default:
                    ShowUsage(user);
                    return;
            }
        }

        private void HandleSleepers(IPlayer user)
        {
            if (animalsIgnoreSleepers)
            {
                animalsIgnoreSleepers = false;
                if (!config.ruleSets.Any(ruleSet => ruleSet.HasFlag(RuleFlags.AnimalsIgnoreSleepers))) 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.");
            }
        }

        private void EnableToggle(BasePlayer player, string command, string[] args)
        {
            if (player.IsAdmin)
            {
                tpveEnabled = !tpveEnabled;
                Message(player, "Enable", new object[] { tpveEnabled.ToString() });
            }
        }

        // handle setting defaults
        private void HandleDef(IPlayer user)
        {
            config.options = new ConfigurationOptions();
            Message(user, "Notify_DefConfigLoad");
            LoadDefaultData();
            Message(user, "Notify_DefDataLoad");
            SaveConfig();
        }

        // handle prod command (raycast to determine what player is looking at)
        private void HandleProd(BasePlayer player, string command, string[] args)
        {
            if (!player.IsAdmin)
            {
                Message(player, "Error_NoPermission");
                return;
            }

            object entity;
            if (!GetRaycastTarget(player, out entity) || entity == null)
            {
                SendReply(player, WrapSize(12, WrapColor("red", GetMessage("Error_NoEntityFound", player.UserIDString))));
                return;
            }
            Message(player, "Notify_ProdResult", new object[] { entity.GetType(), (entity as BaseEntity).ShortPrefabName });
        }

        // delegation method for chat commands
        private void ChatCommandDelegator(BasePlayer player, string command, string[] args)
        {
            if (!player.IsAdmin && !permission.UserHasPermission(player.UserIDString, PermCanMap))
            {
                Message(player, "Error_NoPermission");
                return;
            }

            // assume args[0] is the command (beyond /tpve)
            if (args.Length > 0) command = args[0];

            // shift arguments
            args = args.Length > 1 ? args.Skip(1).ToArray() : new string[0];

            if (command != "map")
            {
                Message(player, "Error_InvalidCommand");
            }
            else if (args.Length == 0)
            {
                Message(player, "Error_InvalidParamForCmd", command);
            }
            else
            {
                // args[0] should be mapping name
                // args[1] if exists should be target ruleset or "exclude"
                // if args[1] is empty, delete mapping
                string from = args[0];
                string to = args.Length == 2 ? args[1] : null;

                if (to != null && !config.ruleSets.Select(r => r.name).Contains(to) && to != "exclude")
                {
                    // target ruleset must exist, or be "exclude"
                    Message(player, "Error_InvalidMapping", from, to);
                }
                else
                {
                    bool dirty = false;
                    if (to != null)
                    {
                        dirty = true;
                        if (config.HasMapping(from))
                        {
                            // update existing mapping
                            string old = config.mappings[from];
                            config.mappings[from] = to;
                            Message(player, "Notify_MappingUpdated", from, old, to);
                        }
                        else
                        {
                            // add new mapping
                            config.mappings.Add(from, to);
                            Message(player, "Notify_MappingCreated", from, to);
                        }
                    }
                    else
                    {
                        if (config.HasMapping(from))
                        {
                            dirty = true;
                            // remove mapping
                            string old = config.mappings[from];
                            config.mappings.Remove(from);
                            Message(player, "Notify_MappingDeleted", from, old);
                        }
                        else
                        {
                            Message(player, "Error_NoMappingToDelete", from);
                        }
                    }

                    if (dirty)
                    {
                        SaveConfig(); // save changes to config file
                    }
                }
            }
        }

        // handles schedule enable/disable
        private void HandleScheduleSet(IPlayer user, string[] args)
        {
            if (args.Length == 0)
            {
                Message(user, "Error_InvalidParamForCmd");
                return;
            }
            if (!config.schedule.valid)
            {
                Message(user, "Notify_InvalidSchedule");
            }
            else if (args[0] == "enable")
            {
                if (config.schedule.enabled) return;
                config.schedule.enabled = true;
                TimerLoop();
                Message(user, "Notify_SchedSetEnabled");
            }
            else if (args[0] == "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[0]);
            }
        }
        #endregion

        #region Configuration/Data

        // load config
        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<Configuration>();
                if (config == null) LoadDefaultConfig();
                CheckData();
                SaveConfig();
            }
            catch (Exception ex)
            {
                Puts(ex.Message);
                LoadDefaultConfig();
                return;
            }

            // check config version, update version to current version
            if (config.configVersion == null || !config.configVersion.Equals(Version.ToString()))
            {
                config.configVersion = Version.ToString();
            }
        }

        protected override void LoadDefaultConfig()
        {
            config = new Configuration
            {
                configVersion = Version.ToString(),
                options = new ConfigurationOptions()
            };
            LoadDefaultData();
        }

        // save data
        protected override void SaveConfig()
        {
            Config.WriteObject(config);
        }

        // check rulesets and groups
        private bool CheckData()
        {
            bool dirty = false;
            if ((config.ruleSets == null || config.ruleSets.Count == 0) || (config.groups == null || config.groups.Count == 0))
            {
                dirty = LoadDefaultData();
            }
            if (config.schedule == null)
            {
                config.schedule = new Schedule();
                dirty = true;
            }
            dirty |= CheckMappings();
            return dirty;
        }

        // rebuild mappings
        private bool CheckMappings()
        {
            bool dirty = false;
            foreach (RuleSet rs in config.ruleSets)
            {
                if (!config.mappings.ContainsValue(rs.name))
                {
                    config.mappings[rs.name] = rs.name;
                    dirty = true;
                }
            }
            return dirty;
        }

        // load default data to mappings, rulesets, and groups
        private bool LoadDefaultData()
        {
            config.mappings.Clear();
            config.ruleSets.Clear();
            config.groups.Clear();
            config.schedule = new Schedule();
            config.defaultRuleSet = "default";

            // build groups first
            config.groups.Add(new EntityGroup("barricades")
            {
                members = "Barricade, icewall, GraveYardFence", // "barricade.cover.wood, 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",
                exclusions = "barricade.concrete, barricade.sandbags, barricade.metal, barricade.stone, barricade.wood, barricade.woodwire"
            });

            config.groups.Add(new EntityGroup("dispensers")
            {
                members = "BaseCorpse, HelicopterDebris, PlayerCorpse, NPCPlayerCorpse, HorseCorpse"
            });

            config.groups.Add(new EntityGroup("fire")
            {
                members = "FireBall, FlameExplosive, FlameThrower, BaseOven, FlameTurret, rocket_heli_napalm, napalm, oilfireball2"
            });

            config.groups.Add(new EntityGroup("guards")
            {
                members = "bandit_guard, scientistpeacekeeper, sentry.scientist.static"
            });

            config.groups.Add(new EntityGroup("heli")
            {
                members = "BaseHelicopter"
            });

            config.groups.Add(new EntityGroup("highwalls")
            {
                members = "SimpleBuildingBlock, wall.external.high.ice, gates.external.high.stone, gates.external.high.wood"
            });

            config.groups.Add(new EntityGroup("ridablehorses")
            {
                members = "RidableHorse"
            });

            config.groups.Add(new EntityGroup("cars")
            {
                members = "BasicCar, ModularCar, BaseModularVehicle, BaseVehicleModule, VehicleModuleEngine, VehicleModuleSeating, VehicleModuleStorage, VehicleModuleTaxi, ModularCarSeat"
            });

            config.groups.Add(new EntityGroup("mini")
            {
                members = "minicopter.entity"
            });

            config.groups.Add(new EntityGroup("scrapheli")
            {
                members = "ScrapTransportHelicopter"
            });

            config.groups.Add(new EntityGroup("ch47")
            {
                members = "ch47.entity"
            });

            config.groups.Add(new EntityGroup("npcs")
            {
                members = "ch47scientists.entity, BradleyAPC, HumanNPC, NPCPlayer, ScientistNPC, TunnelDweller, SimpleShark, UnderwaterDweller, Zombie, ZombieNPC"
            });

            config.groups.Add(new EntityGroup("players")
            {
                members = "BasePlayer, FrankensteinPet"
            });

            config.groups.Add(new EntityGroup("resources")
            {
                members = "ResourceEntity, TreeEntity, OreResourceEntity, LootContainer",
                exclusions = "hobobarrel.deployed"
            });

            config.groups.Add(new EntityGroup("samsites")
            {
                members = "sam_site_turret_deployed",
                exclusions = "sam_static"
            });

            config.groups.Add(new EntityGroup("traps")
            {
                members = "AutoTurret, BearTrap, FlameTurret, Landmine, GunTrap, ReactiveTarget, TeslaCoil, spikes.floor"
            });

            config.groups.Add(new EntityGroup("junkyard")
            {
                members = "magnetcrane.entity, carshredder.entity"
            });

            // create default ruleset
            RuleSet defaultRuleSet = new RuleSet(config.defaultRuleSet)
            {
                _flags = RuleFlags.HumanNPCDamage | RuleFlags.LockedBoxesImmortal | RuleFlags.LockedDoorsImmortal | RuleFlags.SamSitesIgnorePlayers | RuleFlags.TrapsIgnorePlayers | RuleFlags.TurretsIgnorePlayers,
                flags = "HumanNPCDamage, LockedBoxesImmortal, LockedDoorsImmortal, SamSitesIgnorePlayers, TrapsIgnorePlayers, TurretsIgnorePlayers"
            };

            // create rules and add to ruleset
            defaultRuleSet.AddRule("anything can hurt dispensers");
            defaultRuleSet.AddRule("anything can hurt resources");
            defaultRuleSet.AddRule("anything can hurt barricades");
            defaultRuleSet.AddRule("anything can hurt traps");
            defaultRuleSet.AddRule("anything can hurt heli");
            defaultRuleSet.AddRule("anything can hurt npcs");
            defaultRuleSet.AddRule("anything can hurt players");
            defaultRuleSet.AddRule("nothing can hurt ch47");
            defaultRuleSet.AddRule("nothing can hurt cars");
            defaultRuleSet.AddRule("nothing can hurt mini");
            //defaultRuleSet.AddRule("nothing can hurt guards");
            defaultRuleSet.AddRule("nothing can hurt ridablehorses");
            defaultRuleSet.AddRule("cars cannot hurt anything");
            defaultRuleSet.AddRule("mini cannot hurt anything");
            defaultRuleSet.AddRule("ch47 cannot hurt anything");
            defaultRuleSet.AddRule("scrapheli cannot hurt anything");
            defaultRuleSet.AddRule("players cannot hurt players");
            defaultRuleSet.AddRule("players cannot hurt traps");
            defaultRuleSet.AddRule("guards cannot hurt players");
            defaultRuleSet.AddRule("fire cannot hurt players");
            defaultRuleSet.AddRule("traps cannot hurt players");
            defaultRuleSet.AddRule("highwalls cannot hurt players");
            defaultRuleSet.AddRule("barricades cannot hurt players");
            defaultRuleSet.AddRule("mini cannot hurt mini");
            defaultRuleSet.AddRule("npcs can hurt players");
            defaultRuleSet.AddRule("junkyard cannot hurt anything");
            defaultRuleSet.AddRule("junkyard can hurt cars");

            config.ruleSets.Add(defaultRuleSet); // add ruleset to rulesets list

            config.mappings[config.defaultRuleSet] = config.defaultRuleSet; // create mapping for ruleset

            return true;
        }

        private bool ResetRules(string key)
        {
            if (string.IsNullOrEmpty(key) || config == null)
            {
                return false;
            }

            string old = config.defaultRuleSet;

            config.defaultRuleSet = key;
            currentRuleSet = config.GetDefaultRuleSet();

            if (currentRuleSet == null)
            {
                config.defaultRuleSet = old;
                currentRuleSet = config.GetDefaultRuleSet();
                return false;
            }

            return true;
        }
        #endregion

        #region Trace
        private BaseEntity traceEntity;
        private BasePlayer tracePlayer;

        private void Trace(string message, int indentation = 0)
        {
            if (config.options.PlayerConsole || config.options.ServerConsole)
            {
                if (traceEntity != null && !traceEntity.IsDestroyed && tracePlayer != null && tracePlayer.IsConnected)
                {
                    if (config.options.MaxTraceDistance == 0 || tracePlayer.Distance(traceEntity) <= config.options.MaxTraceDistance)
                    {
                        if (config.options.PlayerConsole)
                        {
                            tracePlayer.ConsoleMessage(message);
                        }

                        if (config.options.ServerConsole)
                        {
                            Puts(message);
                        }

                        _tsb.AppendLine(string.Empty.PadLeft(indentation, ' ') + message);
                    }
                }
            }
            else _tsb.AppendLine(string.Empty.PadLeft(indentation, ' ') + message);
        }

        private void LogTrace()
        {
            var text = _tsb.ToString();
            traceEntity = null;
            try
            {
                LogToFile(traceFile, text, this);
            }
            catch (IOException)
            {
                timer.Once(1f, () => LogToFile(traceFile, text, this));
                return;
            }
            _tsb.Length = 0;
        }
        private StringBuilder _tsb = new StringBuilder();
        #endregion Trace

        #region Hooks/Handler Procedures
        private void OnPlayerConnected(BasePlayer player)
        {
            if (config.schedule.enabled && config.schedule.broadcast && currentBroadcastMessage != null)
            {
                SendReply(player, GetMessage("Prefix") + currentBroadcastMessage);
            }
        }

        private string CurrentRuleSetName() => currentRuleSet.name;
        private bool IsEnabled() => tpveEnabled;

        // handle damage - if another mod must override TruePVE damages or take priority,
        // set handleDamage to false and reference HandleDamage from the other mod(s)
        private object OnEntityTakeDamage(ResourceEntity entity, HitInfo hitInfo)
        {
            // if default global is not enabled, return true (allow all damage)
            if (hitInfo == null || currentRuleSet == null || currentRuleSet.IsEmpty() || !currentRuleSet.enabled)
            {
                return null;
            }

            // get entity and initiator locations (zones)
            List<string> entityLocations = GetLocationKeys(entity);
            List<string> initiatorLocations = GetLocationKeys(hitInfo.Initiator);
            // check for exclusion zones (zones with no rules mapped)
            if (CheckExclusion(entityLocations, initiatorLocations, trace))
            {
                if (trace) Trace("Exclusion found; allow and return", 1);
                return null;
            }

            if (trace) Trace("No exclusion found - looking up RuleSet...", 1);
            // process location rules
            RuleSet ruleSet = GetRuleSet(entityLocations, initiatorLocations);

            return EvaluateRules(entity, hitInfo, ruleSet) ? (object)null : true;
        }

        private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo)
        {
            if (entity == null || hitInfo == null || AllowKillingSleepers(entity, hitInfo))
            {
                return null;
            }

            object extCanTakeDamage = Interface.CallHook("CanEntityTakeDamage", new object[] { entity, hitInfo });

            if (extCanTakeDamage is bool)
            {
                if ((bool)extCanTakeDamage)
                {
                    return null;
                }

                CancelHit(hitInfo);
                return true;
            }

            if (!config.options.handleDamage)
            {
                return null;
            }

            var majority = hitInfo.damageTypes.GetMajorityDamageType();

            if (majority == DamageType.Decay || majority == DamageType.Fall)
            {
                return null;
            }

            if (!AllowDamage(entity, hitInfo))
            {
                if (trace) LogTrace();
                CancelHit(hitInfo);
                return true;
            }

            if (trace) LogTrace();
            return null;
        }

        private void CancelHit(HitInfo hitInfo)
        {
            hitInfo.damageTypes = new DamageTypeList();
            hitInfo.DidHit = false;
            hitInfo.DoHitEffects = false;
        }

        private bool AllowKillingSleepers(BaseCombatEntity entity, HitInfo hitInfo)
        {
            if (animalsIgnoreSleepers && hitInfo.Initiator is BaseNpc) return false;

            return config.AllowKillingSleepers && entity is BasePlayer && entity.ToPlayer().IsSleeping();
        }

        // determines if an entity is "allowed" to take damage
        private bool AllowDamage(BaseCombatEntity entity, HitInfo hitInfo)
        {
            if (trace)
            {
                traceEntity = entity;
                _tsb.Length = 0;
            }

            // if default global is not enabled or entity is npc, allow all damage
            if (currentRuleSet == null || currentRuleSet.IsEmpty() || !currentRuleSet.enabled || entity is BaseNpc || entity is BaseHelicopter)
            {
                return true;
            }

            // allow damage to door barricades and covers
            if (entity is Barricade && (entity.ShortPrefabName.Contains("door_barricade") || entity.ShortPrefabName.Contains("cover")))
            {
                return true;
            }

            // if entity is a barrel, trash can, or giftbox, allow damage (exclude water and hobo barrels)
            if (!entity.ShortPrefabName.Equals("waterbarrel") && entity.prefabID != 1748062128)
            {
                if (entity.ShortPrefabName.Contains("barrel") || entity.ShortPrefabName.Equals("loot_trash") || entity.ShortPrefabName.Equals("giftbox_loot"))
                {
                    return true;
                }
            }

            var weapon = hitInfo.Initiator ?? hitInfo.Weapon ?? hitInfo.WeaponPrefab;

            //hitInfo.ProjectilePrefab.sourceWeaponPrefab
            if (trace)
            {
                // Sometimes the initiator is not the attacker (turrets)
                Trace("======================" + Environment.NewLine +
                  "==  STARTING TRACE  ==" + Environment.NewLine +
                  "==  " + DateTime.Now.ToString("HH:mm:ss.fffff") + "  ==" + Environment.NewLine +
                  "======================");
                Trace($"From: {weapon?.GetType().Name ?? "Unknown_Weapon"}, {weapon?.ShortPrefabName ?? "Unknown_Prefab"}", 1);
                Trace($"To: {entity.GetType().Name}, {entity.ShortPrefabName}", 1);
            }

            // get entity and initiator locations (zones)
            List<string> entityLocations = GetLocationKeys(entity);
            List<string> initiatorLocations = GetLocationKeys(weapon);
            // check for exclusion zones (zones with no rules mapped)
            if (CheckExclusion(entityLocations, initiatorLocations, trace))
            {
                if (trace) Trace("Exclusion found; allow and return", 1);
                return true;
            }

            if (trace) Trace("No exclusion found - looking up RuleSet...", 1);

            // process location rules
            RuleSet ruleSet = GetRuleSet(entityLocations, initiatorLocations);

            if (trace) Trace($"Using RuleSet \"{ruleSet.name}\"", 1);

            if (weapon?.ShortPrefabName == "maincannonshell" || weapon is BradleyAPC)
            {
                if (trace) Trace("Initiator is BradleyAPC; evaluating RuleSet rules...", 1);
                return EvaluateRules(entity, weapon, ruleSet);
            }

            if (ruleSet.HasFlag(RuleFlags.VehiclesTakeCollisionDamageWithoutDriver) && entity is BaseMountable && weapon == entity)
            {
                BaseVehicle vehicle = entity.HasParent() ? (entity as BaseMountable).VehicleParent() : entity as BaseVehicle;

                if (vehicle.IsValid())
                {
                    var player = vehicle.GetDriver();

                    if (trace) Trace($"Vehicle collision: { (player == null ? "No driver; allow and return" : "Has driver; continue checks") }", 1);

                    if (player == null)
                    {
                        return true;
                    }
                }
            }

            // Check storage containers and doors for locks
            if (ruleSet.HasFlag(RuleFlags.LockedBoxesImmortal) && entity is StorageContainer || ruleSet.HasFlag(RuleFlags.LockedDoorsImmortal) && entity is Door)
            {
                if (!(entity is LootContainer))
                {
                    // check for lock
                    object hurt = CheckLock(ruleSet, entity, hitInfo);
                    if (trace) Trace($"Door/StorageContainer detected with immortal flag; lock check results: { (hurt == null ? "null (no lock or unlocked); continue checks" : (bool)hurt ? "allow and return" : "block and return") }", 1);
                    if (hurt is bool)
                    {
                        return (bool)hurt;
                    }
                }
            }

            // check heli and turret
            object heli = CheckHeliInitiator(ruleSet, hitInfo);
            bool isVictim = entity is BasePlayer;

            if (heli is bool)
            {
                return HandleHelicopter(ruleSet, entity, heli, isVictim);
            }

            if (weapon is BaseProjectile && hitInfo.Initiator == null)
            {
                var projectile = weapon as BaseProjectile;

                hitInfo.Initiator = projectile.GetOwnerPlayer();
            }

            // after heli check, return true if initiator is null
            if (hitInfo.Initiator == null)
            {
                if (hitInfo.damageTypes.types.Any(type => damageTypes.Contains((DamageType)type)))
                {
                    if (trace) Trace($"Initiator empty for player damage; block and return", 1);
                    return false;
                }
                if (trace) Trace($"Initiator empty; allow and return", 1);
                return true;
            }

            if (ruleSet.HasFlag(RuleFlags.SamSitesIgnorePlayers) && hitInfo.Initiator is SamSite && !entity.IsNpc)
            {
                var player = isVictim ? entity as BasePlayer : GetMountedPlayer(entity as BaseMountable);

                if (player.IsValid() && player.userID.IsSteamId())
                {
                    // check for exclusions in entity groups
                    bool hasExclusion = CheckExclusion(hitInfo.Initiator);
                    if (trace) Trace($"Initiator is samsite, and target is player; {(hasExclusion ? "exclusion found; allow and return" : "no exclusion; block and return")}", 1);
                    return hasExclusion;
                }
            }

            var victim = entity as BasePlayer;

            // handle suicide
            if (isVictim && !entity.IsNpc && victim.userID.IsSteamId() && hitInfo.damageTypes?.Get(DamageType.Suicide) > 0)
            {
                if (trace) Trace($"DamageType is suicide; blocked? { (ruleSet.HasFlag(RuleFlags.SuicideBlocked) ? "true; block and return" : "false; allow and return") }", 1);
                if (ruleSet.HasFlag(RuleFlags.SuicideBlocked))
                {
                    Message(entity as BasePlayer, "Error_NoSuicide");
                    return false;
                }
                return true;
            }

            // allow players to hurt themselves
            if (isVictim && ruleSet.HasFlag(RuleFlags.SelfDamage) && !entity.IsNpc && victim.userID.IsSteamId() && hitInfo.Initiator == entity)
            {
                if (trace) Trace($"SelfDamage flag; player inflicted damage to self; allow and return", 1);
                return true;
            }

            if (hitInfo.Initiator is MiniCopter && entity is BuildingBlock)
            {
                if (trace) Trace("Initiator is minicopter, target is building; evaluate and return", 1);
                return EvaluateRules(entity, hitInfo, ruleSet);
            }

            if (hitInfo.Initiator is BaseNpc)
            {
                // check for sleeper protection - return false if sleeper protection is on (true)
                if (isVictim && ruleSet.HasFlag(RuleFlags.ProtectedSleepers) && (entity as BasePlayer).IsSleeping())
                {
                    if (trace) Trace("Target is sleeping player, with ProtectedSleepers flag set; block and return", 1);
                    return false;
                }

                if (trace) Trace("Initiator is NPC animal; allow and return", 1);
                return true; // allow NPC damage to other entities if sleeper protection is off
            }

            var attacker = hitInfo.Initiator as BasePlayer;

            if (attacker.IsValid())
            {
                if (attacker.isMounted)
                {
                    if (!EvaluateRules(entity, attacker.GetMounted(), ruleSet, false))
                    {
                        return false;
                    }
                }

                if (entity is AdvancedChristmasLights)
                {
                    if (entity.OwnerID == 0)
                    {
                        return attacker.CanBuild();
                    }
                }
                else if (entity is GrowableEntity)
                {
                    if (attacker.CanBuild())
                    {
                        return true;
                    }

                    var ge = entity as GrowableEntity;
                    var planter = ge.GetPlanter();

                    return planter == null || !planter.OwnerID.IsSteamId() || planter.OwnerID == attacker.userID;
                }
                else if (entity is BuildingBlock)
                {
                    var block = entity as BuildingBlock;

                    if (block.grade == BuildingGrade.Enum.Twigs)
                    {
                        if (ruleSet.HasFlag(RuleFlags.TwigDamageRequiresOwnership)) // Allow twig damage by owner or anyone authed if ruleset flag is set
                        {
                            if (entity.OwnerID == attacker.userID)
                            {
                                return true;
                            }

                            return attacker.IsBuildingAuthed(entity.transform.position, entity.transform.rotation, entity.bounds);
                        }

                        if (ruleSet.HasFlag(RuleFlags.TwigDamage)) // Allow twig damage by anyone if ruleset flag is set
                        {
                            return true;
                        }
                    }
                }

                if (isVictim)
                {
                    if (ruleSet.HasFlag(RuleFlags.FriendlyFire) && victim.userID.IsSteamId() && AreAllies(attacker, victim))
                    {
                        if (trace) Trace("Initiator and target are allied players, with FriendlyFire flag set; allow and return", 1);
                        return true;
                    }

                    // allow sleeper damage by admins if configured
                    if (ruleSet.HasFlag(RuleFlags.AdminsHurtSleepers) && attacker.IsAdmin && victim.IsSleeping())
                    {
                        if (trace) Trace("Initiator is admin player and target is sleeping player, with AdminsHurtSleepers flag set; allow and return", 1);
                        return true;
                    }

                    // allow Human NPC damage if configured
                    if (ruleSet.HasFlag(RuleFlags.HumanNPCDamage) && IsHumanNPC(attacker, victim))
                    {
                        if (trace) Trace("Initiator or target is HumanNPC, with HumanNPCDamage flag set; allow and return", 1);
                        return true;
                    }
                }
                else if (ruleSet.HasFlag(RuleFlags.AuthorizedDamage) && !entity.IsNpc) // ignore checks if authorized damage enabled (except for players and npcs)
                {
                    if (ruleSet.HasFlag(RuleFlags.AuthorizedDamageRequiresOwnership) && entity.OwnerID != attacker.userID && CanAuthorize(entity, attacker, ruleSet))
                    {
                        if (trace) Trace("Initiator is player who does not own non-player target; block and return", 1);
                        return false;
                    }

                    if (CheckAuthorized(entity, attacker, ruleSet))
                    {
                        if (entity is SamSite)
                        {
                            if (trace) Trace("Target is SamSite; evaluate and return", 1);
                            return EvaluateRules(entity, hitInfo, ruleSet);
                        }
                        if (trace) Trace("Initiator is player with authorization over non-player target; allow and return", 1);
                        return true;
                    }
                }
            }

            if (trace) Trace("No match in pre-checks; evaluating RuleSet rules...", 1);
            return EvaluateRules(entity, hitInfo, ruleSet);
        }

        private bool HandleHelicopter(RuleSet ruleSet, BaseCombatEntity entity, object heli, bool isVictim)
        {
            if (isVictim)
            {
                var victim = entity as BasePlayer;

                if (ruleSet.HasFlag(RuleFlags.NoHeliDamageSleepers))
                {
                    if (trace) Trace($"Initiator is heli, and target is player; flag check results: { (victim.IsSleeping() ? "victim is sleeping; block and return" : "victim is not sleeping; continue checks") }", 1);
                    if (victim.IsSleeping()) return false;
                }

                if (trace) Trace($"Initiator is heli, and target is player; flag check results: { (ruleSet.HasFlag(RuleFlags.NoHeliDamagePlayer) ? "flag set; block and return" : "flag not set; block and return") }", 1);
                return !ruleSet.HasFlag(RuleFlags.NoHeliDamagePlayer);
            }
            if (entity is MiningQuarry)
            {
                if (trace) Trace($"Initiator is heli, and target is quarry; flag check results: { (ruleSet.HasFlag(RuleFlags.NoHeliDamageQuarry) ? "flag set; block and return" : "flag not set; allow and return") }", 1);
                return !ruleSet.HasFlag(RuleFlags.NoHeliDamageQuarry);
            }
            if (entity is RidableHorse)
            {
                if (trace) Trace($"Initiator is heli, and target is ridablehorse; flag check results: { (ruleSet.HasFlag(RuleFlags.NoHeliDamageRidableHorses) ? "flag set; block and return" : "flag not set; allow and return") }", 1);
                return !ruleSet.HasFlag(RuleFlags.NoHeliDamageRidableHorses);
            }
            if (trace) Trace($"Initiator is heli, target is non-player; results: { ((bool)heli ? "allow and return" : "block and return") }", 1);
            //return EvaluateRules(entity, weapon, ruleSet);
            return (bool)heli;
        }

        public bool AreAllies(BasePlayer attacker, BasePlayer victim)
        {
            if (attacker.currentTeam != 0uL && attacker.Team.members.Contains(victim.userID))
            {
                return true;
            }

            if (Clans != null && Convert.ToBoolean(Clans?.Call("IsMemberOrAlly", attacker.userID, victim.userID)))
            {
                return true;
            }

            if (Friends != null && Convert.ToBoolean(Friends?.Call("AreFriends", attacker.UserIDString, victim.UserIDString)))
            {
                return true;
            }

            return false;
        }

        private const uint WALL_LOW_JPIPE = 310235277;

        private bool CanAuthorize(BaseEntity entity, BasePlayer attacker, RuleSet ruleSet)
        {
            if (entity is BaseVehicle && !EvaluateRules(entity, attacker, ruleSet, false))
            {
                return false;
            }

            if (entity.OwnerID == 0)
            {
                return entity is MiniCopter || entity.prefabID == WALL_LOW_JPIPE && !entity.enableSaving;
            }

            return entity is BuildingBlock || entity.PrefabName.Contains("building") || _deployables.Contains(entity.PrefabName);
        }

        private void InitializeDeployables()
        {
            foreach (var def in ItemManager.GetItemDefinitions())
            {
                var imd = def.GetComponent<ItemModDeployable>();
                if (imd == null || _deployables.Contains(imd.entityPrefab.resourcePath)) continue;
                _deployables.Add(imd.entityPrefab.resourcePath);
            }
        }

        // process rules to determine whether to allow damage
        private bool EvaluateRules(BaseEntity entity, BaseEntity attacker, RuleSet ruleSet, bool returnDefaultValue = true)
        {
            List<string> e0Groups = config.ResolveEntityGroups(attacker);
            List<string> e1Groups = config.ResolveEntityGroups(entity);

            if (trace)
            {
                Trace($"Initator EntityGroup matches: { (e0Groups == null || e0Groups.Count == 0 ? "none" : string.Join(", ", e0Groups.ToArray())) }", 2);
                Trace($"Target EntityGroup matches: { (e1Groups == null || e1Groups.Count == 0 ? "none" : string.Join(", ", e1Groups.ToArray())) }", 2);
            }

            return ruleSet.Evaluate(e0Groups, e1Groups, attacker, returnDefaultValue);
        }

        private bool EvaluateRules(BaseEntity entity, HitInfo hitInfo, RuleSet ruleSet)
        {
            return EvaluateRules(entity, hitInfo.Initiator ?? hitInfo.WeaponPrefab, ruleSet);
        }

        // checks an entity to see if it has a lock
        private object CheckLock(RuleSet ruleSet, BaseEntity entity, HitInfo hitInfo)
        {
            var slot = entity.GetSlot(BaseEntity.Slot.Lock); // check for lock

            if (slot == null || !slot.IsLocked())
            {
                return null; // no lock or unlocked, continue checks
            }

            // if HeliDamageLocked flag is false or NoHeliDamage flag, all damage is cancelled from immortal flag
            if (!ruleSet.HasFlag(RuleFlags.HeliDamageLocked) || ruleSet.HasFlag(RuleFlags.NoHeliDamage))
            {
                return false;
            }

            object heli = CheckHeliInitiator(ruleSet, hitInfo);

            return Convert.ToBoolean(heli); // cancel damage except from heli
    
// ... [Code preview truncated due to length. Full source in attached file] ...

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