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

FREE LootDefender : 2.2.8 2.2.8

Нет прав для скачивания
C#:
using Facepunch;
using Facepunch.Math;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
namespace Oxide.Plugins {
    [Info("Loot Defender", "Author Egor Blagov, Maintainer nivex", "2.2.8")]
    [Description("Defends loot from other players who dealt less damage than you.")]
    internal class LootDefender : RustPlugin {
        [PluginReference]
        Plugin PersonalHeli, Friends, Clans, RustRewards, HelpfulSupply, ShoppyStock, XLevels, XPerience, SkillTree;
        private const string ZEROMEMBERID = "0";
        private const ulong RR_EVENT = 8675309;
        private const ulong CONVOY_EVENT = 755446;
        private const ulong HARBOR_EVENT = 81182151852251420;
        private const ulong CARGO_TRAIN_EVENT = 1337422;
        private const ulong DEFENDER_SKIN_ID = 3790355587;
        private Dictionary<NetworkableId, List<DamageKey>> _apcAttackers = new();
        private Dictionary<NetworkableId, List<DamageKey>> _heliAttackers = new();
        private Dictionary<NetworkableId, ulong> _locked = new();
        private HashSet<NetworkableId> _heliCriticalHits = new();
        private List<NetworkableId> _personal = new();
        private List<NetworkableId> _boss = new();
        private StoredData data = new();
        private MonumentInfo launchSite;
        private List<MonumentInfo> harbors = new();
        private List<ulong> ownerids = new() {
            0, CARGO_TRAIN_EVENT, 3566257, 123425345634634
        }
        ;
        public enum DamageEntryType {
            Bradley, Corpse, Heli, NPC, None
        }
        public class Lockout {
            public double Bradley;
            public double Heli;
            public bool Any(int current) => Bradley > current || Heli > current;
        }
        private class StoredData {
            public Dictionary<string, Lockout> Lockouts = new();
            public Dictionary<string, UiHandler.Info> UI = new();
            [JsonConverter(typeof(NetworkableIdConverter<DamageInfo>))]
            public Dictionary<NetworkableId, DamageInfo> Damage = new();
            [JsonConverter(typeof(NetworkableIdConverter<LockInfo>))]
            public Dictionary<NetworkableId, LockInfo> LootLock = new();
            public Dictionary<string, HashSet<ulong>> Dinosaurs = new();
            internal LootDefender Instance;
            internal Configuration config => Instance.config;
            internal bool IsTypeEnabled(DamageEntryType damageEntryType) => damageEntryType switch {
                DamageEntryType.NPC => config.Npc.IsEnabledWithThreshold, DamageEntryType.Bradley => config.Bradley.IsEnabled, DamageEntryType.Heli => config.Helicopter.IsEnabled, _ => false
            }
            ;
            public void EnsureInitialized() {
                Dinosaurs ??= new();
                Lockouts ??= new();
                UI ??= new();
                foreach (var info in UI.Values) {
                    info?.EnsureInitialized();
                }
                LootLock ??= new();
                Damage ??= new();
            }
            public void ClearEntityOwners() {
                foreach (var (id, damageInfo) in Damage) {
                    ClearEntityOwner(id, damageInfo?.OwnerID ?? 0uL);
                }
                foreach (var (id, lockInfo) in LootLock) {
                    ClearEntityOwner(id, lockInfo?.damageInfo?.OwnerID ?? 0uL);
                }
            }
            private static void ClearEntityOwner(NetworkableId id, ulong ownerid) {
                if (!ownerid.IsSteamId()) {
                    return;
                }
                BaseEntity entity = BaseNetworkable.serverEntities.Find(id) as BaseEntity;
                ClearEntityOwner(entity, ownerid);
            }
            private static void ClearEntityOwner(BaseEntity entity, ulong ownerid) {
                if (entity != null && !entity.IsDestroyed && entity.OwnerID == ownerid) {
                    entity.OwnerID = 0uL;
                }
            }
            public void Sanitize() {
                EnsureInitialized();
                using var dps = Pool.Get<PooledList<KeyValuePair<NetworkableId, DamageInfo>>>();
                dps.AddRange(Damage);
                foreach (var (id, damageInfo) in dps) {
                    if (damageInfo == null) {
                        Damage.Remove(id);
                        continue;
                    }
                    damageInfo.Instance = Instance;
                    BaseEntity entity = BaseNetworkable.serverEntities.Find(id) as BaseEntity;
                    if (damageInfo.damageKeys == null) {
                        ClearEntityOwner(entity, damageInfo.OwnerID);
                        Damage.Remove(id);
                        Instance._locked.Remove(id);
                        continue;
                    }
                    damageInfo.damageKeys.RemoveAll(x => x == null || x.damageEntry == null || !x.userid.IsSteamId());
                    if (!HasNetworkId(entity) || damageInfo.damageKeys.Count == 0 || !IsTypeEnabled(damageInfo.damageEntryType)) {
                        if (damageInfo.OwnerID.IsSteamId()) {
                            ClearEntityOwner(entity, damageInfo.OwnerID);
                        }
                        Damage.Remove(id);
                        Instance._locked.Remove(id);
                        continue;
                    }
                    foreach (var x in damageInfo.damageKeys) {
                        x.attacker = RelationshipManager.FindByID(x.userid);
                    }
                    damageInfo._id = id;
                    damageInfo._entity = entity;
                    damageInfo.maxHealth = entity.MaxHealth();
                    damageInfo._position = entity.transform.position;
                    if (damageInfo.OwnerID.IsSteamId()) {
                        entity.OwnerID = damageInfo.OwnerID;
                        Instance._locked[id] = damageInfo.OwnerID;
                    }
                    else damageInfo.OwnerID = 0uL;
                    damageInfo.Start();
                }
                using var locks = Pool.Get<PooledList<KeyValuePair<NetworkableId, LockInfo>>>();
                locks.AddRange(LootLock);
                foreach (var (id, lockInfo) in locks) {
                    BaseEntity entity = BaseNetworkable.serverEntities.Find(id) as BaseEntity;
                    if (lockInfo?.damageInfo != null) {
                        lockInfo.damageInfo.Instance = Instance;
                    }
                    if (lockInfo?.damageInfo?.damageKeys != null) {
                        lockInfo.damageInfo.damageKeys.RemoveAll(x => x == null || x.damageEntry == null || !x.userid.IsSteamId());
                    }
                    if (!HasNetworkId(entity) || !TryGetOwner(lockInfo, out ulong ownerid) || !IsTypeEnabled(lockInfo.damageInfo.damageEntryType)) {
                        Instance.RemoveLootLock(id, entity, lockInfo);
                        continue;
                    }
                    foreach (var x in lockInfo.damageInfo.damageKeys) {
                        x.attacker = RelationshipManager.FindByID(x.userid);
                    }
                    lockInfo.damageInfo._position = entity.transform.position;
                    entity.OwnerID = ownerid;
                    Instance.ScheduleLootLock(id, entity, lockInfo);
                }
            }
        }
        private class NetworkableIdConverter<TValue> : JsonConverter {
            public override bool CanConvert(Type objectType) {
                return objectType == typeof(Dictionary<NetworkableId, TValue>);
            }
            public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
                if (reader.TokenType == JsonToken.Null) {
                    return new Dictionary<NetworkableId, TValue>();
                }
                if (reader.TokenType != JsonToken.StartObject) {
                    throw new JsonSerializationException($"Expected an object while reading {objectType.Name}, but found {reader.TokenType}.");
                }
                var dict = existingValue as Dictionary<NetworkableId, TValue> ?? new();
                dict.Clear();
                while (reader.Read()) {
                    if (reader.TokenType == JsonToken.EndObject) {
                        return dict;
                    }
                    if (reader.TokenType != JsonToken.PropertyName) {
                        throw new JsonSerializationException($"Expected a NetworkableId property name, but found {reader.TokenType}.");
                    }
                    string key = reader.Value as string;
                    if (!reader.Read()) {
                        throw new JsonSerializationException("Unexpected end while reading a NetworkableId dictionary value.");
                    }
                    if (!TryParse(key, out ulong value)) {
                        reader.Skip();
                        continue;
                    }
                    dict[new NetworkableId(value)] = (TValue)serializer.Deserialize(reader, typeof(TValue));
                }
                throw new JsonSerializationException("Unexpected end while reading a NetworkableId dictionary.");
            }
            public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
                if (value == null) {
                    writer.WriteNull();
                    return;
                }
                var dict = (Dictionary<NetworkableId, TValue>)value;
                writer.WriteStartObject();
                foreach (var pair in dict) {
                    writer.WritePropertyName(pair.Key.Value.ToString(CultureInfo.InvariantCulture));
                    serializer.Serialize(writer, pair.Value);
                }
                writer.WriteEndObject();
            }
        }
        private class DamageEntry {
            public float DamageDealt;
            public DateTime Timestamp;
            public string MemberId;
            public DamageEntry() {
            }
            public DamageEntry(string memberId) {
                Timestamp = DateTime.Now;
                MemberId = memberId;
            }
            public bool IsOutdated(int timeout) => timeout > 0 && DateTime.Now.Subtract(Timestamp).TotalSeconds >= timeout;
        }
        private class DamageKey {
            public ulong userid;
            public string name;
            public DamageEntry damageEntry;
            internal BasePlayer attacker;
            public DamageKey() {
            }
            public DamageKey(BasePlayer attacker) {
                this.attacker = attacker;
                userid = attacker.userID;
                name = attacker.displayName;
            }
            public BasePlayer GetAttacker() {
                if (attacker == null) attacker = RelationshipManager.FindByID(userid);
                return attacker;
            }
            public bool TryGetAttacker(out BasePlayer result) {
                result = GetAttacker();
                return result != null;
            }
        }
        private class DamageInfo {
            public List<DamageKey> damageKeys = new();
            internal Dictionary<ulong, BasePlayer> interact = new();
            internal List<ulong> participants = new();
            public DamageEntryType damageEntryType = DamageEntryType.None;
            public string NPCName;
            public ulong OwnerID;
            public ulong SkinID;
            public DateTime start;
            internal int _lockTime;
            internal BaseEntity _entity;
            internal Vector3 _position;
            internal Vector3 lastAttackedPosition;
            internal NetworkableId _id;
            internal float maxHealth;
            internal Timer _timer;
            internal List<DamageKey> keys = new();
            internal List<DamageGroup> damageGroups;
            internal LootDefender Instance;
            internal Configuration config => Instance.config;
            internal float FullDamage {
                get {
                    float damage = 0;
                    for (int i = 0; i < damageKeys.Count; i++) {
                        damage += damageKeys[i].damageEntry.DamageDealt;
                    }
                    return damage;
                }
            }
            public DamageInfo() {
            }
            public DamageInfo(LootDefender instance, DamageEntryType damageEntryType, string NPCName, BaseEntity entity, DateTime start) {
                Instance = instance;
                SkinID = entity.skinID;
                _entity = entity;
                _id = entity.net.ID;
                maxHealth = entity.MaxHealth();
                this.damageEntryType = damageEntryType;
                this.NPCName = NPCName;
                this.start = start;
                Start();
            }
            public void Start() {
                DestroyTimer();
                _lockTime = Instance.GetLockTime(damageEntryType);
                if (_lockTime > 0) {
                    _timer = Instance.timer.Every(1f, CheckExpiration);
                }
            }
            public void DestroyTimer() {
                if (_timer is {
                    Destroyed : false
                }
                ) {
                    _timer.Destroy();
                }
                _timer = null;
            }
            private void CheckExpiration() {
                foreach (var x in damageKeys) {
                    if (x.damageEntry.IsOutdated(_lockTime)) {
                        if (x.userid == OwnerID) {
                            Unlock();
                        }
                        keys.Add(x);
                    }
                }
                foreach (var x in keys) {
                    damageKeys.Remove(x);
                }
                keys.Clear();
                if (damageKeys.Count == 0) {
                    Instance.RemoveDamageInfo(_id, this);
                }
            }
            public void Unlock() {
                if (!Instance._locked.Remove(_id, out ulong userid)) {
                    OwnerID = 0;
                    return;
                }
                bool isDestroyed = !HasNetworkId(_entity);
                if (!isDestroyed && _entity.OwnerID == userid)  // don't reset the owner when another plugin changed ours
                {
                    _entity.OwnerID = 0;
                }
                OwnerID = 0;
                Interface.CallHook("OnUnlockedEntity", _entity, userid, _id.Value, isDestroyed);
                string grid = PositionToGrid(_position);
                if (damageEntryType == DamageEntryType.Bradley && config.Bradley.Messages.NotifyChat) {
                    foreach (var target in BasePlayer.activePlayerList) {
                        CreateMessage(target, "BradleyUnlocked", grid);
                    }
                }
                if (damageEntryType == DamageEntryType.Heli && config.Helicopter.Messages.NotifyChat) {
                    foreach (var target in BasePlayer.activePlayerList) {
                        CreateMessage(target, "HeliUnlocked", grid);
                    }
                }
                if (damageEntryType == DamageEntryType.NPC && config.Npc.Messages.NotifyChat) {
                    foreach (var target in BasePlayer.activePlayerList) {
                        CreateMessage(target, "NpcUnlocked", NPCName, grid);
                    }
                }
            }
            private void Lock(BaseEntity entity, ulong userid) {
                Instance._locked[_id] = entity.OwnerID = OwnerID = userid;
                _position = entity.transform.position;
                Interface.CallHook("OnLockedEntity", entity, OwnerID, _id.Value, _position);
            }
            public void AddDamage(BaseCombatEntity entity, BasePlayer attacker, DamageEntry entry, float amount) {
                entry.DamageDealt += amount;
                entry.Timestamp = DateTime.Now;
                _position = entity.transform.position;
                lastAttackedPosition = attacker.transform.position;
                if (SkinID == 0) {
                    SkinID = entity.skinID;
                }
                if (damageEntryType == DamageEntryType.NPC && !Instance.CanLockNpc(entity)) {
                    return;
                }
                if (entity.OwnerID.IsSteamId()) {
                    OwnerID = entity.OwnerID;
                }
                if (OwnerID != 0uL) {
                    return;
                }
                float damage = 0f;
                var grid = PositionToGrid(entity.transform.position);
                if (entry.MemberId != ZEROMEMBERID) {
                    foreach (var x in damageKeys) {
                        if (x.damageEntry.MemberId == entry.MemberId) {
                            damage += x.damageEntry.DamageDealt;
                        }
                    }
                }
                else damage = entry.DamageDealt;
                if (config.Helicopter.IsEnabled && entity is PatrolHelicopter) {
                    if (damage >= maxHealth * config.Helicopter.Threshold && !Instance.HasPermission(attacker, "lootdefender.bypasshelilock")) {
                        if (config.Helicopter.Messages.NotifyLocked == true) {
                            foreach (var target in BasePlayer.activePlayerList) {
                                CreateMessage(target, "Locked Heli", grid, attacker.displayName);
                            }
                        }
                        Lock(entity, attacker.userID);
                    }
                }
                else if (config.Bradley.IsEnabled && entity is BradleyAPC) {
                    if (damage >= maxHealth * config.Bradley.Threshold && !Instance.HasPermission(attacker, "lootdefender.bypassbradleylock")) {
                        if (config.Bradley.Messages.NotifyLocked == true) {
                            foreach (var target in BasePlayer.activePlayerList) {
                                CreateMessage(target, "Locked Bradley", grid, attacker.displayName);
                            }
                        }
                        Lock(entity, attacker.userID);
                    }
                }
                else if (config.Npc.Enabled && entity is BasePlayer npc && Instance.CanLockNpc(npc)) {
                    if (!npc.userID.IsSteamId() && damage >= maxHealth * config.Npc.Threshold && !Instance.HasPermission(attacker, "lootdefender.bypassnpclock")) {
                        if (config.Npc.Messages.NotifyLocked == true) {
                            foreach (var target in BasePlayer.activePlayerList) {
                                CreateMessage(target, "Locked Npc", grid, npc.displayName, attacker.displayName);
                            }
                        }
                        Lock(entity, attacker.userID);
                    }
                }
            }
            public bool TryGet(ulong userid, out DamageEntry damageEntry) {
                foreach (var x in damageKeys) {
                    if (x.userid == userid) {
                        damageEntry = x.damageEntry;
                        return true;
                    }
                }
                damageEntry = null;
                return false;
            }
            public DamageEntry Get(BasePlayer attacker) {
                if (!TryGet(attacker.userID, out var entry)) {
                    string id = Instance.GetMemberId(attacker);
                    damageKeys.Add(new(attacker) {
                        damageEntry = entry = new(id),
                    }
                    );
                }
                return entry;
            }
            public bool isKilled;
            public void OnKilled(Vector3 position, HitInfo info, float distance) {
                if (isKilled) return;
                isKilled = true;
                SetCanInteract();
                DisplayDamageReport();
                FindLooters(position, info, distance);
            }
            private void FindLooters(Vector3 position, HitInfo info, float distance) {
                var weapon = info?.Weapon?.GetItem()?.info?.shortname ?? info?.WeaponPrefab?.ShortPrefabName ?? "";
                HashSet<ulong> looters = new();
                HashSet<ulong> users = new();
                foreach (var x in damageKeys) {
                    if (CanInteract(x.GetAttacker(), x.userid)) {
                        if (TryGet(x.userid, out var entry) && entry.DamageDealt > 0) {
                            users.Add(x.userid);
                        }
                        looters.Add(x.userid);
                    }
                }
                foreach (var userid in users) {
                    Instance.GiveXpReward(_entity, this, userid, weapon, distance, users.Count);
                    Instance.GiveRustReward(_entity, this, userid, weapon, users.Count);
                    Instance.GiveShopReward(_entity, this, userid, weapon, distance, users.Count);
                }
                if (damageEntryType is DamageEntryType.Bradley or DamageEntryType.Heli) {
                    Instance.LockoutLooters(looters, position, damageEntryType, SkinID);
                }
            }
            public void DisplayDamageReport() {
                if (damageEntryType is DamageEntryType.Bradley or DamageEntryType.Heli) {
                    foreach (var target in BasePlayer.activePlayerList) {
                        if (CanDisplayReport(target)) {
                            Instance.Message(target, GetDamageReport(target.userID));
                        }
                    }
                }
                else if (damageEntryType == DamageEntryType.NPC) {
                    foreach (var x in damageKeys) {
                        if (CanDisplayReport(x.GetAttacker())) {
                            Instance.Message(x.GetAttacker(), GetDamageReport(x.userid));
                        }
                    }
                }
            }
            private bool CanDisplayReport(BasePlayer target) {
                if (target == null || !target.IsConnected) {
                    return false;
                }
                if (damageEntryType == DamageEntryType.Bradley) {
                    if (config.Bradley.Messages.NotifyKiller && IsParticipant(target)) {
                        return true;
                    }
                    return config.Bradley.Messages.NotifyChat;
                }
                if (damageEntryType == DamageEntryType.Heli) {
                    if (config.Helicopter.Messages.NotifyKiller && IsParticipant(target)) {
                        return true;
                    }
                    return config.Helicopter.Messages.NotifyChat;
                }
                if (damageEntryType == DamageEntryType.NPC) {
                    if (config.Npc.Messages.NotifyKiller && IsParticipant(target)) {
                        return true;
                    }
                    return config.Npc.Messages.NotifyChat;
                }
                return false;
            }
            public void SetCanInteract() {
                var damageGroups = GetDamageGroups();
                if (damageGroups.Count > 0) {
                    using var ownerDamageGroups = GetOwnerDamageGroups(damageGroups);
                    foreach (var damageGroup in damageGroups) {
                        if (ownerDamageGroups.Contains(damageGroup) || Instance.IsAlly(damageGroup.FirstDamagerDealer.attacker, damageGroup.FirstDamagerDealer.userid, OwnerID)) {
                            interact[damageGroup.FirstDamagerDealer.userid] = damageGroup.FirstDamagerDealer.GetAttacker();
                        }
                        else {
                            var damage = TryGet(damageGroup.FirstDamagerDealer.userid, out var x) ? x.DamageDealt : 0f;
                            var ratio = damage > 0 && FullDamage > 0 ? damage / FullDamage : 0;
                            float threshold = config.Npc.Threshold;
                            if (damageEntryType == DamageEntryType.Bradley) {
                                threshold = config.Bradley.Threshold;
                            }
                            else if (damageEntryType == DamageEntryType.Heli) {
                                threshold = config.Helicopter.Threshold;
                            }
                            if (OwnerID == 0 && ratio >= threshold) {
                                OwnerID = damageGroup.FirstDamagerDealer.userid;
                                interact[damageGroup.FirstDamagerDealer.userid] = damageGroup.FirstDamagerDealer.GetAttacker();
                            }
                            else {
                                participants.Add(damageGroup.FirstDamagerDealer.userid);
                            }
                        }
                    }
                }
                this.damageGroups = damageGroups;
            }
            private string Localize(string key, ulong id, params object[] args) => Instance.Localize(key, id.ToString(), args);
            private string Localize(string key, string id, params object[] args) => Instance.Localize(key, id, args);
            private void CreateMessage(BasePlayer player, string key, params object[] args) => Instance.CreateMessage(player, key, args);
            private StringBuilder sb = new();
            public string GetDamageReport(ulong userid) {
                var nameKey = damageEntryType == DamageEntryType.Bradley ? Localize("BradleyAPC", userid) : damageEntryType == DamageEntryType.Heli ? Localize("Helicopter", userid) : NPCName;
                var firstDamageDealer = string.Empty;
                sb.Length = 0;
                sb.AppendLine($"{Localize("DamageReport", userid, $"<color= {
                    config.Report.Ok
                }
                > {
                    nameKey
                }
                </color>")}:");
                if (damageEntryType is DamageEntryType.Bradley or DamageEntryType.Heli) {
                    var seconds = Math.Ceiling((DateTime.Now - start).TotalSeconds);
                    sb.AppendLine($"{Localize("DamageTime", userid, nameKey, seconds)}");
                }
                if (damageGroups.Count > 0) {
                    foreach (var damageGroup in damageGroups) {
                        if (interact.ContainsKey(damageGroup.FirstDamagerDealer.userid)) {
                            sb.Append($"<color={config.Report.Ok}>√</color> ");
                            firstDamageDealer = damageGroup.FirstDamagerDealer.name;
                        }
                        else {
                            sb.Append($"<color={config.Report.NotOk}>X</color> ");
                        }
                        sb.Append($"{damageGroup.ToReport(Instance, damageGroup.FirstDamagerDealer, this)}\n");
                    }
                    if (damageEntryType == DamageEntryType.NPC && damageGroups.Count > 1 && !string.IsNullOrEmpty(firstDamageDealer)) {
                        sb.Append($" {Localize("FirstLock", userid, firstDamageDealer, config.Npc.Threshold * 100f)}");
                    }
                }
                return sb.ToString();
            }
            public bool IsParticipant(BasePlayer player) {
                return participants.Contains(player.userID) || CanInteract(player, player.userID);
            }
            public bool CanInteract(BasePlayer player, ulong userid) {
                if (damageEntryType == DamageEntryType.NPC && !config.Npc.Enabled) {
                    return true;
                }
                if (damageGroups == null) {
                    interact.Clear();
                    participants.Clear();
                    SetCanInteract();
                }
                if (interact.Count == 0 || interact.ContainsKey(userid)) {
                    return true;
                }
                if (Instance.IsAlly(player, userid, OwnerID)) {
                    interact.Add(userid, player);
                    return true;
                }
                return false;
            }
            private PooledList<DamageGroup> GetOwnerDamageGroups(List<DamageGroup> damageGroups) {
                var ownerDamageGroups = Pool.Get<PooledList<DamageGroup>>();
                if (damageGroups.Count == 0) {
                    return ownerDamageGroups;
                }
                foreach (var damageGroup in damageGroups) {
                    foreach (var userid in damageGroup.Players) {
                        BasePlayer player = RelationshipManager.FindByID(userid);
                        if (Instance.IsAlly(player, userid, OwnerID)) {
                            ownerDamageGroups.Add(damageGroup);
                            break;
                        }
                    }
                }
                return ownerDamageGroups;
            }
            private List<DamageGroup> GetDamageGroups() {
                List<DamageGroup> damageGroups = new();
                foreach (var x in damageKeys) {
                    damageGroups.Add(new(x));
                }
                damageGroups.Sort((x, y) => y.TotalDamage.CompareTo(x.TotalDamage));
                return damageGroups;
            }
        }
        private class LockInfo {
            public DamageInfo damageInfo;
            public DateTime ExpiresAt;
            internal bool IsLockOutdated => ExpiresAt != default && DateTime.Now >= ExpiresAt;
            public LockInfo() {
            }
            public LockInfo(DamageInfo damageInfo, DateTime expiresAt) {
                ExpiresAt = expiresAt;
                this.damageInfo = damageInfo;
            }
            public bool CanInteract(BasePlayer target) => damageInfo.CanInteract(target, target.userID);
            public string GetDamageReport(ulong userId) => damageInfo.GetDamageReport(userId);
        }
        private class DamageGroup {
            public float TotalDamage;
            public DamageKey FirstDamagerDealer;
            private List<ulong> additionalPlayers = new();
            internal List<ulong> Players {
                get {
                    List<ulong> players = new() {
                        FirstDamagerDealer.userid
                    }
                    ;
                    foreach (var userid in additionalPlayers) {
                        if (!players.Contains(userid)) {
                            players.Add(userid);
                        }
                    }
                    return players;
                }
            }
            public DamageGroup() {
            }
            public DamageGroup(DamageKey x) {
                TotalDamage = x.damageEntry.DamageDealt;
                FirstDamagerDealer = x;
                if (RelationshipManager.ServerInstance.playerToTeam.TryGetValue(x.userid, out var team)) {
                    for (int i = 0; i < team.members.Count; i++) {
                        ulong member = team.members[i];
                        if (member == x.userid || additionalPlayers.Contains(member)) {
                            continue;
                        }
                        additionalPlayers.Add(member);
                    }
                }
                if (x.TryGetAttacker(out BasePlayer attacker) && attacker.clanId != 0 && TryGetClan(attacker, out IClan clan)) {
                    for (int i = 0; i < clan.Members.Count; i++) {
                        ulong member = clan.Members[i].SteamId;
                        if (member == x.userid || additionalPlayers.Contains(member)) {
                            continue;
                        }
                        additionalPlayers.Add(member);
                    }
                    if (clan.Creator != x.userid && !additionalPlayers.Contains(clan.Creator)) {
                        additionalPlayers.Add(clan.Creator);
                    }
                }
            }
            public string ToReport(LootDefender Instance, DamageKey damageKey, DamageInfo damageInfo) {
                var damage = damageInfo.TryGet(damageKey.userid, out var x) ? x.DamageDealt : 0f;
                var percent = damage > 0 && damageInfo.FullDamage > 0 ? damage / damageInfo.FullDamage * 100 : 0;
                var color = additionalPlayers.Count == 0 ? Instance.config.Report.SinglePlayer : Instance.config.Report.Team;
                var damageLine = Instance.Localize("Format", damageKey.userid.ToString(), damage, percent);
                return $"<color={color}>{damageKey.name}</color> {damageLine}";
            }
        }
        public string GetMemberId(BasePlayer attacker) {
            string id = attacker switch {
                {
                    clanId : not 0
                }
                => attacker.clanId.ToString(), {
                    currentTeam : not 0
                }
                => attacker.currentTeam.ToString(), _ => Clans?.Call("GetClanOf", attacker.userID.Get()) as string
            }
            ;
            if (string.IsNullOrEmpty(id)) return ZEROMEMBERID;
            return id;
        }
        public void UpdateMemberId(BasePlayer player, string id) {
            if (player == null) {
                return;
            }
            if (string.IsNullOrEmpty(id) || id == ZEROMEMBERID) {
                id = GetMemberId(player);
            }
            if (id == ZEROMEMBERID) {
                return;
            }
            allyLookupTimes.Clear();
            foreach (DamageInfo damageInfo in data.Damage.Values) {
                if (damageInfo.TryGet(player.userID, out DamageEntry damageEntry) && (string.IsNullOrEmpty(damageEntry.MemberId) || damageEntry.MemberId == ZEROMEMBERID)) {
                    damageEntry.MemberId = id;
                }
            }
        }
#region Hooks
        private void OnClanMemberJoined(string tag, ulong joining, List<ulong> members) {
            BasePlayer player = RelationshipManager.FindByID(joining);
            if (player != null) {
                UpdateMemberId(player, tag);
            }
        }
        private void OnTeamCreated(BasePlayer player, RelationshipManager.PlayerTeam team) {
            if (player != null) {
                UpdateMemberId(player, team.teamID.ToString());
            }
        }
        private void OnTeamAcceptInvite(RelationshipManager.PlayerTeam team, BasePlayer player) {
            if (player == null) {
                return;
            }
            player.Invoke(() => {
                if (!player.IsDestroyed && player.currentTeam == team?.teamID) {
                    UpdateMemberId(player, team.teamID.ToString());
                }
            }
            , 0.01f);
        }
        private object OnTeamLeave(RelationshipManager.PlayerTeam team, BasePlayer player) => HandleTeam(player?.userID ?? 0uL) ? (object)true : null;
        private object OnTeamKick(RelationshipManager.PlayerTeam team, BasePlayer player, ulong targetId) => HandleTeam(targetId) ? (object)true : null;
        private void OnServerSave() {
            timer.Once(15f, SaveData);
        }
        private void Init() {
            UI = new() {
                Instance = this
            }
            ;
            Unsubscribe(nameof(OnEventTrigger));
            Unsubscribe();
            if (!string.IsNullOrEmpty(config.Lockout.Command)) AddCovalenceCommand(config.Lockout.Command, nameof(CommandLockouts));
            if (!string.IsNullOrEmpty(config.UI.Command)) AddCovalenceCommand(config.UI.Command, nameof(CommandUI));
            AddCovalenceCommand("lo", nameof(CommandLootDefender));
            AddCovalenceCommand("lootdefender", nameof(CommandLootDefender));
            RegisterPermissions();
            LoadData();
        }
        private void OnServerInitialized(bool initial) {
            if (initial) {
                data.ClearEntityOwners();
                data.Damage.Clear();
                data.LootLock.Clear();
                _locked.Clear();
                SaveData();
            }
            else {
                data.Sanitize();
            }
            if (config.Hackable.Enabled) {
                Subscribe(nameof(CanHackCrate));
                Subscribe(nameof(OnGuardedCrateEventEnded));
            }
            if (config.SupplyDrop.Lock) {
                if (config.SupplyDrop.LockTime > 0) {
                    if (config.SupplyDrop.NpcRandomRaids) {
                        Subscribe(nameof(OnRandomRaidWin));
                    }
                    Subscribe(nameof(OnSupplyDropLanded));
                }
                if (config.SupplyDrop.Excavator) {
                    Subscribe(nameof(OnExcavatorSuppliesRequested));
                }
                if (config.SupplyDrop.HelpfulSupply && HelpfulSupply != null) {
                    Subscribe(nameof(OnEntitySpawned));
                }
                Subscribe(nameof(OnExplosiveDropped));
                Subscribe(nameof(OnExplosiveThrown));
                Subscribe(nameof(OnSupplyDropDropped));
                Subscribe(nameof(OnCargoPlaneSignaled));
            }
            if (config.SupplyDrop.DestroyTime > 0f || config.CH47Gibs) {
                Subscribe(nameof(OnEntitySpawned));
            }
            if (config.Npc.BossMonster) {
                Unsubscribe(nameof(OnBossSpawn));
                Unsubscribe(nameof(OnBossKilled));
            }
            if (!config.Bradley.LockPersonal) {
                Subscribe(nameof(OnPersonalApcSpawned));
            }
            if (!config.Helicopter.LockPersonal) {
                Subscribe(nameof(OnPersonalHeliSpawned));
            }
            if (config.UI.Bradley.Enabled || config.UI.Heli.Enabled) {
                foreach (var player in BasePlayer.activePlayerList) {
                    UI.ShowLockouts(player);
                }
                Subscribe(nameof(OnPlayerSleepEnded));
                Subscribe(nameof(OnCuiDraggableDrag));
            }
            if (config.BradleyOrHelicopterIsEnabled) {
                Subscribe(nameof(OnCrateSpawned));
            }
            if (config.BradleyOrHelicopterIsEnabled || config.Hackable.Laptop) {
                Subscribe(nameof(OnPlayerAttack));
            }
            if (config.Lockout.F15) {
                Subscribe(nameof(OnEventTrigger));
            }
            if (config.Helicopter.IsEnabled) {
                Subscribe(nameof(OnPatrolHelicopterKill));
            }
            Subscribe(nameof(OnEntityTakeDamage));
            Subscribe(nameof(OnEntityDeath));
            Subscribe(nameof(OnEntityKill));
            Subscribe(nameof(CanLootEntity));
            SetupLaunchSite();
        }
        private bool IsF15EventActive;
        private void OnEventTrigger(TriggeredEventPrefab prefab) {
            if (config.Lockout.F15 && !IsF15EventActive && prefab.name == "assets/bundled/prefabs/world/event_f15e.prefab") {
                Puts("F15 event has started; bypassing player lockouts!");
                IsF15EventActive = true;
            }
        }
        private void Unload() {
            UI.DestroyAllLockoutUI();
            SaveData();
        }
        private void OnPlayerSleepEnded(BasePlayer player) => UI.ShowLockouts(player);
        private void OnCuiDraggableDrag(BasePlayer player, string name, Vector3 position, CommunityEntity.DraggablePositionSendType dragType) {
            if (!HasNetworkId(player)) {
                return;
            }
            UiType uiType = UI.GetUiType(name);
            if (uiType == UiType.Invalid) {
                return;
            }
            UiOffsets offsets = UI.GetOffsets(player.UserIDString, uiType, true);
            switch (dragType) {
                case CommunityEntity.DraggablePositionSendType.NormalizedScreen : offsets.SetAnchor(new Vector2(position.x, 1f - position.y));
                break;
                case CommunityEntity.DraggablePositionSendType.NormalizedParent : offsets.SetAnchor(new Vector2(position.x, position.y));
                break;
                case CommunityEntity.DraggablePositionSendType.Relative : case CommunityEntity.DraggablePositionSendType.RelativeAnchor : offsets.Move(new Vector2(position.x, position.y));
                break;
                default : return;
            }
            UI.SaveOffsetData();
            UI.UpdateLockoutUI(player, uiType);
        }
        private object OnPlayerAttack(BasePlayer attacker, HitInfo info) {
            if (attacker == null || HasPermission(attacker, "lootdefender.bypass.damage") || info == null) {
                return null;
            }
            if (config.Hackable.Laptop && info.HitBone == 242862488 && info.HitEntity is HackableLockedCrate crate && IsDefended(crate))  // laptopcollision
            {
                info.HitBone = 0;
                return null;
            }
            if (!config.BradleyOrHelicopterIsEnabled) {
                return null;
            }
            if (!info.HitEntity.Is(out ServerGib gibs) || !TryGetNetworkId(gibs, out NetworkableId id)) {
                return null;
            }
            if (!data.LootLock.TryGetValue(id, out var lockInfo)) {
                return null;
            }
            if (!TryGetOwner(lockInfo, out ulong ownerid)) {
                RemoveLootLock(id, gibs, lockInfo);
                return null;
            }
            if (gibs.OwnerID != ownerid) {
                if (DebugMode) Puts("Restored ownership of '{0}' to {1} [was={2}, now={3}]", gibs.ShortPrefabName, GetPlayerName(ownerid), gibs.OwnerID, ownerid);
                gibs.OwnerID = ownerid;
            }
            if (lockInfo.CanInteract(attacker)) {
                return null;
            }
            if (CanMessage(attacker)) {
                CreateMessage(attacker, "CannotMine");
                Message(attacker, lockInfo.GetDamageReport(attacker.userID));
            }
            CancelDamage(info);
            return true;
        }
        private object OnPatrolHelicopterKill(PatrolHelicopter heli, HitInfo info) {
            if (info == null || !config.Helicopter.IsEnabled || !TryGetNetworkId(heli, out NetworkableId id) || heli.myAI == null || heli.myAI.isDead || !ShouldHandleHeli(heli, id)) {
                return null;
            }
            if (!TryGetPlayerAttacker(info, out BasePlayer attacker)) {
                return null;
            }
            if (CanTakeDamage(heli, id, info, attacker, DamageEntryType.Heli, _heliAttackers, false) is false) {
                return true;
            }
            _heliCriticalHits.Add(id);
            NextTick(() => _heliCriticalHits.Remove(id));
            return null;
        }
        private bool IsDamageBlocked(BaseCombatEntity entity, NetworkableId id, BasePlayer attacker, HitInfo info, DamageEntryType damageEntryType) {
            if (!_locked.TryGetValue(id, out ulong ownerid) || HasPermission(attacker, "lootdefender.bypass.damage") || IsAlly(attacker, ownerid) || !BlockDamage(damageEntryType)) {
                return false;
            }
            if (CanMessage(attacker)) {
                CreateMessage(attacker, "CannotDamageThis");
            }
            CancelDamage(info);
            return true;
        }
        private object CanTakeDamage(BaseCombatEntity entity, NetworkableId id, HitInfo info, BasePlayer attacker, DamageEntryType damageEntryType, Dictionary<NetworkableId, List<DamageKey>> source, bool add = true) {
            if (info.damageTypes.Total() <= 0f) {
                return null;
            }
            if (IsDamageBlocked(entity, id, attacker, info, damageEntryType)) {
                return false;
            }
            if (UI.GetLockoutTime(damageEntryType) <= 0d) {
                return null;
            }
            if (HasLockout(attacker, damageEntryType, entity.skinID)) {
                CancelDamage(info);
                return false;
            }
            if (add) {
                AddDamageAttacker(source, id, attacker);
            }
            return null;
        }
        private void AddDamageAttacker(Dictionary<NetworkableId, List<DamageKey>> source, NetworkableId id, BasePlayer attacker) {
            if (!source.TryGetValue(id, out var attackers)) {
                source[id] = attackers = new();
            }
            for (int i = 0; i < attackers.Count; i++) {
                if (attackers[i].userid == attacker.userID) {
                    return;
                }
            }
            attackers.Add(new(attacker));
        }
        private object OnEntityTakeDamage(PatrolHelicopter heli, HitInfo info) {
            if (info == null || !config.Helicopter.IsEnabled || !TryGetNetworkId(heli, out NetworkableId id) || heli.myAI == null || !ShouldHandleHeli(heli, id)) {
                return null;
            }
            bool criticalHit = _heliCriticalHits.Remove(id);
            if (!criticalHit && heli.myAI.isDead) {
                return null;
            }
            if (!TryGetPlayerAttacker(info, out BasePlayer attacker)) {
                return null;
            }
            if (criticalHit) {
                if (config.Lockout.Heli > 0d && info.damageTypes.Total() > 0f) {
                    AddDamageAttacker(_heliAttackers, id, attacker);
                }
            }
            else if (CanTakeDamage(heli, id, info, attacker, DamageEntryType.Heli, _heliAttackers) is false) {
                return true;
            }
            return OnEntityTakeDamageHandler(heli, id, info, attacker, DamageEntryType.Heli, string.Empty);
        }
        private object OnEntityTakeDamage(BradleyAPC apc, HitInfo info) {
            if (info == null || !config.Bradley.IsEnabled || !TryGetNetworkId(apc, out NetworkableId id) || !ShouldHandleBradley(apc, id)) {
                return null;
            }
            if (!TryGetPlayerAttacker(info, out BasePlayer attacker)) {
                return null;
            }
            if (CanTakeDamage(apc, id, info, attacker, DamageEntryType.Bradley, _apcAttackers) is false) {
                return true;
            }
            return OnEntityTakeDamageHandler(apc, id, info, attacker, DamageEntryType.Bradley, string.Empty);
        }
        private object OnEntityTakeDamage(BasePlayer player, HitInfo info) {
            if (info == null || !config.Npc.IsEnabledWithThreshold ||
// ... [Code preview truncated due to length. Full source in attached file] ...

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