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

FREE RaidNotes : 1.0.0 1.0.0

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Text.RegularExpressions;

using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Core.Configuration;
using Oxide.Core.Libraries.Covalence;
using Oxide.Game.Rust.Libraries.Covalence;

using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

using UnityEngine;
using Rust;
using Facepunch;

namespace Oxide.Plugins
{
    [Info("RaidNotes", "Calytic", "1.0.0", ResourceId = 2117)]
    [Description("Broadcasts raid activity to chat & more")]
    public class RaidNotes : RustPlugin
    {
        #region Variables

        [PluginReference]
        Plugin Discord, Slack, Clans, LustyMap;

        private DynamicConfigFile data;
        public static JsonSerializer SERIALIZER = new JsonSerializer();
        public static JsonConverter[] CONVERTERS = new JsonConverter[] { new UnityVector3Converter(), new DateTimeConverter()};
        Dictionary<string, bool> _raidableCache = new Dictionary<string, bool>();
        private int blockLayer = UnityEngine.LayerMask.GetMask(new string[] { "Player (Server)" });
        private static Dictionary<string, int> reverseItems = new Dictionary<string, int>();
        private static List<string> explosionRadiusPrefabs = new List<string>();
        private Dictionary<Raid, Timer> timers = new Dictionary<Raid, Timer>();
        private Dictionary<string, DateTime> detectionCooldowns = new Dictionary<string, DateTime>();
        static Regex _htmlRegex = new Regex("<.*?>", RegexOptions.Compiled);
        Color[] colors = new Color[7] { Color.blue, Color.cyan, Color.gray, Color.green, Color.magenta, Color.red, Color.yellow };

        private float raidDuration = 300f;
        private float raidDistance = 50f;
        private float shoulderHeight = 0.45f;
        private bool isNewSave = false;
        private float detectionDuration = 60f;
        private string slackType = "FancyMessage";
        private int logHours = 12;

        private bool trackExplosives = true;
        private bool checkEntityDamage, checkEntityDeath, announceGlobal, printToLog = true;
        private bool announceRaidStart, announceRaidEnd, announceToVictims, announceToSlack, announceToDiscord, announceToLustyMap, announceClan = false;
        private string announcePrefixColor = "orange";
        private string announceIcon;
        private string announceNameColor = "lightblue";
        private string announceClanColor = "#00eaff";
        private string lustyMapIcon = "special";
        private float lustyMapDuration = 10f;
        private int detectionDistance = 50;
        private float detectionCountdown = 1f;

        private Dictionary<string, object> weaponColors = new Dictionary<string, object>() {
            {"0", "#666333"},
            {"498591726", "#666666"}
        };

        private Dictionary<string, object> gradeColors = new Dictionary<string, object>() {
            {"Wood", "#a68b44"},
            {"Stone", "#a4a4a4"},
            {"Metal", "#9b5050"},
            {"TopTier", "#473131"}
        };

        private float announceDelay, announceRadius = 0f;

        internal int announceMinParticipants, announceMinWeapons, announceMinKills, announceMinDestroyed, announceMinMinutes = 0;

        List<string> prefabs = new List<string>()
        {
            "door.hinged",
            "door.double.hinged",
            "window.bars",
            "floor.ladder.hatch",
            "floor.frame",
            "wall.frame",
            "shutter"
        };

        Dictionary<long, Raid> raids = new Dictionary<long, Raid>();
        #endregion

        #region Classes
        public class AttackVector {
            public Vector3 start;
            public Vector3 end;
            public int weapon;
            public ulong victim;
            public ulong initiator;

            [JsonConstructor]
            public AttackVector(Vector3 start, Vector3 end, int weapon, ulong victim = 0, ulong initiator = 0) {
                this.start = start;
                this.end = end;
                this.weapon = weapon;
                this.victim = victim;
                this.initiator = initiator;
            }
        }
        
        public class Raid
        {
            public long start = DateTime.Now.ToBinary();
            public long end = 0;
            public Vector3 firstDamage;
            public Vector3 lastDamage;
            public List<AttackVector> attackVector = new List<AttackVector>();
            public List<AttackVector> killMap = new List<AttackVector>();
            public ulong initiator;
            public ulong victim;
            public List<ulong> blockOwners = new List<ulong>();
            public List<ulong> participants = new List<ulong>();
            public int lastWeapon;
            public Dictionary<int, int> weapons = new Dictionary<int, int>();

            public Dictionary<BuildingGrade.Enum, int> blocksDestroyed = new Dictionary<BuildingGrade.Enum,int>();
            public Dictionary<string, int> entitiesDestroyed = new Dictionary<string,int>();

            [JsonConstructor]
            public Raid(
                long start, 
                long end, 
                Vector3 firstDamage, 
                Vector3 lastDamage, 
                List<AttackVector> attackVector, 
                ulong initiator, 
                ulong victim, 
                List<ulong> blockOwners, 
                List<ulong> participants,
                int lastWeapon, 
                Dictionary<int, int> weapons,
                List<AttackVector> killMap = null,
                Dictionary<BuildingGrade.Enum, int> blocksDestroyed = null,
                Dictionary<string, int> entitiesDestroyed = null
            ) {
                this.start = start;
                this.end = end;
                this.firstDamage = firstDamage;
                this.lastDamage = lastDamage;
                this.attackVector = attackVector;
                this.initiator = initiator;
                this.victim = victim;
                this.blockOwners = blockOwners;
                this.participants = participants;
                this.lastWeapon = lastWeapon;
                this.weapons = weapons;
                if(killMap != null) {
                    this.killMap = killMap;
                }

                if(blocksDestroyed != null) {
                    this.blocksDestroyed = blocksDestroyed;
                }
                
                if(entitiesDestroyed != null) {
                    this.entitiesDestroyed = entitiesDestroyed;
                }
            }

            [JsonIgnore]
            internal AttackVector lastAttackVector = null;

            [JsonIgnore]
            RaidNotes plugin;

            [JsonIgnore]
            public bool Completed
            {
                get
                {
                    return end != 0;
                }
            }

            [JsonIgnore]
            public DateTime lastRefresh;

            [JsonIgnore]
            public IPlayer Initiator
            {
                get
                {
                    return plugin.covalence.Players.FindPlayerById(initiator.ToString());
                }
            }

            [JsonIgnore]
            public IPlayer Victim
            {
                get
                {
                    return plugin.covalence.Players.FindPlayerById(victim.ToString());
                }
            }

            [JsonIgnore]
            [JsonConverter(typeof(IsoDateTimeConverter))]
            public DateTime Start {
                get {
                    return DateTime.FromBinary(start);
                }
            }

            [JsonIgnore]
            [JsonConverter(typeof(IsoDateTimeConverter))]
            public DateTime End {
                get {
                    return DateTime.FromBinary(end);
                }
            }

            public Raid(RaidNotes plugin, ulong initiator, ulong victim, Vector3 firstDamage)
            {
                this.plugin = plugin;
                this.initiator = initiator;
                this.victim = victim;
                this.firstDamage = firstDamage;
            }

            [JsonIgnore]
            public double Hours
            {
                get
                {
                    if(!Completed) {
                        return 0;
                    }
                    var ts = DateTime.Now - End;
                    return ts.TotalHours;
                }
            }

            public bool HasHours(int hours)
            {
                if (Hours >= hours)
                {
                    return true;
                }

                return false;
            }

            public void Participate(BasePlayer player) {
                var behavior = player.gameObject.AddComponent<RaidBehavior>();
                behavior.raid = this;
                if (!participants.Contains(player.userID))
                {
                    participants.Add(player.userID);
                }
            }

            public bool IsAnnounced()
            {
                if (participants.Count < plugin.announceMinParticipants) return false;
                if (killMap.Count < plugin.announceMinKills) return false;
                if (weapons.Count < plugin.announceMinWeapons) return false;
                if ((entitiesDestroyed.Count + blocksDestroyed.Count) < plugin.announceMinDestroyed) return false;

                var ts = End - Start;
                if (ts.TotalMinutes < plugin.announceMinMinutes) return false;

                return true;
            }

            internal JObject Vector2JObject(Vector3 vector)
            {
                var obj = new JObject();
                obj.Add("x", vector.x);
                obj.Add("y", vector.y);
                obj.Add("z", vector.z);

                return obj;
            }

            public override string ToString()
            {
 	             return ToJObject().ToString();
            }

            internal JObject ToJObject()
            {
                var obj = new JObject();

                obj["start"] = Start.ToString();
                obj["end"] = End.ToString();
                var explosions = new JObject();
                explosions.Add("first", Vector2JObject(firstDamage));
                explosions.Add("last", Vector2JObject(lastDamage));
                obj["explosions"] = explosions;

                obj["initiator"] = initiator;
                obj["victim"] = victim;

                JArray owners = new JArray();
                foreach (var owner in blockOwners)
                    owners.Add(owner);

                obj["owners"] = owners;

                JArray participantsData = new JArray();
                foreach (var participant in participants)
                    participantsData.Add(participant);

                obj["participants"] = participantsData;


                JObject weaponsData = new JObject();
                foreach (var kvp in weapons)
                    weaponsData.Add(kvp.Key.ToString(), kvp.Value);
                
                obj["weapons"] = weaponsData;

                obj["attackvector"] = JArray.FromObject(attackVector, SERIALIZER);
                obj["kills"] = JArray.FromObject(killMap, SERIALIZER);

                return obj;
            }

            internal void OnEnded()
            {
                end = DateTime.Now.ToBinary();
                Interface.CallHook("OnRaidEnded", ToJObject());
            }

            internal void OnStarted()
            {
                Interface.CallHook("OnRaidStarted", ToJObject());
            }

            internal void Attack(AttackVector vector) {
                lastAttackVector = vector;
                attackVector.Add(vector);
            }

            internal void Kill(AttackVector vector) {
                killMap.Add(vector);
            }
        }

        public class RaidBehavior : MonoBehaviour
        {
            public BasePlayer player;
            internal Raid raid;
            

            void Awake()
            {
                player = GetComponent<BasePlayer>();
            }

            void OnDestroy()
            {
                GameObject.Destroy(this);
            }
        }

        public class ExplosiveTracker : MonoBehaviour {
            public BaseEntity entity;
            public Vector3 lastValidPosition;

            public BasePlayer thrownBy;
            public Vector3 thrownFrom;

            void Awake() {
                entity = GetComponent<BaseEntity>();
                lastValidPosition = entity.transform.position;
            }

            void Update() {
                if(Vector3.Distance(entity.transform.position, Vector3.zero) > 3) {
                    lastValidPosition = entity.transform.position;
                }
            }

            void OnDestroy()
            {
                if(thrownBy == null) return;
                var behavior = thrownBy.GetComponent<RaidBehavior>();

                if(behavior != null && behavior.raid != null) {
                    int itemid;
                    if(reverseItems.TryGetValue(entity.PrefabName, out itemid)) 
                        behavior.raid.Attack(new AttackVector(thrownFrom, lastValidPosition, itemid, 0, thrownBy.userID));
                    
                }
                GameObject.Destroy(this);
            }
        }

        enum AnnouncementType {
            Start = 1,
            End = 2,
            Slack_Start = 3,
            Slack_End = 4,
            Discord_Start = 5,
            Discord_End = 6
        }

        #endregion

        #region Initialization

        protected override void LoadDefaultConfig()
        {
            PrintToConsole("Creating new configuration");
            Config.Clear();

            Config["Settings", "trackEntityDamage"] = true;
            Config["Settings", "trackEntityDeath"] = true;
            Config["Settings","trackExplosives"] = true;

            Config["Raid", "distance"] = 50f;
            Config["Raid", "duration"] = 300f;
            Config["Raid", "logUpToHours"] = 3;
            Config["Raid", "detectionDistance"] = 50;
            Config["Raid", "detectionDuration"] = 60f;
            Config["Raid", "detectionCountdownMinutes"] = 1f;

            Config["AnnounceWhen", "raidEnds"] = false;
            Config["AnnounceWhen", "raidStarts"] = false;
            Config["AnnounceWhen", "minParticipants"] = 0;
            Config["AnnounceWhen", "minWeapons"] = 0;
            Config["AnnounceWhen", "minKills"] = 0;
            Config["AnnounceWhen", "minMinutes"] = 0;

            Config["AnnounceTo", "global"] = false;
            Config["AnnounceTo", "clan"] = true;
            Config["AnnounceTo", "victims"] = true;
            Config["AnnounceTo", "slack"] = false;
            Config["AnnounceTo", "discord"] = false;
            Config["AnnounceTo", "lustymap"] = false;
            Config["AnnounceTo", "log"] = true;

            Config["Announcements","icon"] = 0;
            Config["Announcements","prefixColor"] = "orange";
            Config["Announcements","nameColor"] = "lightblue";
            Config["Announcements","clanColor"] = "#00eaff";
            Config["Announcements","weaponColors"] = weaponColors;
            Config["Announcements","delay"] = 0f;
            Config["Announcements","radius"] = 0f;

            Config["LustyMap","icon"] = "special";
            Config["LustyMap","duration"] = 10f;

            Config["Slack","messageType"] = "FancyMessage";
        }

        void LoadMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                {"Announce: Prefix", "Raid"},
                {"Announce: Start", "{initiatorClan} {initiator} ({initiatorClanMates}) is raiding {victimClan} {victim} ({victimClanMates})"},
                {"Announce: End", "{initiatorClan} {initiator} ({initiatorClanMates}) raided {victimClan} {victim} ({victimClanMates}) using {weaponList} destroying {destroyedList}"},
                {"Announce: Slack Start", "{initiatorClan} {initiator} ({initiatorClanMates}) is raiding {victimClan} {victim} ({victimClanMates})"},
                {"Announce: Slack End", "{initiatorClan} {initiator} ({initiatorClanMates}) raided {victimClan} {victim} ({victimClanMates}) with {weaponList} destroying {destroyedList}"},
                {"Announce: Discord Start", "{initiatorClan} {initiator} ({initiatorClanMates}) is raiding {victimClan} {victim} ({victimClanMates})"},
                {"Announce: Discord End", "{initiatorClan} {initiator} ({initiatorClanMates}) raided {victimClan} {victim} ({victimClanMates}) with {weaponList} destroying {destroyedList}"},
                {"Denied: Permission", "You lack permission to do that"},
                {"Raid: Found", "<size=15>Raid(s) found: {raidCount}</size>"},
                {"Raid: Started", "Started: <b>{date}</b>"},
                {"Raid: Ended", "Ended: <b>{date}</b>"},
                {"Raid: Duration", "Duration: <b>{duration}</b> minutes"},
                {"Raid: Initiator", "Initiator: <b>{initiatorName}</b> ({initiatorID})"},
                {"Raid: PlayerList", "{listName}: <b>{list}</b>"},
                {"Raid: Activity", "Raid"},
                {"Target: Nothing", "Nothing"},
                {"Cooldown: Seconds", "You are doing that too often, try again in a {0} seconds(s)."},
                {"Cooldown: Minutes", "You are doing that too often, try again in a {0} minute(s)."},
            }, this);
        }

        void OnServerInitialized()
        {
            LoadData();
            LoadMessages();

            foreach(JsonConverter converter in CONVERTERS) {
                SERIALIZER.Converters.Add(converter);
            }

            permission.RegisterPermission("raidnotes.inspect", this);

            raidDistance = GetConfig("Raid","distance", 50f);
            raidDuration = GetConfig("Raid","duration", 300f);
            logHours = GetConfig("Raid", "logUpToHours", 3);
            detectionDistance = GetConfig("Raid", "detectionDistance", 50);
            detectionDuration = GetConfig("Raid", "detectionDuration", 60f);
            detectionDuration = GetConfig("Raid", "detectionCountdownMinutes", 1f);

            checkEntityDamage = GetConfig("Settings","hookEntityDamage", true);
            checkEntityDeath = GetConfig("Settings","hookEntityDeath", true);
            trackExplosives = GetConfig("Settings","trackExplosives", true);

            announceGlobal = GetConfig("AnnounceTo","global", false);
            announceClan = GetConfig("AnnounceTo","clan", true);
            announceToVictims = GetConfig("AnnounceTo","victims", true);
            announceToSlack = GetConfig("AnnounceTo","slack", false);
            announceToLustyMap = GetConfig("AnnounceTo","lustymap", false);
            announceToDiscord = GetConfig("AnnounceTo","discord", false);
            printToLog = GetConfig("AnnounceTo","log", true);

            announceRaidEnd = GetConfig("AnnounceWhen","raidEnds", false);
            announceRaidStart = GetConfig("AnnounceWhen","raidStarts", false);
            announceMinParticipants = GetConfig("AnnounceWhen","minParticipants", 0);
            announceMinWeapons = GetConfig("AnnounceWhen","minWeapons", 0);
            announceMinDestroyed = GetConfig("AnnounceWhen","minDestroyed", 0);
            announceMinKills = GetConfig("AnnounceWhen","minKills", 0);
            announceMinMinutes = GetConfig("AnnounceWhen","minMinutes", 0);

            announceIcon = GetConfig("Announcements","icon", "0");
            announcePrefixColor = GetConfig("Announcements","prefixColor", "orange");
            announceNameColor = GetConfig("Announcements","nameColor", "lightblue");
            announceClanColor = GetConfig("Announcements","clanColor", "#00eaff");
            weaponColors = GetConfig("Announcements","weaponColors", weaponColors);
            announceDelay = GetConfig("Announcements","delay", 0f);
            announceRadius = GetConfig("Announcements","radius", 0f);

            lustyMapIcon = GetConfig("LustyMap","icon", "special");
            lustyMapDuration = GetConfig("LustyMap","duration", 10f);

            slackType = GetConfig("Slack","messageType", "FancyMessage");

            if (announceToSlack && !Slack)
            {
                PrintWarning("Slack plugin not found, please install http://oxidemod.org/plugins/slack.1952/");
                announceToSlack = false;
            }

            if (announceToDiscord && !Discord)
            {
                PrintWarning("Discord plugin not found, please install http://oxidemod.org/plugins/discord.2149/");
                announceToDiscord = false;
            }

            if(announceToLustyMap && !LustyMap) {
                PrintWarning("LustyMap plugin not found, please install http://oxidemod.org/plugins/lustymap.1333/");
            }

            if (logHours == 0)
            {
                Unsubscribe(nameof(OnServerSave));
            }

            if(!trackExplosives) {
                Unsubscribe(nameof(OnExplosiveThrown));
                Unsubscribe(nameof(OnRocketLaunched));
            }

            foreach (ItemDefinition def in ItemManager.GetItemDefinitions())
            {
                var modEntity = def.GetComponent<ItemModEntity>();
                if (modEntity != null && modEntity.entityPrefab != null)
                {
                    var prefab = modEntity.entityPrefab.Get();
                    var thrownWeapon = prefab.GetComponent<ThrownWeapon>();

                    if (thrownWeapon != null && !string.IsNullOrEmpty(thrownWeapon.prefabToThrow.guid) && !reverseItems.ContainsKey(thrownWeapon.prefabToThrow.resourcePath))
                    {
                        reverseItems.Add(thrownWeapon.prefabToThrow.resourcePath, def.itemid);
                        continue;
                    }
                }

                var baseProjectile = def.GetComponent<ItemModProjectile>();
                if (baseProjectile != null && !string.IsNullOrEmpty(baseProjectile.projectileObject.guid) && !reverseItems.ContainsKey(baseProjectile.projectileObject.resourcePath))
                {
                    if (baseProjectile.projectileObject.resourcePath.Contains("rocket") && !baseProjectile.projectileObject.resourcePath.Contains("smoke"))
                    {
                        explosionRadiusPrefabs.Add(baseProjectile.projectileObject.resourcePath);
                        reverseItems.Add(baseProjectile.projectileObject.resourcePath, def.itemid);
                    }
                }
            }

            if (announceClan)
            {
                if (!plugins.Exists("Clans"))
                {
                    announceClan = false;
                    PrintWarning("Clans plugin not found, please install http://oxidemod.org/plugins/clans.2087/");
                }
            }
        }

        protected void ReloadConfig()
        {
            Config["VERSION"] = Version.ToString();
            ClearData();
            PrintToConsole("Upgrading configuration file");
            SaveConfig();
        }

        void LoadData()
        {
            if (logHours > 0)
            {
                data = Interface.Oxide.DataFileSystem.GetFile(nameof(RaidNotes));
                data.Settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                data.Settings.Converters = CONVERTERS;

                raids = data.ReadObject<Dictionary<long, Raid>>();

                if(isNewSave) {
                    ClearData();
                    isNewSave = false;
                }
            }

            if (Config["VERSION"] == null)
            {
                // FOR COMPATIBILITY WITH INITIAL VERSIONS WITHOUT VERSIONED CONFIG
                ReloadConfig();
            }
            else if (GetConfig<string>("VERSION", "") != Version.ToString())
            {
                // ADDS NEW, IF ANY, CONFIGURATION OPTIONS
                ReloadConfig();
            }
        }

        void SaveData(bool force = false)
        {
            if(raids.Count > 0 && !force) {
                var toRemove = raids.Where(pair => pair.Value.Hours > logHours).Select(pair => pair.Key).ToList();

                foreach (var key in toRemove)
                    raids.Remove(key);
            }

            data.WriteObject<Dictionary<long, Raid>>(raids);
        }

        void ClearData()
        {
            raids.Clear();
            SaveData();
        }

        void OnNewSave(string filename)
        {
            isNewSave = true;
        }

        void OnServerSave()
        {
            SaveData();
        }

        void Unload()
        {
            var objects = GameObject.FindObjectsOfType(typeof(RaidBehavior));
            if (objects != null)
                foreach (var gameObj in objects)
                    GameObject.Destroy(gameObj);

            objects = GameObject.FindObjectsOfType(typeof(ExplosiveTracker));
            if (objects != null)
                foreach (var gameObj in objects)
                    GameObject.Destroy(gameObj);

            if(logHours > 0) {
                SaveData(true);
            }
        }

        #endregion

        #region Oxide Hooks

        void OnExplosiveThrown(BasePlayer player, BaseEntity entity)
        {
            if (player == null || entity.net == null) return;
            AddTracker(player, entity);
        }

        void OnRocketLaunched(BasePlayer player, BaseEntity entity)
        {
            if(player == null || entity.net == null || !reverseItems.ContainsKey(entity.PrefabName)) return;

            AddTracker(player, entity);
        }

        List<uint> recentAttacks = new List<uint>();

        private void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo)
        {
            if (!checkEntityDamage) return;
            if (hitInfo == null ||
                hitInfo.Initiator == null ||
                hitInfo.WeaponPrefab == null ||
                !IsEntityRaidable(entity))
                return;

            var prefabName = hitInfo.WeaponPrefab.PrefabName;

            if(explosionRadiusPrefabs.Contains(prefabName)) {
                if(recentAttacks.Contains(hitInfo.Initiator.net.ID)) {
                    return;
                } else {
                    recentAttacks.Add(hitInfo.Initiator.net.ID);
                }

                Interface.Oxide.NextTick(delegate() {
                    if(recentAttacks.Contains(hitInfo.Initiator.net.ID)) {
                        recentAttacks.Remove(hitInfo.Initiator.net.ID);
                    }
                });
            }
            
            int itemUsed;
            if (!reverseItems.TryGetValue(prefabName, out itemUsed))
                return;

            var majorityDamageType = hitInfo.damageTypes.GetMajorityDamageType();

            switch (majorityDamageType)
            {
                case DamageType.Explosion:
                case DamageType.Heat:
                    StructureAttack(entity, hitInfo.Initiator, itemUsed);
                    break;
            }
        }

        private void OnEntityDeath(BaseCombatEntity entity, HitInfo hitInfo)
        {
            if (!checkEntityDeath || hitInfo == null || hitInfo.WeaponPrefab == null || hitInfo.Initiator == null || !(hitInfo.Initiator is BasePlayer)) return;
            if (IsEntityRaidable(entity))
            {
                var majorityDamageType = hitInfo.damageTypes.GetMajorityDamageType();

                var prefabName = hitInfo.WeaponPrefab.PrefabName;
                int itemUsed;
                if (reverseItems.TryGetValue(prefabName, out itemUsed))
                {
                    switch (majorityDamageType)
                    {
                        case DamageType.Explosion:
                        case DamageType.Heat:
                            StructureAttack(entity, hitInfo.Initiator as BasePlayer, itemUsed, true);
                            break;
                    }
                }
            }
            else if (entity is BasePlayer)
                RegisterKill(entity as BasePlayer, hitInfo.Initiator as BasePlayer);
            
        }

        #endregion

        #region Commands

        [ConsoleCommand("raids.wipe")]
        private void ccRaidsWipe(ConsoleSystem.Arg arg)
        {
            if (arg.Connection == null || (arg.Connection != null && arg.Connection.authLevel > 0)) {
                ClearData();
                
                SendReply(arg, "Data wiped");
                return;
            }

            SendReply(arg, GetMsg("Denied: Permission"));
        }

        [ChatCommand("inspect")]
        private void cmdInspect(BasePlayer player, string command, string[] args)
        {
            var permission = HasPerm(player, "raidnotes.inspect");
            if (permission || (!permission && player.net.connection.authLevel > 0))
            {
                if (!CheckCooldown(player))
                    return;
                else 
                    PlayerCooldown(player);
                
                SendRaids(player, args);
                return;
            }

            SendReply(player, GetMsg("Denied: Permission", player));
        }

        #endregion

        #region Core Methods

        void PlayerCooldown(BasePlayer player)
        {
            if (player.IsAdmin)
                return;

            if (detectionCooldowns.ContainsKey(player.UserIDString))
                detectionCooldowns.Remove(player.UserIDString);

            detectionCooldowns.Add(player.UserIDString, DateTime.Now);
        }

        bool CheckCooldown(BasePlayer player)
        {
            if (detectionCountdown > 0)
            {
                DateTime startTime;
                if (detectionCooldowns.TryGetValue(player.UserIDString, out startTime))
                {
                    var endTime = DateTime.Now;

                    var span = endTime.Subtract(startTime);
                    if (span.TotalMinutes > 0 && span.TotalMinutes < Convert.ToDouble(detectionCountdown))
                    {
                        var timeleft = System.Math.Round(Convert.ToDouble(detectionCountdown) - span.TotalMinutes, 2);
                        if (timeleft < 1)
                        {
                            var timelefts = System.Math.Round((Convert.ToDouble(detectionCountdown) * 60) - span.TotalSeconds);
                            SendReply(player, string.Format(GetMsg("Cooldown: Seconds", player), timelefts.ToString()));
                        }
                        else
                            SendReply(player, string.Format(GetMsg("Cooldown: Minutes", player), System.Math.Round(timeleft).ToString()));
                        
                        return false;
                    }
                    else
                        detectionCooldowns.Remove(player.UserIDString);
                    
                }
            }

            return true;
        }

        void AddTracker(BasePlayer player, BaseEntity entity)
        {
            var tracker = entity.gameObject.AddComponent<ExplosiveTracker>();
            tracker.thrownBy = player;
            tracker.thrownFrom = player.GetEstimatedWorldPosition();
            tracker.thrownFrom.y += player.GetHeight() * shoulderHeight;
        }

        Vector3 Track(BaseEntity initiator, BaseEntity entity) {
            var fromPos = Vector3.zero;
            var tracker = trackExplosives ? initiator.gameObject.GetComponent<ExplosiveTracker>() : null;
            if(tracker != null)
                fromPos = tracker.thrownFrom;
            else
                fromPos = initiator.GetEstimatedWorldPosition();

            return fromPos;
        }

        List<Raid> GetRaids(BasePlayer player, string[] args) {
            var defaultDistance = detectionDistance;
            if(args != null && args.Length == 1)
                int.TryParse(args[0], out defaultDistance);

            if(defaultDistance > detectionDistance)
                defaultDistance = detectionDistance;

            return raids.Where(pair => Vector3.Distance(pair.Value.firstDamage, player.transform.position) <= defaultDistance).Select(pair => pair.Value).ToList();
        }

        void SendRaids(BasePlayer player, string[] args)
        {
            if(player.net.connection == null)
                return;
            
            var nearbyRaids = GetRaids(player, args);
            
            if(nearbyRaids.Count > 0) {
                int found = 0;
                var sbs = new List<StringBuilder>();
                foreach (var raid in nearbyRaids) {
                    var sb = new StringBuilder();
                    if(SendRaid(player, raid, sb, found)) {
                        found++;
                        sbs.Add(sb);
                    }
                }

                if(found > 0) {
                    SendReply(player, Format(GetMsg("Raid: Found", player), raidCount => found));
                    foreach(var sb in sbs) {
                        SendReply(player, sb.ToString());
                    }
                    return;
                } 
            }

            SendReply(player, "No raids found");
        }

        public string ToHex(Color c)
        {
            return string.Format("#{0:X2}{1:X2}{2:X2}", ToByte(c.r), ToByte(c.g), ToByte(c.b));
        }

        private byte ToByte(float f)
        {
            f = Mathf.Clamp01(f);
            return (byte)(f * 255);
        }

        bool SendRaid(BasePlayer player, Raid raid, StringBuilder sb, int found = 0) {
            var uiduration = detectionDuration * 60;
            var admin = false;
            if(player.net.connection.authLevel > 0)
                admin = true;

            var randomColor = colors[UnityEngine.Random.Range(0, colors.Length - 1)];
            var validAttack = false;
            foreach (AttackVector attack in raid.attackVector)
            {
                if(!admin && attack.victim != player.userID)
                    continue;
                
                validAttack = true;
                var weapName = string.Empty;
                var def = ItemManager.FindItemDefinition(attack.weapon);
                if(def is ItemDefinition)
                    weapName = def.displayName.english;
                
                player.SendConsoleCommand("ddraw.arrow", uiduration, randomColor, attack.start, attack.end, 0.2);

                if(!string.IsNullOrEmpty(weapName))
                    player.SendConsoleCommand("ddraw.text", uiduration, GetWeaponColor(attack.weapon), attack.start, weapName);
            }

            foreach(AttackVector kill in raid.killMap) {
                if(!admin && kill.victim != player.userID)
                    continue;

                validAttack = true;

                player.SendConsoleCommand("ddraw.arrow", uiduration, randomColor, kill.start, kill.end, 0.2);
                player.SendConsoleCommand("ddraw.sphere", uiduration, Color.red, kill.end, 0.5f);

                var victimPlayer = covalence.Players.FindPlayerById(kill.victim.ToString());
                var initiatorPlayer = covalence.Players.FindPlayerById(kill.initiator.ToString());

                if(victimPlayer is IPlayer) {
                    player.SendConsoleCommand("ddraw.text", uiduration, Color.red, kill.end + Vector3.up, victimPlayer.Name);
                }

                if(initiatorPlayer is IPlayer) {
                    player.SendConsoleCommand("ddraw.text", uiduration, Color.red, kill.start + Vector3.up, initiatorPlayer.Name);
                }
            }

            if(validAttack) {
                player.SendConsoleCommand("ddraw.arrow", uiduration, Color.green, raid.firstDamage + new Vector3(0,5,0), raid.firstDamage, 0.2);
                if(raid.lastDamage != Vector3.zero) {
                    player.SendConsoleCommand("ddraw.arrow", uiduration, Color.red, raid.lastDamage + new Vector3(0,5,0), raid.lastDamage, 0.2);
                }
                sb.Append(Format("<color={color}><size=17>#{count}</size></color> + ", count => (found+1), color => ToHex(randomColor)));
                var start = (raid.Start != null) ? raid.Start.ToString() : "N/A";
                sb.AppendLine(Format(GetMsg("Raid: Started"), date => start));
                if(raid.Completed) {
                    TimeSpan ts = raid.End - raid.Start;
                    var end = (raid.End != null) ? raid.End.ToString() : "N/A";
                    sb.Append("   |- ").AppendLine(Format(GetMsg("Raid: Duration"), duration => Math.Round(ts.TotalMinutes,2)));
                    sb.Append("   |- ").AppendLine(Format(GetMsg("Raid: Ended"), date => end));
                }

                var initiator = covalence.Players.FindPlayerById(raid.initiator.ToString());
                if(initiator != null) {
                    sb.Append("   |- ").AppendLine(Format(GetMsg("Raid: Initiator"), initiatorName => initiator.Name, initiatorID => initiator.Id));
                }

                if(raid.blockOwners.Count > 0) {
                    var victimList = string.Join(", ", raid.blockOwners.Select(x => covalence.Players.FindPlayerById(x.ToString()).Name).ToArray());
                    sb.Append("   |- ").AppendLine(Format(GetMsg("Raid: PlayerList"), listName => "Property Of", list => victimList));
                }

                if(raid.participants.Count > 0) {
                    var participantList = string.Join(", ", raid.blockOwners.Where(x => !raid.blockOwners.Contains(x)).Select(x => covalence.Players.FindPlayerById(x.ToString()).Name).ToArray());
                    if(!string.IsNullOrEmpty(participantList.Trim())) {
                        sb.Append("   |- ").AppendLine(Format(GetMsg("Raid: PlayerList"), listName => "Perpetrators", list => participantList));
                    }
                }

                if(raid.weapons.Count > 0) 
                    sb.Append("   |- ").AppendLine(GetWeaponList(raid));
                

                if(raid.blocksDestroyed.Count > 0 || raid.entitiesDestroyed.Count > 0) 
                    sb.Append("   |- ").AppendLine(GetDestroyedList(raid));
                

                return true;
            }

            return false;
        }

        void RegisterKill(BasePlayer player, BasePlayer attacker)
        {
            var behavior = player.GetComponent<RaidBehavior>();
            if (behavior != null && behavior.raid != null)
            {
                var activeItem = attacker.GetActiveItem();
                var itemid = 0;
                if(activeItem != null) 
                    itemid = activeItem.info.itemid;
                
                behavior.raid.Kill(new AttackVector(attacker.transform.position, player.transform.position, itemid, player.userID, attacker.userID));

                GameObject.Destroy(behavior);
            }
        }

        void StructureAttack(BaseEntity targetEntity, BaseEntity sourceEntity,int weapon, bool destroy = false)
        {
            BasePlayer source;
            
            if (sourceEntity.ToPlayer() is BasePlayer)
                source = sourceEntity.ToPlayer();
            else
            {
                var ownerID = (sourceEntity.OwnerID == 0) ? sourceEntity.OwnerID.ToString() : string.Empty;
                if (!string.IsNullOrEmpty(ownerID))
                    source = BasePlayer.Find(ownerID);
                else
                    return;
            }

            if (source == null)
                return;

            var targetID = targetEntity.OwnerID.IsSteamId() ? targetEntity.OwnerID.ToString() : string.Empty;
            
            if (!string.IsNullOrEmpty(targetID) && targetID != source.UserIDString)
            {
                var targetIDUint = Convert.ToUInt64(targetID);
                var target = covalence.Players.FindPlayerById(targetID);
                Raid raid;
                var raidFound = TryGetRaid(source, targetIDUint, targetEntity.transform.position, out raid);
                raid.lastWeapon = weapon;
                

                if (raid.blockOwners.Count == 0)
                    raid.victim = targetIDUint;

                if (!raid.blockOwners.Contains(targetIDUint))
                    raid.blockOwners.Add(targetIDUint);

                if(destroy) {
                    if(targetEntity is BuildingBlock) {
                        var grade = ((BuildingBlock)targetEntity).grade;
                        if(raid.blocksDestroyed.ContainsKey(grade))
                            raid.blocksDestroyed[grade]++;
                         else 
                            raid.blocksDestroyed.Add(grade, 1);
                    } else if(targetEntity is BaseCombatEntity) {
                        var name = targetEntity.ShortPrefabName;
                        if(raid.entitiesDestroyed.ContainsKey(name)) 
                            raid.entitiesDestroyed[name]++;
                        else 
                            raid.entitiesDestroyed.Add(name, 1);
                    }
                } else {
                    if (raid.weapons.ContainsKey(weapon))
                        raid.weapons[weapon]++;
                    else
                        raid.weapons.Add(weapon, 1);
                }

                if(raid.lastAttackVector != null) 
                    raid.lastAttackVector.victim = targetIDUint;

                raid.lastDamage = targetEntity.transform.position;

                if (!raidFound && announceRaidStart)
                {
                    AnnounceRaidMsg(raid, AnnouncementType.Start);
                    if(announceToSlack) 
                        AnnounceRaidMsg(raid, AnnouncementType.Slack_Start);
                    
                    if(announceToDiscord)
                        AnnounceRaidMsg(raid, AnnouncementType.Discord_Start);
                }
            }
        }
        
        Raid FindRaid(Vector3 position, out List<BasePlayer> nearbyTargets) {
            Raid existingRaid = null;

            nearbyTargets = GetNearbyPlayers(position);

            if (existingRaid == null && nearbyTargets.Count > 0)
            {
                foreach (var nearbyTarget in nearbyTargets)
                {
                    var behavior = nearbyTarget.GetComponent<RaidBehavior>();
                    if (behavior != null && behavior.raid != null && existingRaid != behavior.raid && !behavior.raid.Completed)
                    {
                        existingRaid = behavior.raid;
                        break;
                    }
                }
            }

            return existingRaid;
        }

        List<BasePlayer> GetNearbyPlayers(Vector3 position) {
            var nearbyTargets = Pool.GetList<BasePlayer>();
            Vis.Entities<BasePlayer>(position, raidDistance, nearbyTargets, blockLayer);
            nearbyTargets = Sort(position, nearbyTargets);

            return nearbyTargets;
        }

        bool TryGetRaid(BasePlayer source, ulong victim, Vector3 position, out Raid raid)
        {
            Raid existingRaid = null;
            List<BasePlayer> nearbyTargets = null;
            var sourceBehavior = source.GetComponent<RaidBehavior>();

            if (sourceBehavior != null && sourceBehavior.raid != null && !sourceBehavior.raid.Completed)
                existingRaid = sourceBehavior.raid;
            else
                existingRaid = FindRaid(position, out nearbyTargets);
            
            bool found = true;

            if (existingRaid == null || (existingRaid != null && existingRaid.Completed)) {
                found = false;
                var newRaid = StartRaid(source, victim, position);
                existingRaid = newRaid;

                if(nearbyTargets == null)
                    nearbyTargets = GetNearbyPlayers(position);

                foreach (var nearbyTarget in nearbyTargets) {
                    var behavior = nearbyTarget.GetComponent<RaidBehavior>();
                    if (behavior == null || (behavior != null && behavior.raid == null)) {
                        existingRaid.Participate(nearbyTarget);
                    }
                }
            } else if (sourceBehavior == null || (sourceBehavior != null && sourceBehavior.raid == null))
                existingRaid.Participate(source);
            

            if(nearbyTargets != null)
                Pool.FreeList<BasePlayer>(ref nearbyTargets);

            RefreshRaid(existingRaid);
            raid = existingRaid;
            return found;
        }

        public Raid StartRaid(BasePlayer source, ulong victim, Vector3 position)
        {
            var raid = new Raid(this, source.userID, victim, position);

            RefreshRaid(raid);

            raid.Participate(source);
            raid.OnStarted();

            return raid;
        }

        private string GetAnnouncementMsg(AnnouncementType type) {
            var msgName = string.Empty;
            switch(type) {
                case AnnouncementType.Start:
                    msgName = "Announce: Start";
                    break;
                case AnnouncementType.End:
                    msgName = "Announce: End";
                    break;
                case AnnouncementType.Slack_Start:
                    msgName = "Announce: Slack Start";
                    break;
                case AnnouncementType.Slack_End:
                    msgName = "Announce: Slack End";
                    break;
                case AnnouncementType.Discord_Start:
                    msgName = "Announce: Discord Start";
                    break;
                case AnnouncementType.Discord_End:
                    msgName = "Announce: Discord End";
                    break;
            }

            if(!string.IsNullOrEmpty(msgName))
                return GetMsg(msgName);
            
            return msgName;
        }

        public void CheckRaid(Raid raid)
        {
            var ts = DateTime.Now - raid.lastRefresh;
            if (ts.TotalSeconds > raidDuration)
            {
                if(announceToLustyMap && lustyMapDuration > 0) 
                    LustyMap?.Call("RemoveMarker", raid.start.ToString());
                
                StopRaid(raid);
                if (announceRaidEnd)
                {
                    AnnounceRaidMsg(raid, AnnouncementType.End);
                    if(announceToSlack) 
                        AnnounceRaidMsg(raid, AnnouncementType.Slack_End);
                    
                    if(announceToDiscord) 
                        AnnounceRaidMsg(raid, AnnouncementType.Discord_End);
                    
                }
            }
        }
        
        void AnnounceRaidMsg(Raid raid, AnnouncementType type) {
            if (announceDelay > 0)
                timer.In(announceDelay, delegate()
                {
                    AnnounceRaid(raid, type);
                });
            else
                AnnounceRaid(raid, type);
        }

        public void RefreshRaid(Raid raid)
        {
            
            raid.lastRefresh = DateTime.Now;
            Timer t;

            if (timers.TryGetValue(raid, out t)) {
                if(t.Destroyed) {
                    timers.Add(raid, t = timer.Repeat(raidDuration, 0, () => CheckRaid(raid)));
                }
            }
            else {
                timers.Add(raid, t = timer.Repeat(raidDuration, 0, () => CheckRaid(raid)));
       
// ... [Code preview truncated due to length. Full source in attached file] ...

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