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

FREE KillFeed : 1.3.0 1.3.0

Нет прав для скачивания
C#:
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Game.Rust.Cui;
using UnityEngine;
//  ____  _       _ _       
// / ___|(_) __ _(_) | ___  
// \___ \| |/ _` | | |/ _ \ 
//  ___) | | (_| | | | (_) |
// |____/|_|\__, |_|_|\___/ 
//          |___/             
// ☽✧✵✧✵✧✵✧✵✧✵✧✵✧☾
// ✦    RUST PLUGINS   ✦
// ✦     https://sigilo.dev    ✦
// ☽✧✵✧✵✧✵✧✵✧✵✧✵✧☾
namespace Oxide.Plugins
{
    [Info("KillFeed", "Sigilo", "1.3.0")]
    [Description("Displays kill notifications at the top right of the screen")]
    class KillFeed : RustPlugin
    {
        private Configuration _config;
        private HashSet<ulong> _disabledPlayers = new HashSet<ulong>();
        private List<KillEntry> _killEntries = new List<KillEntry>();
        private const string UIPanel = "KillFeed.Panel";
        private Timer _cleanupTimer;
        private bool _uiNeedsRefresh;
        private float _lastRefreshTime;
        private const float RefreshCooldown = 0.5f;
        private StringBuilder _sb = new StringBuilder(256);
        private string _cachedUIJson;
        private bool _uiCacheValid;
        private class Configuration
        {
            [JsonProperty("Show Player Kills")]
            public bool ShowPlayerKills = true;
            [JsonProperty("Show Suicides")]
            public bool ShowSuicides = true;
            [JsonProperty("Show Environmental Deaths")]
            public bool ShowEnvironmentalDeaths = true;
            [JsonProperty("Show Players Killed by Animals")]
            public bool ShowPlayersKilledByAnimals = false;
            [JsonProperty("Show Players Killed by NPCs")]
            public bool ShowPlayersKilledByNPCs = false;
            [JsonProperty("Show Players Killing Animals")]
            public bool ShowPlayersKillingAnimals = false;
            [JsonProperty("Show Players Killing NPCs")]
            public bool ShowPlayersKillingNPCs = false;
            [JsonProperty("Display Duration (seconds)")]
            public float DisplayDuration = 10f;
            [JsonProperty("Max Visible Entries")]
            public int MaxEntries = 5;
            [JsonProperty("Font Size")]
            public int FontSize = 14;
            [JsonProperty("Colors")]
            public ColorConfig Colors = new ColorConfig();
            [JsonProperty("Position")]
            public PositionConfig Position = new PositionConfig();
        }
        private class ColorConfig
        {
            [JsonProperty("Killer Name")]
            public string KillerColor = "#6BA8E5";
            [JsonProperty("Victim Name")]
            public string VictimColor = "#E56B6B";
            [JsonProperty("Weapon Name")]
            public string WeaponColor = "#D4A340";
            [JsonProperty("Distance")]
            public string DistanceColor = "#8A96A5";
            [JsonProperty("Text")]
            public string TextColor = "#C8CED5";
            [JsonProperty("Background")]
            public string BackgroundColor = "0 0 0 0";
        }
        private class PositionConfig
        {
            [JsonProperty("Anchor Min")]
            public string AnchorMin = "0.60 0.85";
            [JsonProperty("Anchor Max")]
            public string AnchorMax = "0.995 0.99";
        }
        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                _config = Config.ReadObject<Configuration>();
                if (_config == null)
                    throw new JsonException();
            }
            catch
            {
                LoadDefaultConfig();
            }
            SaveConfig();
        }
        protected override void LoadDefaultConfig() => _config = new Configuration();
        protected override void SaveConfig() => Config.WriteObject(_config);
        private void OnServerInitialized()
        {
            _cleanupTimer = timer.Every(2f, CleanupAndRefresh);
        }
        private void Unload()
        {
            _cleanupTimer?.Destroy();
            foreach (var player in BasePlayer.activePlayerList)
                CuiHelper.DestroyUi(player, UIPanel);
        }
        private void OnPlayerDeath(BasePlayer victim, HitInfo info)
        {
            if (victim == null || victim.IsNpc) return;
            ProcessPlayerDeath(victim, info);
        }
        private object OnEntityDeath(BaseCombatEntity entity, HitInfo info)
        {
            if (entity is BasePlayer) return null;
            if (!_config.ShowPlayersKillingAnimals && !_config.ShowPlayersKillingNPCs) return null;
            var attacker = info?.InitiatorPlayer;
            if (attacker == null || attacker.IsNpc) return null;
            var victimNpc = entity as BaseNpc;
            if (victimNpc == null) return null;
            bool victimIsAnimal = IsAnimal(victimNpc.ShortPrefabName);
            if (victimIsAnimal)
            {
                if (!_config.ShowPlayersKillingAnimals) return null;
            }
            else
            {
                if (!_config.ShowPlayersKillingNPCs) return null;
            }
            string victimName = GetNPCName(victimNpc.ShortPrefabName);
            string killerName = attacker.displayName;
            string weaponName = GetWeaponName(info);
            float distance = Vector3.Distance(attacker.transform.position, entity.transform.position);
            AddKillEntry(new KillEntry
            {
                KillerName = killerName,
                VictimName = victimName,
                WeaponName = weaponName,
                Distance = distance,
                Timestamp = Time.realtimeSinceStartup,
                IsSuicide = false,
                IsEnvironmental = false
            });
            return null;
        }
        private void ProcessPlayerDeath(BasePlayer victim, HitInfo info)
        {
            var attacker = info?.InitiatorPlayer;
            var attackerNpc = info?.Initiator as BaseNpc;
            bool attackerIsRealPlayer = attacker != null && !attacker.IsNpc;
            bool attackerIsAnimal = attackerNpc != null && IsAnimal(attackerNpc.ShortPrefabName);
            bool isWildBees = IsWildBeeAttack(info);
            bool isBeeGrenade = IsBeeGrenadeAttack(info);
            var damageType = info?.damageTypes.GetMajorityDamageType() ?? Rust.DamageType.Generic;
            bool isSuicide = damageType == Rust.DamageType.Suicide;
            bool isEnvironmental = !isSuicide && IsEnvironmentalDamage(damageType) && (attacker == null || attacker == victim) && attackerNpc == null && !isWildBees;
            if (isSuicide)
            {
                if (!_config.ShowSuicides) return;
            }
            else if (isEnvironmental)
            {
                if (!_config.ShowEnvironmentalDeaths) return;
            }
            else if (isBeeGrenade)
            {
                if (!_config.ShowPlayerKills) return;
            }
            else if (isWildBees)
            {
                if (!_config.ShowPlayersKilledByAnimals) return;
            }
            else if (attackerIsRealPlayer && attacker != victim)
            {
                if (!_config.ShowPlayerKills) return;
            }
            else if (attackerIsAnimal)
            {
                if (!_config.ShowPlayersKilledByAnimals) return;
            }
            else if (attacker != null && attacker.IsNpc)
            {
                if (!_config.ShowPlayersKilledByNPCs) return;
            }
            else if (attackerNpc != null && !attackerIsAnimal)
            {
                if (!_config.ShowPlayersKilledByNPCs) return;
            }
            else if (!isSuicide && !isEnvironmental)
            {
                return;
            }
            string victimName = victim.displayName;
            if (string.IsNullOrEmpty(victimName))
                return;
            bool noKillerNeeded = isSuicide || isEnvironmental;
            string killerName;
            string weaponName;
            if (noKillerNeeded)
            {
                killerName = null;
                weaponName = GetDeathReason(info, isSuicide);
            }
            else if (isBeeGrenade)
            {
                killerName = attacker.displayName;
                weaponName = "Bee Grenade";
            }
            else if (isWildBees)
            {
                killerName = "Bees";
                weaponName = "Stings";
            }
            else
            {
                killerName = GetKillerName(info);
                weaponName = GetWeaponName(info);
            }
            if (!noKillerNeeded && string.IsNullOrEmpty(killerName))
                return;
            float distance = 0f;
            if (!noKillerNeeded && info?.Initiator != null)
                distance = Vector3.Distance(info.Initiator.transform.position, victim.transform.position);
            var killEntry = new KillEntry
            {
                KillerName = killerName,
                VictimName = victimName,
                WeaponName = weaponName,
                Distance = distance,
                Timestamp = Time.realtimeSinceStartup,
                IsSuicide = isSuicide,
                IsEnvironmental = isEnvironmental
            };
            AddKillEntry(killEntry);
        }
        private void AddKillEntry(KillEntry killEntry)
        {
            if (_killEntries.Count >= _config.MaxEntries)
                _killEntries.RemoveAt(0);
            _killEntries.Add(killEntry);
            _uiNeedsRefresh = true;
            _uiCacheValid = false;
            float now = Time.realtimeSinceStartup;
            if (now - _lastRefreshTime >= RefreshCooldown)
            {
                _lastRefreshTime = now;
                RefreshAllUI();
                _uiNeedsRefresh = false;
            }
        }
        private bool IsEnvironmentalDamage(Rust.DamageType damageType)
        {
            switch (damageType)
            {
                case Rust.DamageType.Bleeding:
                case Rust.DamageType.Cold:
                case Rust.DamageType.Drowned:
                case Rust.DamageType.Fall:
                case Rust.DamageType.Heat:
                case Rust.DamageType.Hunger:
                case Rust.DamageType.Poison:
                case Rust.DamageType.Radiation:
                case Rust.DamageType.Thirst:
                    return true;
                default:
                    return false;
            }
        }
        private bool IsWildBeeAttack(HitInfo info)
        {
            if (info == null || info.Initiator == null) return false;
            if (info.InitiatorPlayer != null && !info.InitiatorPlayer.IsNpc) return false;
            string prefabName = info.Initiator.ShortPrefabName;
            return prefabName == "beeswarm" || prefabName == "bee_swarm";
        }
        private bool IsBeeGrenadeAttack(HitInfo info)
        {
            if (info == null || info.WeaponPrefab == null) return false;
            if (info.InitiatorPlayer == null || info.InitiatorPlayer.IsNpc) return false;
            string weaponName = info.WeaponPrefab.ShortPrefabName;
            return weaponName == "grenade.bee" || weaponName == "grenade_bee";
        }
        private void CleanupAndRefresh()
        {
            if (_killEntries.Count == 0)
            {
                if (_uiNeedsRefresh)
                {
                    RefreshAllUI();
                    _uiNeedsRefresh = false;
                }
                return;
            }
            float currentTime = Time.realtimeSinceStartup;
            int removed = 0;
            for (int i = _killEntries.Count - 1; i >= 0; i--)
            {
                if (currentTime - _killEntries[i].Timestamp >= _config.DisplayDuration)
                {
                    _killEntries.RemoveAt(i);
                    removed++;
                }
            }
            if (removed > 0)
                _uiCacheValid = false;
            if (removed > 0 || _uiNeedsRefresh)
            {
                RefreshAllUI();
                _uiNeedsRefresh = false;
            }
        }
        private void RefreshAllUI()
        {
            var players = BasePlayer.activePlayerList;
            int count = players.Count;
            if (!_uiCacheValid && _killEntries.Count > 0)
            {
                _cachedUIJson = BuildUIJson();
                _uiCacheValid = true;
            }
            for (int i = 0; i < count; i++)
            {
                var player = players[i];
                if (player == null || !player.IsConnected) continue;
                if (_disabledPlayers.Contains(player.userID)) continue;
                CuiHelper.DestroyUi(player, UIPanel);
                if (_killEntries.Count > 0 && _cachedUIJson != null)
                    CuiHelper.AddUi(player, _cachedUIJson);
            }
        }
        private string BuildUIJson()
        {
            var container = new CuiElementContainer();
            container.Add(new CuiPanel
            {
                Image = { Color = _config.Colors.BackgroundColor },
                RectTransform = 
                { 
                    AnchorMin = _config.Position.AnchorMin,
                    AnchorMax = _config.Position.AnchorMax
                },
                CursorEnabled = false
            }, "Hud.Menu", UIPanel);
            float yOffset = 0.95f;
            float entryHeight = 0.15f;
            int entryCount = _killEntries.Count;
            int maxEntries = _config.MaxEntries;
            for (int i = entryCount - 1; i >= 0 && (entryCount - 1 - i) < maxEntries; i--)
            {
                int index = entryCount - 1 - i;
                var entry = _killEntries[i];
                _sb.Clear();
                if (entry.IsSuicide || entry.IsEnvironmental)
                {
                    _sb.Append("<color=").Append(_config.Colors.VictimColor).Append(">").Append(entry.VictimName).Append("</color> ");
                    _sb.Append("<color=").Append(_config.Colors.WeaponColor).Append(">").Append(entry.WeaponName).Append("</color>");
                }
                else
                {
                    _sb.Append("<color=").Append(_config.Colors.KillerColor).Append(">").Append(entry.KillerName).Append("</color> ");
                    _sb.Append("<color=").Append(_config.Colors.TextColor).Append(">killed</color> ");
                    _sb.Append("<color=").Append(_config.Colors.VictimColor).Append(">").Append(entry.VictimName).Append("</color> ");
                    _sb.Append("<color=").Append(_config.Colors.TextColor).Append(">with</color> ");
                    _sb.Append("<color=").Append(_config.Colors.WeaponColor).Append(">").Append(entry.WeaponName).Append("</color>");
                    if (entry.Distance > 0)
                        _sb.Append(" at <color=").Append(_config.Colors.DistanceColor).Append(">").Append(entry.Distance.ToString("F0")).Append("m</color>");
                }
                float yMin = yOffset - (index + 1) * entryHeight;
                float yMax = yOffset - index * entryHeight;
                container.Add(new CuiLabel
                {
                    Text = 
                    { 
                        Text = _sb.ToString(),
                        FontSize = _config.FontSize,
                        Align = TextAnchor.UpperRight,
                        Color = "1 1 1 1"
                    },
                    RectTransform = 
                    { 
                        AnchorMin = $"0.01 {yMin}",
                        AnchorMax = $"0.99 {yMax}"
                    }
                }, UIPanel);
            }
            return container.ToJson();
        }
        [ChatCommand("killfeed")]
        private void KillFeedCommand(BasePlayer player, string command, string[] args)
        {
            if (args.Length == 0)
            {
                SendReply(player, "KillFeed Commands:\n/killfeed toggle - Enable/disable kill feed\n/killfeed status - Check current status");
                return;
            }
            switch (args[0].ToLower())
            {
                case "toggle":
                    if (_disabledPlayers.Contains(player.userID))
                    {
                        _disabledPlayers.Remove(player.userID);
                        SendReply(player, "KillFeed enabled!");
                        if (_killEntries.Count > 0)
                        {
                            if (!_uiCacheValid)
                            {
                                _cachedUIJson = BuildUIJson();
                                _uiCacheValid = true;
                            }
                            if (_cachedUIJson != null)
                                CuiHelper.AddUi(player, _cachedUIJson);
                        }
                    }
                    else
                    {
                        _disabledPlayers.Add(player.userID);
                        SendReply(player, "KillFeed disabled!");
                        CuiHelper.DestroyUi(player, UIPanel);
                    }
                    break;
                case "status":
                    SendReply(player, $"KillFeed is currently {(_disabledPlayers.Contains(player.userID) ? "disabled" : "enabled")}");
                    break;
                default:
                    SendReply(player, "Unknown command. Use /killfeed for help.");
                    break;
            }
        }
        private class KillEntry
        {
            public string KillerName;
            public string VictimName;
            public string WeaponName;
            public float Distance;
            public float Timestamp;
            public bool IsSuicide;
            public bool IsEnvironmental;
        }
        private string GetKillerName(HitInfo info)
        {
            if (info.InitiatorPlayer != null)
            {
                if (!info.InitiatorPlayer.IsNpc)
                    return info.InitiatorPlayer.displayName;
                string name = info.InitiatorPlayer.displayName;
                if (name.IndexOf("scientist", StringComparison.OrdinalIgnoreCase) >= 0) return "Scientist";
                if (name.IndexOf("tunnel", StringComparison.OrdinalIgnoreCase) >= 0) return "Tunnel Dweller";
                if (name.IndexOf("bandit", StringComparison.OrdinalIgnoreCase) >= 0) return "Bandit Guard";
                return name;
            }
            if (info.Initiator is BaseNpc npc)
                return GetNPCName(npc.ShortPrefabName);
            return info.Initiator != null ? info.Initiator.ShortPrefabName : "Unknown";
        }
        private string GetVictimName(BaseCombatEntity entity, BasePlayer player)
        {
            if (player != null)
            {
                if (!player.IsNpc)
                    return player.displayName;
                string name = player.displayName;
                if (name.IndexOf("scientist", StringComparison.OrdinalIgnoreCase) >= 0) return "Scientist";
                if (name.IndexOf("tunnel", StringComparison.OrdinalIgnoreCase) >= 0) return "Tunnel Dweller";
                if (name.IndexOf("bandit", StringComparison.OrdinalIgnoreCase) >= 0) return "Bandit Guard";
                return name;
            }
            if (entity is BaseNpc npc)
                return GetNPCName(npc.ShortPrefabName);
            return entity.ShortPrefabName;
        }
        private string GetNPCName(string shortName)
        {
            switch (shortName)
            {
                case "bear": return "Bear";
                case "polarbear": return "Polar Bear";
                case "boar": return "Boar";
                case "chicken": return "Chicken";
                case "horse": return "Horse";
                case "stag": return "Stag";
                case "wolf": return "Wolf";
                case "tiger": return "Tiger";
                case "panther": return "Panther";
                case "crocodile": return "Crocodile";
                case "snake": return "Snake";
                case "simpleshark": return "Shark";
                case "zombie": return "Zombie";
                case "beeswarm": return "Bees";
                case "bee_swarm": return "Bees";
                default: return shortName;
            }
        }
        private bool IsAnimal(string shortName)
        {
            switch (shortName)
            {
                case "bear":
                case "polarbear":
                case "boar":
                case "chicken":
                case "horse":
                case "stag":
                case "wolf":
                case "tiger":
                case "panther":
                case "crocodile":
                case "snake":
                case "simpleshark":
                case "beeswarm":
                case "bee_swarm":
                    return true;
                default:
                    return false;
            }
        }
        private bool IsAnimalEntity(BaseCombatEntity entity)
        {
            if (entity is BaseNpc npc)
                return IsAnimal(npc.ShortPrefabName);
            return false;
        }
        private string GetWeaponName(HitInfo info)
        {
            if (info.Weapon != null)
            {
                var item = info.Weapon.GetItem();
                if (item != null)
                    return item.info.displayName.english;
            }
            if (info.WeaponPrefab != null)
            {
                string shortName = info.WeaponPrefab.ShortPrefabName;
                return shortName.Replace(".entity", "").Replace("_", " ").Replace(".", " ");
            }
            return GetDamageTypeName(info.damageTypes.GetMajorityDamageType());
        }
        private string GetDeathReason(HitInfo info, bool isSuicide)
        {
            if (isSuicide)
                return "Suicided";
            var damageType = info.damageTypes.GetMajorityDamageType();
            switch (damageType)
            {
                case Rust.DamageType.Bleeding: return "Bled out";
                case Rust.DamageType.Cold: return "Froze to death";
                case Rust.DamageType.Drowned: return "Drowned";
                case Rust.DamageType.Fall: return "Fell to their death";
                case Rust.DamageType.Heat: return "Burned to death";
                case Rust.DamageType.Hunger: return "Starved to death";
                case Rust.DamageType.Poison: return "Was poisoned";
                case Rust.DamageType.Radiation: return "Died from radiation";
                case Rust.DamageType.Thirst: return "Died of thirst";
                default: return "Died";
            }
        }
        private string GetDamageTypeName(Rust.DamageType damageType)
        {
            switch (damageType)
            {
                case Rust.DamageType.Bite: return "Bite";
                case Rust.DamageType.Bleeding: return "Bleeding";
                case Rust.DamageType.Blunt: return "Blunt";
                case Rust.DamageType.Bullet: return "Bullet";
                case Rust.DamageType.Cold: return "Cold";
                case Rust.DamageType.Drowned: return "Drowning";
                case Rust.DamageType.ElectricShock: return "Electric Shock";
                case Rust.DamageType.Explosion: return "Explosion";
                case Rust.DamageType.Fall: return "Fall";
                case Rust.DamageType.Heat: return "Heat";
                case Rust.DamageType.Hunger: return "Hunger";
                case Rust.DamageType.Poison: return "Poison";
                case Rust.DamageType.Radiation: return "Radiation";
                case Rust.DamageType.Slash: return "Slash";
                case Rust.DamageType.Stab: return "Stab";
                case Rust.DamageType.Suicide: return "Suicide";
                case Rust.DamageType.Thirst: return "Thirst";
                default: return "Unknown";
            }
        }
    }
}

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