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

FREE ChtoZaPrefab : 1.2.0 1.2.0

Нет прав для скачивания
C#:
using Oxide.Core;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Newtonsoft.Json;
using System.IO;

namespace Oxide.Plugins
{
    [Info("ChtoZaPrefab", "Semnavmeleon", "1.2.0")]
    [Description("Entity inspector with ESP visualization (RU/EN)")]
    public class ChtoZaPrefab : RustPlugin
    {
        #region Configuration
        private Configuration _config;
        private Timer _updatePlayersTimer;
        private Timer _updateRaysTimer;
        
        public class Configuration
        {
            [JsonProperty("Permission to use")]
            public string Permission { get; set; } = "chtozaprefab.use";
            [JsonProperty("Auto-enable for admins")]
            public bool AutoEnableForAdmins { get; set; } = false;
            [JsonProperty("Max distance (m)")]
            public float MaxDistance { get; set; } = 100f;

            [JsonProperty("Enable ESP highlighting")]
            public bool EnableESP { get; set; } = true;
            [JsonProperty("ESP color")]
            public string ESPColor { get; set; } = "1 0 0 1";
            [JsonProperty("ESP line thickness")]
            public float ESPThickness { get; set; } = 0.07f;
            [JsonProperty("Draw look ray")]
            public bool DrawLookRay { get; set; } = true;
            [JsonProperty("Ray color")]
            public string RayColor { get; set; } = "0 1 1 0.8";
            [JsonProperty("Ray thickness")]
            public float RayThickness { get; set; } = 0.015f;
            [JsonProperty("Highlight target prefab")]
            public bool HighlightPrefab { get; set; } = true;
            [JsonProperty("Highlight color")]
            public string HighlightColor { get; set; } = "1 0 0.5 1";
            [JsonProperty("Highlight line thickness")]
            public float HighlightLineThickness { get; set; } = 0.03f;
            [JsonProperty("Highlight pulse period (seconds)")]
            public float HighlightPulsePeriod { get; set; } = 2.5f;
            [JsonProperty("Highlight draw duration (seconds)")]
            public float HighlightDrawDuration { get; set; } = 0.25f;
            [JsonProperty("Fixed box size")]
            public float FixedBoxSize { get; set; } = 1.5f;
            [JsonProperty("Update moving objects")]
            public bool UpdateMovingObjects { get; set; } = true;
            [JsonProperty("Precise framing threshold (m)")]
            public float PreciseFramingThreshold { get; set; } = 3f;
            [JsonProperty("Simplify distance (m)")]
            public float SimplifyDistance { get; set; } = 40f;

            [JsonProperty("Text color")]
            public string TextColor { get; set; } = "1 1 1 1";
            [JsonProperty("Header color")]
            public string HeaderColor { get; set; } = "1 1 0 1";
            [JsonProperty("Basic properties color")]
            public string BasicColor { get; set; } = "1 1 1 1";
            [JsonProperty("Entity properties color")]
            public string EntityColor { get; set; } = "0 1 1 1";
            [JsonProperty("Player properties color")]
            public string PlayerColor { get; set; } = "0 1 0 1";
            [JsonProperty("Building properties color")]
            public string BuildingColor { get; set; } = "1 0.5 0 1";
            [JsonProperty("Technical properties color")]
            public string TechnicalColor { get; set; } = "1 1 1 1";

            [JsonProperty("Font size")]
            public int FontSize { get; set; } = 13;
            [JsonProperty("Position X (pixels)")]
            public int PositionX { get; set; } = 25;
            [JsonProperty("Position Y (pixels)")]
            public int PositionY { get; set; } = 0;
            [JsonProperty("UI background color")]
            public string UIColor { get; set; } = "0.15 0.15 0.2 1";
            [JsonProperty("UI opacity")]
            public float UIOpacity { get; set; } = 0.88f;

            [JsonProperty("Default language")]
            public string DefaultLanguage { get; set; } = "auto";
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                _config = Config.ReadObject<Configuration>();
                if (_config == null) throw new Exception();
                if (_config.PositionY == 0) _config.PositionY = 140;
            }
            catch
            {
                LoadDefaultConfig();
            }
            SaveConfig();
        }

        protected override void LoadDefaultConfig()
        {
            _config = new Configuration();
        }

        protected override void SaveConfig()
        {
            Config.WriteObject(_config, true);
        }
        #endregion

        #region Localization
        private enum Language { Russian, English, Auto }
        private class Localization
        {
            public Language Lang { get; private set; }
            private Dictionary<string, string> _strings = new Dictionary<string, string>();
            public Localization(Language lang)
            {
                Lang = lang;
                LoadTranslations();
            }
            private void LoadTranslations()
            {
                _strings.Clear();
                Language effectiveLang = (Lang == Language.Auto) ? Language.English : Lang;
                if (effectiveLang == Language.Russian)
                {
                    _strings["panel_no_target"] = "Нет цели";
                    _strings["panel_target_static"] = "Цель: Статический объект";
                    _strings["panel_target_player"] = "Игрок";
                    _strings["panel_distance"] = "Дистанция";
                    _strings["panel_distance_unit"] = "м";
                    _strings["panel_prefab_id"] = "ID префаба";
                    _strings["panel_prefab"] = "Префаб";
                    _strings["panel_owner_id"] = "ID владельца";
                    _strings["panel_owner_name"] = "Владелец";
                    _strings["panel_position"] = "Позиция";
                    _strings["panel_rotation"] = "Поворот";
                    _strings["panel_collider"] = "Коллайдер";
                    _strings["panel_object"] = "Объект";
                    _strings["panel_health"] = "Здоровье";
                    _strings["panel_steam_id"] = "Steam ID";
                    _strings["panel_display_name"] = "Имя";
                    _strings["panel_ip"] = "IP";
                    _strings["panel_admin"] = "Админ";
                    _strings["panel_sleeping"] = "Спит";
                    _strings["panel_team"] = "Команда";
                    _strings["panel_grade"] = "Уровень";
                    _strings["panel_online"] = "Онлайн";
                    _strings["panel_authorized"] = "Авторизовано";
                    _strings["panel_authorized_suffix"] = " игроков";
                    _strings["panel_driver"] = "Водитель";
                    _strings["panel_items"] = "Предметы";
                    _strings["panel_loot_items"] = "Лут";
                    _strings["panel_door_password"] = "Пароль";
                    _strings["panel_door_keylock"] = "Замок (ключ)";
                    _strings["panel_button_on"] = "ВКЛ";
                    _strings["panel_button_off"] = "ВЫКЛ";
                    _strings["cmd_toggle_on"] = "Плагин включен";
                    _strings["cmd_toggle_off"] = "Плагин выключен";
                    _strings["cmd_panel_opened"] = "Панель открыта";
                    _strings["cmd_panel_closed"] = "Панель закрыта";
                    _strings["cmd_no_permission"] = "У вас нет доступа к этой команде";
                    _strings["cmd_lang_set_ru"] = "Язык изменён на Русский";
                    _strings["cmd_lang_set_en"] = "Язык изменён на English";
                    _strings["cmd_lang_auto"] = "Язык установлен автоматически: {0}";
                    _strings["lang_russian"] = "Русский";
                    _strings["lang_english"] = "English";
                    _strings["esp_triangle"] = "Треугольный ESP";
                    _strings["esp_complex_box"] = "Сложный бокс";
                    _strings["section_entity"] = "Сущность";
                    _strings["section_transform"] = "Трансформ";
                    _strings["section_owner"] = "Владелец";
                    _strings["section_health"] = "Здоровье";
                    _strings["section_player"] = "Игрок";
                    _strings["section_building"] = "Строительство";
                    _strings["section_turret"] = "Турель";
                    _strings["section_vehicle"] = "Транспорт";
                    _strings["section_inventory"] = "Инвентарь";
                    _strings["section_loot"] = "Лут";
                    _strings["section_door_lock"] = "Дверь / Замок";
                    _strings["section_components"] = "Компоненты";
                    _strings["section_bounds"] = "Границы";
                    _strings["section_identity"] = "Идентификация";
                    _strings["section_static_object"] = "Статический объект";
                    _strings["label_distance"] = "Дистанция";
                    _strings["label_prefab_id"] = "ID префаба";
                    _strings["label_full_path"] = "Полный путь";
                    _strings["label_short_name"] = "Краткое имя";
                    _strings["label_net_id"] = "Net ID";
                    _strings["label_destroyed"] = "Уничтожен";
                    _strings["label_position"] = "Позиция";
                    _strings["label_rotation_euler"] = "Поворот (эйлер)";
                    _strings["label_local_position"] = "Лок. позиция";
                    _strings["label_local_rotation"] = "Лок. поворот";
                    _strings["label_local_scale"] = "Лок. масштаб";
                    _strings["label_colliders"] = "Коллайдеры";
                    _strings["label_renderers"] = "Рендереры";
                    _strings["label_rigidbody"] = "Rigidbody";
                    _strings["label_steam_id"] = "Steam ID";
                    _strings["label_display_name"] = "Имя";
                    _strings["label_online"] = "Онлайн";
                    _strings["label_current"] = "Текущее";
                    _strings["label_max"] = "Макс.";
                    _strings["label_building_id"] = "ID постройки";
                    _strings["label_modules"] = "Модули";
                    _strings["label_contents"] = "Содержимое";
                    _strings["label_open"] = "Открыто";
                    _strings["label_object_name"] = "Имя объекта";
                    _strings["label_center"] = "Центр";
                    _strings["label_size"] = "Размер";
                    _strings["label_colliders_total"] = "Коллайдеров всего";
                    _strings["label_yes"] = "Да";
                    _strings["label_no"] = "Нет";
                    _strings["target_unknown"] = "Цель: Неизвестный объект";
                    _strings["target_none"] = "Цель: Нет";
                    _strings["player_not_active"] = "Игрок не активен";
                    _strings["target_hit_point"] = "Цель: Точка попадания";
                    _strings["target_type_player"] = "Игрок";
                    _strings["target_type_building_block"] = "Строительный блок";
                    _strings["target_type_turret"] = "Турель";
                    _strings["target_type_vehicle"] = "Транспорт";
                    _strings["target_type_container"] = "Контейнер";
                    _strings["target_type_loot_container"] = "Контейнер с лутом";
                    _strings["target_type_combat_entity"] = "Боевая сущность";
                    _strings["target_type_entity"] = "Сущность";
                    _strings["target_type_object"] = "Объект";
                }
                else
                {
                    _strings["panel_no_target"] = "No target";
                    _strings["panel_target_static"] = "Target: Static Object";
                    _strings["panel_target_player"] = "Player";
                    _strings["panel_distance"] = "Distance";
                    _strings["panel_distance_unit"] = "m";
                    _strings["panel_prefab_id"] = "Prefab ID";
                    _strings["panel_prefab"] = "Prefab";
                    _strings["panel_owner_id"] = "Owner ID";
                    _strings["panel_owner_name"] = "Owner";
                    _strings["panel_position"] = "Position";
                    _strings["panel_rotation"] = "Rotation";
                    _strings["panel_collider"] = "Collider";
                    _strings["panel_object"] = "Object";
                    _strings["panel_health"] = "Health";
                    _strings["panel_steam_id"] = "Steam ID";
                    _strings["panel_display_name"] = "Display Name";
                    _strings["panel_ip"] = "IP";
                    _strings["panel_admin"] = "Admin";
                    _strings["panel_sleeping"] = "Sleeping";
                    _strings["panel_team"] = "Team";
                    _strings["panel_grade"] = "Grade";
                    _strings["panel_online"] = "Online";
                    _strings["panel_authorized"] = "Authorized";
                    _strings["panel_authorized_suffix"] = " players";
                    _strings["panel_driver"] = "Driver";
                    _strings["panel_items"] = "Items";
                    _strings["panel_loot_items"] = "Loot Items";
                    _strings["panel_door_password"] = "Password";
                    _strings["panel_door_keylock"] = "Key lock";
                    _strings["panel_button_on"] = "ON";
                    _strings["panel_button_off"] = "OFF";
                    _strings["cmd_toggle_on"] = "Plugin enabled";
                    _strings["cmd_toggle_off"] = "Plugin disabled";
                    _strings["cmd_panel_opened"] = "Panel opened";
                    _strings["cmd_panel_closed"] = "Panel closed";
                    _strings["cmd_no_permission"] = "You don't have permission for this command";
                    _strings["cmd_lang_set_ru"] = "Language changed to Русский";
                    _strings["cmd_lang_set_en"] = "Language changed to English";
                    _strings["cmd_lang_auto"] = "Language set automatically: {0}";
                    _strings["lang_russian"] = "Русский";
                    _strings["lang_english"] = "English";
                    _strings["esp_triangle"] = "Triangle ESP";
                    _strings["esp_complex_box"] = "Complex Box";
                    _strings["section_entity"] = "Entity";
                    _strings["section_transform"] = "Transform";
                    _strings["section_owner"] = "Owner";
                    _strings["section_health"] = "Health";
                    _strings["section_player"] = "Player";
                    _strings["section_building"] = "Building";
                    _strings["section_turret"] = "Turret";
                    _strings["section_vehicle"] = "Vehicle";
                    _strings["section_inventory"] = "Inventory";
                    _strings["section_loot"] = "Loot";
                    _strings["section_door_lock"] = "Door / Lock";
                    _strings["section_components"] = "Components";
                    _strings["section_bounds"] = "Bounds";
                    _strings["section_identity"] = "Identity";
                    _strings["section_static_object"] = "Static Object";
                    _strings["label_distance"] = "Distance";
                    _strings["label_prefab_id"] = "Prefab ID";
                    _strings["label_full_path"] = "Full path";
                    _strings["label_short_name"] = "Short name";
                    _strings["label_net_id"] = "Net ID";
                    _strings["label_destroyed"] = "Destroyed";
                    _strings["label_position"] = "Position";
                    _strings["label_rotation_euler"] = "Rotation (euler)";
                    _strings["label_local_position"] = "Local position";
                    _strings["label_local_rotation"] = "Local rotation";
                    _strings["label_local_scale"] = "Local scale";
                    _strings["label_colliders"] = "Colliders";
                    _strings["label_renderers"] = "Renderers";
                    _strings["label_rigidbody"] = "Rigidbody";
                    _strings["label_steam_id"] = "Steam ID";
                    _strings["label_display_name"] = "Display name";
                    _strings["label_online"] = "Online";
                    _strings["label_current"] = "Current";
                    _strings["label_max"] = "Max";
                    _strings["label_building_id"] = "Building ID";
                    _strings["label_modules"] = "Modules";
                    _strings["label_contents"] = "Contents";
                    _strings["label_open"] = "Open";
                    _strings["label_object_name"] = "Object name";
                    _strings["label_center"] = "Center";
                    _strings["label_size"] = "Size";
                    _strings["label_colliders_total"] = "Colliders (total)";
                    _strings["label_yes"] = "Yes";
                    _strings["label_no"] = "No";
                    _strings["target_unknown"] = "Target: Unknown Object";
                    _strings["target_none"] = "Target: None";
                    _strings["player_not_active"] = "Player not active";
                    _strings["target_hit_point"] = "Target: Hit Point";
                    _strings["target_type_player"] = "Player";
                    _strings["target_type_building_block"] = "Building Block";
                    _strings["target_type_turret"] = "Turret";
                    _strings["target_type_vehicle"] = "Vehicle";
                    _strings["target_type_container"] = "Container";
                    _strings["target_type_loot_container"] = "Loot Container";
                    _strings["target_type_combat_entity"] = "Combat Entity";
                    _strings["target_type_entity"] = "Entity";
                    _strings["target_type_object"] = "Object";
                }
            }
            public string Get(string key, params object[] args)
            {
                if (_strings.TryGetValue(key, out string value))
                {
                    return args.Length > 0 ? string.Format(value, args) : value;
                }
                return $"[{key}]";
            }
        }

        private Dictionary<ulong, Language> _playerLanguages = new Dictionary<ulong, Language>();
        private string _langsFilePath;

        private void LoadLanguages()
        {
            _langsFilePath = Interface.Oxide.DataDirectory + Path.DirectorySeparatorChar + "ChtoZaPrefab" + Path.DirectorySeparatorChar + "langs.json";
            try
            {
                if (File.Exists(_langsFilePath))
                {
                    var json = File.ReadAllText(_langsFilePath);
                    _playerLanguages = JsonConvert.DeserializeObject<Dictionary<ulong, Language>>(json) ?? new Dictionary<ulong, Language>();
                }
            }
            catch
            {
                _playerLanguages = new Dictionary<ulong, Language>();
            }
        }

        private void SaveLanguages()
        {
            try
            {
                Directory.CreateDirectory(Path.GetDirectoryName(_langsFilePath));
                var json = JsonConvert.SerializeObject(_playerLanguages, Formatting.Indented);
                File.WriteAllText(_langsFilePath, json);
            }
            catch (Exception ex)
            {
                Puts($"Error saving languages: {ex.Message}");
            }
        }

        private Language GetPlayerLanguage(BasePlayer player)
        {
            if (player == null) return Language.English;
            if (_playerLanguages.TryGetValue(player.userID, out Language lang))
            {
                if (lang == Language.Auto)
                {
                    string ip = player.IPlayer?.Address ?? "";
                    return ip.EndsWith(".ru") || ip.EndsWith(".su") || ip.EndsWith(".by") ||
                           ip.EndsWith(".kz") || ip.EndsWith(".uz") || ip.StartsWith("10.") ||
                           ip.StartsWith("172.16.") || ip.StartsWith("192.168.") ? Language.Russian : Language.English;
                }
                return lang;
            }
            if (_config.DefaultLanguage == "auto")
            {
                string ip = player.IPlayer?.Address ?? "";
                return ip.EndsWith(".ru") || ip.EndsWith(".su") || ip.EndsWith(".by") ||
                       ip.EndsWith(".kz") || ip.EndsWith(".uz") || ip.StartsWith("10.") ||
                       ip.StartsWith("172.16.") || ip.StartsWith("192.168.") ? 
                       Language.Russian : Language.English;
            }
            lang = _config.DefaultLanguage.ToLower().StartsWith("ru") ? Language.Russian : Language.English;
            _playerLanguages[player.userID] = lang;
            SaveLanguages();
            return lang;
        }

        private void SetPlayerLanguage(BasePlayer player, Language lang)
        {
            if (player == null) return;
            _playerLanguages[player.userID] = lang;
            SaveLanguages();
        }

        private Language GetStoredPlayerLanguage(BasePlayer player)
        {
            if (player == null) return Language.Auto;
            return _playerLanguages.TryGetValue(player.userID, out Language lang) ? lang : Language.Auto;
        }

        private Localization GetLocalization(BasePlayer player)
        {
            return new Localization(GetPlayerLanguage(player));
        }
        #endregion

        #region Data Structures
        [Serializable]
        private class PlayerData
        {
            public bool IsEnabled { get; set; }
            public string TargetUI { get; set; }
            public string LastTargetKey { get; set; }
            public float LastTargetTime { get; set; }
            public Dictionary<string, Vector3> ObjectPositions { get; set; } = new Dictionary<string, Vector3>();
            public Dictionary<string, float> ObjectUpdateTimes { get; set; } = new Dictionary<string, float>();

            [NonSerialized] public RaycastHit[] RaycastHitsCache = new RaycastHit[32];
            [NonSerialized] public Collider LastCollider;
            [NonSerialized] public BaseEntity LastEntity;
            [NonSerialized] public float LastRaycastTime;
            [NonSerialized] public Vector3 LastRaycastOrigin;
            [NonSerialized] public Vector3 LastRaycastDirection;
        }

        private Dictionary<ulong, PlayerData> _playerData = new Dictionary<ulong, PlayerData>();

        private readonly HashSet<string> _complexPrefabs = new HashSet<string>
        {
            "door", "box", "locker", "fridge", "cupboard", "vending",
            "turret", "sam.site", "trap", "workbench", "research",
            "tool", "computer", "switch", "button", "lever", "hatch",
            "shutter", "gate", "computerstation", "rf.detector"
        };

        private readonly HashSet<string> _simplePrefabs = new HashSet<string>
        {
            "wall", "floor", "foundation", "ceiling", "stair", "ramp",
            "rock", "tree", "ore", "stone", "barrel", "crate",
            "pillar", "window", "hedge", "fence", "sign"
        };

        private class HitInfo
        {
            public Vector3? HitPoint { get; set; }
            public string DisplayText { get; set; }
            public Collider Collider { get; set; }
            public string PrefabName { get; set; }
            public float Distance { get; set; }
        }
        #endregion

        #region Lifecycle
        void Init()
        {
            permission.RegisterPermission(_config.Permission, this);
            LoadLanguages();
            _updatePlayersTimer = timer.Every(0.2f, UpdateAllPlayers);
            _updateRaysTimer = timer.Every(2.0f, UpdateAllRays);
        }

        void OnServerInitialized()
        {
            foreach (var player in BasePlayer.activePlayerList)
            {
                OnPlayerConnected(player);
            }
        }

        void Unload()
        {
            if (_updatePlayersTimer != null && !_updatePlayersTimer.Destroyed)
            {
                _updatePlayersTimer.Destroy();
                _updatePlayersTimer = null;
            }
            if (_updateRaysTimer != null && !_updateRaysTimer.Destroyed)
            {
                _updateRaysTimer.Destroy();
                _updateRaysTimer = null;
            }
            foreach (var player in BasePlayer.activePlayerList)
            {
                DestroyTargetUI(player);
                ClearPlayerDrawings(player);
            }
            _playerData.Clear();
            _playerLanguages.Clear();
        }

        void OnPlayerConnected(BasePlayer player)
        {
            if (player == null) return;
            timer.In(2f, () =>
            {
                if (player == null || !player.IsConnected) return;
                if (permission.UserHasPermission(player.UserIDString, _config.Permission))
                {
                    bool shouldEnable = _config.AutoEnableForAdmins && player.IsAdmin;
                    if (!_playerData.ContainsKey(player.userID))
                    {
                        _playerData[player.userID] = new PlayerData { IsEnabled = shouldEnable };
                    }
                }
            });
        }

        void OnPlayerDisconnected(BasePlayer player)
        {
            if (player == null) return;
            DestroyTargetUI(player);
            ClearPlayerDrawings(player);
            _playerData.Remove(player.userID);
        }
        #endregion

        #region Commands
        [ChatCommand("pref")]
        void CommandLookTarget(BasePlayer player, string command, string[] args)
        {
            if (!HasPermission(player))
                return;
            var playerData = GetPlayerData(player.userID);
            if (args.Length > 0 && args[0].ToLower() == "lang")
            {
                if (args.Length > 1)
                {
                    Language newLang = args[1].ToLower().StartsWith("ru") ? Language.Russian : (args[1].ToLower().StartsWith("en") ? Language.English : Language.Auto);
                    SetPlayerLanguage(player, newLang);
                    if (!string.IsNullOrEmpty(playerData.TargetUI))
                    {
                        DestroyTargetUI(player);
                        CreateTargetUI(player);
                    }
                }
                return;
            }
            if (!string.IsNullOrEmpty(playerData.TargetUI))
            {
                DestroyTargetUI(player);
            }
            else
            {
                CreateTargetUI(player);
            }
        }

        [ConsoleCommand("pref.closeui")]
        void ConsoleCloseUI(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            if (player != null) DestroyTargetUI(player);
        }

        [ConsoleCommand("pref.toggle")]
        void ConsoleToggle(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            if (player == null || !HasPermission(player)) return;
            ToggleLookTarget(player);
        }

        [ConsoleCommand("pref.lang.cycle")]
        void ConsoleLangCycle(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            if (player == null || !HasPermission(player)) return;
            Language current = GetStoredPlayerLanguage(player);
            Language next = current == Language.Russian ? Language.English : (current == Language.English ? Language.Auto : Language.Russian);
            SetPlayerLanguage(player, next);
            var playerData = GetPlayerData(player.userID);
            if (!string.IsNullOrEmpty(playerData.TargetUI))
            {
                DestroyTargetUI(player);
                CreateTargetUI(player);
            }
        }
        #endregion

        #region Core Update Logic
        void UpdateAllPlayers()
        {
            float currentTime = Time.realtimeSinceStartup;

            foreach (var kvp in _playerData)
            {
                var player = BasePlayer.FindByID(kvp.Key);
                if (player == null || !player.IsConnected || player.IsDead() || !kvp.Value.IsEnabled) continue;

                var playerData = kvp.Value;
                var hitInfo = GetRaycastHitInfo(player, playerData, currentTime);
                string currentTargetKey = hitInfo.Collider != null
                    ? $"{hitInfo.Collider.GetInstanceID()}_{hitInfo.PrefabName}"
                    : null;

                if (playerData.LastTargetKey != null && currentTargetKey != playerData.LastTargetKey)
                {
                    ClearOldESPDrawings(player);
                }

                UpdateTargetInfo(player, hitInfo, playerData);

                if (_config.EnableESP && _config.HighlightPrefab && _config.UpdateMovingObjects && hitInfo.Collider != null)
                {
                    UpdateESPForMovingObjects(player, playerData, hitInfo.Collider, currentTime);
                }
            }
        }

        void UpdateAllRays()
        {
            foreach (var kvp in _playerData)
            {
                if (!kvp.Value.IsEnabled) continue;
                var player = BasePlayer.FindByID(kvp.Key);
                if (player == null || !player.IsConnected || player.IsDead()) continue;

                if (_config.DrawLookRay)
                {
                    var hitInfo = GetCachedRaycastHitInfo(player, kvp.Value);
                    Vector3 rayEnd = hitInfo.HitPoint.HasValue
                        ? hitInfo.HitPoint.Value
                        : player.eyes.position + player.eyes.BodyForward() * _config.MaxDistance;

                    DrawLookRay(player, rayEnd);
                }
            }
        }

        void UpdateTargetInfo(BasePlayer player, HitInfo hitInfo, PlayerData playerData)
        {
            playerData.LastTargetTime = Time.realtimeSinceStartup;

            if (hitInfo.HitPoint.HasValue)
            {
                UpdateTargetUI(player, hitInfo.DisplayText);

                string targetKey = hitInfo.Collider != null 
                    ? $"{hitInfo.Collider.GetInstanceID()}_{hitInfo.PrefabName}"
                    : null;

                if (targetKey != null)
                {
                    bool targetChanged = playerData.LastTargetKey != targetKey;

                    if (targetChanged)
                    {
                        playerData.LastTargetKey = targetKey;
                        playerData.ObjectPositions[targetKey] = Vector3.zero;
                        playerData.ObjectUpdateTimes[targetKey] = 0f;
                    }

                    if (_config.EnableESP && _config.HighlightPrefab)
                    {
                        Vector3 objectCenter = hitInfo.Collider.bounds.center;
                        HighlightObject(player, hitInfo.Collider, objectCenter, hitInfo.Distance, targetChanged);
                    }
                }
            }
            else
            {
                var loc = GetLocalization(player);
                if (!string.IsNullOrEmpty(playerData.TargetUI))
                {
                    UpdateTargetUI(player, CreateColoredLine(loc.Get("panel_no_target"), "#FF0000", _config.HeaderColor));
                }

                if (!string.IsNullOrEmpty(playerData.LastTargetKey))
                {
                    ClearOldESPDrawings(player);
                    playerData.LastTargetKey = null;
                }
            }
        }

        void UpdateESPForMovingObjects(BasePlayer player, PlayerData playerData, Collider currentCollider, float currentTime)
        {
            if (string.IsNullOrEmpty(playerData.LastTargetKey)) return;

            string targetKey = playerData.LastTargetKey;

            if (playerData.ObjectUpdateTimes.TryGetValue(targetKey, out float lastUpdate))
            {
                if (currentTime - lastUpdate < 0.1f) return;

                playerData.ObjectUpdateTimes[targetKey] = currentTime;

                string currentTargetKey = $"{currentCollider.GetInstanceID()}_{GetPrefabName(currentCollider)}";
                if (currentTargetKey == targetKey)
                {
                    Vector3 currentPosition = GetObjectPosition(currentCollider.gameObject);

                    if (playerData.ObjectPositions.TryGetValue(targetKey, out Vector3 lastPosition))
                    {
                        if (Vector3.Distance(lastPosition, currentPosition) > 0.1f)
                        {
                            playerData.ObjectPositions[targetKey] = currentPosition;
                            ClearPlayerDrawings(player);
                            Vector3 objectCenter = currentCollider.bounds.center;
                            HighlightObject(player, currentCollider, objectCenter, Vector3.Distance(player.eyes.position, objectCenter), true);
                        }
                    }
                    else
                    {
                        playerData.ObjectPositions[targetKey] = currentPosition;
                    }
                }
            }
        }

        private string GetPrefabName(Collider collider)
        {
            if (collider == null) return "";
            var entity = collider.GetComponent<BaseEntity>();
            return entity != null ? entity.ShortPrefabName : collider.name.Replace("(Clone)", "").Trim();
        }
        #endregion

        #region Raycast & Hit Detection
        HitInfo GetRaycastHitInfo(BasePlayer player, PlayerData playerData, float currentTime)
        {
            if (player == null || player.IsDead()) return new HitInfo();

            Vector3 eyePos = player.eyes.position;
            Vector3 forward = player.eyes.BodyForward();

            if (playerData.LastRaycastTime > 0 && 
                currentTime - playerData.LastRaycastTime < 0.1f &&
                Vector3.Distance(playerData.LastRaycastOrigin, eyePos) < 0.01f &&
                Vector3.Distance(playerData.LastRaycastDirection, forward) < 0.01f &&
                playerData.LastCollider != null)
            {
                return new HitInfo
                {
                    HitPoint = playerData.LastCollider.bounds.center,
                    DisplayText = GetCachedDisplayText(player, playerData.LastCollider, playerData.LastEntity),
                    Collider = playerData.LastCollider,
                    PrefabName = playerData.LastEntity != null ? playerData.LastEntity.ShortPrefabName : "",
                    Distance = Vector3.Distance(eyePos, playerData.LastCollider.bounds.center)
                };
            }

            int layerMask = ~((1 << 23) | (1 << 4));
            RaycastHit hit;

            if (!Physics.Raycast(eyePos, forward, out hit, _config.MaxDistance, layerMask, QueryTriggerInteraction.Ignore))
                return new HitInfo();

            if (hit.collider.gameObject.layer == 23 || hit.collider.gameObject.layer == 4) 
                return new HitInfo();

            Collider hitCollider = hit.collider;
            if (hitCollider == null)
            {
                var loc = GetLocalization(player);
                float distanceToHitPoint = Vector3.Distance(eyePos, hit.point);
                var lines = new List<string>();
                lines.Add($"<color={_config.HeaderColor}>——— {loc.Get("target_hit_point")} ———</color>");
                lines.Add(ColorizeText(loc.Get("label_distance"), $"{distanceToHitPoint:F1}{loc.Get("panel_distance_unit")}", _config.EntityColor));
                lines.Add(ColorizeText(loc.Get("label_position"), $"{hit.point.x:F1}, {hit.point.y:F1}, {hit.point.z:F1}", _config.EntityColor));
                return new HitInfo
                {
                    HitPoint = hit.point,
                    DisplayText = string.Join("\n", lines),
                    Distance = distanceToHitPoint
                };
            }

            playerData.LastRaycastTime = currentTime;
            playerData.LastRaycastOrigin = eyePos;
            playerData.LastRaycastDirection = forward;
            playerData.LastCollider = hitCollider;
            playerData.LastEntity = hitCollider.GetComponent<BaseEntity>() ?? hitCollider.GetComponentInParent<BaseEntity>();

            string displayText = GetEntityDisplayInfo(player, hitCollider, playerData.LastEntity);
            float distance = playerData.LastEntity != null 
                ? Vector3.Distance(eyePos, playerData.LastEntity.transform.position)
                : Vector3.Distance(eyePos, hit.point);

            return new HitInfo
            {
                HitPoint = hit.point,
                DisplayText = displayText,
                Collider = hitCollider,
                PrefabName = playerData.LastEntity != null ? playerData.LastEntity.ShortPrefabName : "",
                Distance = distance
            };
        }

        HitInfo GetRaycastHitInfo(BasePlayer player)
        {
            return GetRaycastHitInfo(player, GetPlayerData(player.userID), Time.realtimeSinceStartup);
        }

        HitInfo GetCachedRaycastHitInfo(BasePlayer player, PlayerData playerData)
        {
            if (playerData.LastCollider != null && 
                Time.realtimeSinceStartup - playerData.LastRaycastTime < 0.5f)
            {
                return new HitInfo
                {
                    HitPoint = playerData.LastCollider.bounds.center,
                    DisplayText = GetCachedDisplayText(player, playerData.LastCollider, playerData.LastEntity),
                    Collider = playerData.LastCollider,
                    PrefabName = playerData.LastEntity != null ? playerData.LastEntity.ShortPrefabName : "",
                    Distance = Vector3.Distance(player.eyes.position, playerData.LastCollider.bounds.center)
                };
            }
            return GetRaycastHitInfo(player, playerData, Time.realtimeSinceStartup);
        }

        private string GetEntityDisplayInfo(BasePlayer player, Collider collider, BaseEntity entity)
        {
            if (entity != null)
            {
                Vector3 entityPosition = entity.transform.position;
                float entityDistance = Vector3.Distance(player.eyes.position, entityPosition);
                return GetEntityInfo(entity, entityDistance, entityPosition, player);
            }
            else
            {
                return GetStaticObjectFullInfo(player, collider);
            }
        }

        string GetStaticObjectFullInfo(BasePlayer player, Collider collider)
        {
            var loc = GetLocalization(player);
            if (collider == null) return CreateColoredLine(loc.Get("target_none"), AlertColor, _config.HeaderColor);
            List<string> lines = new List<string>();

            AddSection(lines, loc.Get("section_static_object"), GetSectionColor("Static Object"));
            float dist = Vector3.Distance(player.eyes.position, collider.bounds.center);
            AddLine(lines, loc.Get("label_distance"), $"{dist:F1}{loc.Get("panel_distance_unit")}", _config.EntityColor);

            string objectName = collider.gameObject.name.Replace("(Clone)", "").Trim();
            AddSection(lines, loc.Get("section_identity"), GetSectionColor("Identity"));
            AddLine(lines, loc.Get("label_object_name"), objectName, _config.TechnicalColor);
            string fullPrefabPath = GetStaticPrefabPath(collider.gameObject);
            if (!string.IsNullOrEmpty(fullPrefabPath)) AddLine(lines, loc.Get("label_full_path"), fullPrefabPath, _config.EntityColor);

            Transform tr = collider.transform;
            AddSection(lines, loc.Get("section_transform"), GetSectionColor("Transform"));
            AddLine(lines, loc.Get("label_position"), $"{tr.position.x:F2}, {tr.position.y:F2}, {tr.position.z:F2}", _config.BasicColor);
            AddLine(lines, loc.Get("label_rotation_euler"), $"{tr.rotation.eulerAngles.x:F0}, {tr.rotation.eulerAngles.y:F0}, {tr.rotation.eulerAngles.z:F0}", _config.TechnicalColor);
            AddLine(lines, loc.Get("label_local_position"), $"{tr.localPosition.x:F2}, {tr.localPosition.y:F2}, {tr.localPosition.z:F2}", _config.TechnicalColor);
            AddLine(lines, loc.Get("label_local_rotation"), $"{tr.localRotation.eulerAngles.x:F0}, {tr.localRotation.eulerAngles.y:F0}, {tr.localRotation.eulerAngles.z:F0}", _config.TechnicalColor);
            AddLine(lines, loc.Get("label_local_scale"), $"{tr.localScale.x:F2}, {tr.localScale.y:F2}, {tr.localScale.z:F2}", _config.TechnicalColor);

            AddSection(lines, loc.Get("section_components"), GetSectionColor("Components"));
            AddLine(lines, loc.Get("panel_collider"), collider.GetType().Name, _config.TechnicalColor);
            var allColliders = collider.gameObject.GetComponentsInChildren<Collider>(true);
            if (allColliders != null && allColliders.Length > 1)
                AddLine(lines, loc.Get("label_colliders_total"), allColliders.Length.ToString(), _config.TechnicalColor);
            var renderers = collider.gameObject.GetComponentsInChildren<Renderer>(true);
            AddLine(lines, loc.Get("label_renderers"), renderers != null ? renderers.Length.ToString() : "0", _config.TechnicalColor);
            var rb = collider.GetComponent<Rigidbody>();
            AddLine(lines, loc.Get("label_rigidbody"), rb != null ? loc.Get("label_yes") : loc.Get("label_no"), _config.TechnicalColor);

            AddSection(lines, loc.Get("section_bounds"), GetSectionColor("Bounds"));
            Bounds b = collider.bounds;
            AddLine(lines, loc.Get("label_center"), $"{b.center.x:F2}, {b.center.y:F2}, {b.center.z:F2}", _config.BasicColor);
            AddLine(lines, loc.Get("label_size"), $"{b.size.x:F2}, {b.size.y:F2}, {b.size.z:F2}", _config.BasicColor);

            return string.Join("\n", lines);
        }

        private string GetCachedDisplayText(BasePlayer player, Collider collider, BaseEntity entity)
        {
            if (entity != null)
            {
                return GetEntityInfo(entity, Vector3.Distance(player.eyes.position, entity.transform.position), 
                                    entity.transform.position, player);
            }
            else
            {
                var loc = GetLocalization(player);
                return CreateColoredLine(loc.Get("panel_target_static"), "#FF0000", _config.HeaderColor);
            }
        }

        Vector3 GetObjectPosition(GameObject obj)
        {
            if (obj == null) return Vector3.zero;
            BaseEntity entity = obj.GetComponent<BaseEntity>();
            if (entity != null) return entity.transform.position;
            Rigidbody rb = obj.GetComponent<Rigidbody>();
            if (rb != null) return rb.position;
            return obj.transform.position;
        }

        private string GetStaticPrefabPath(GameObject obj)
        {
            if (obj == null) return "";
            string name = obj.name.Replace("(Clone)", "").Trim();
            if (name.Contains("/") || name.Contains(".prefab"))
                return name;
            
            var resource = obj.GetComponent<ResourceEntity>();
            if (resource != null)
            {
                string prefabName = resource.PrefabName;
                if (!string.IsNullOrEmpty(prefabName) && (prefabName.Contains("/") || prefabName.Contains(".prefab")))
                    return prefabName;
            }
            
            Transform parent = obj.transform.parent;
            while (parent != null)
            {
                string parentName = parent.name.Replace("(Clone)", "").Trim();
                if (parentName.Contains("/") || parentName.Contains(".prefab"))
                    return parentName;
                parent = parent.parent;
            }
            
            string lowerName = name.ToLower();
            if (lowerName.Contains("tree") || lowerName.Contains("stump") || lowerName.Contains("log") ||
                lowerName.Contains("pine") || lowerName.Contains("oak") || lowerName.Contains("birch") ||
                lowerName.Contains("hemp") || lowerName.Contains("ore") || lowerName.Contains("stone") ||
                lowerName.Contains("rock") || lowerName.Contains("metal") || lowerName.Contains("sulfur") ||
                lowerName.Contains("hqm") || lowerName.Contains("barrel") || lowerName.Contains("crate") ||
                lowerName.Contains("loot") || lowerName.Contains("supply"))
            {
                return "assets/bundled/prefabs/autospawn/resource/" + name + ".prefab";
            }
            
            return name;
        }

        private bool IsResourceObject(GameObject obj)
        {
            if (obj == null) return false;
            
            var resource = obj.GetComponent<ResourceEntity>();
            if (resource != null) return true;
            
            string prefabPath = GetStaticPrefabPath(obj);
            if (string.IsNullOrEmpty(prefabPath)) return false;
            
            string lowerPath = prefabPath.ToLower();
            return lowerPath.Contains("/resource/") || lowerPath.Contains("/autospawn/resource/") ||
                   (lowerPath.Contains("/autospawn/") && (lowerPath.Contains("tree") || lowerPath.Contains("ore") ||
                    lowerPath.Contains("stone") || lowerPath.Contains("pine") || lowerPath.Contains("oak") ||
                    lowerPath.Contains("birch") || lowerPath.Contains("hemp") || lowerPath.Contains("barrel") ||
                    lowerPath.Contains("crate")));
        }
        #endregion

        #region Entity Information
        string GetSectionColor(string sectionName)
        {
            if (string.IsNullOrEmpty(sectionName)) return _config.HeaderColor;
            string k = sectionName.Trim().ToLower();
            if (k == "health") return "1 0 0 1";
            if (k == "inventory" || k == "loot") return "0.5 1 0.5 1";
            if (k == "transform" || k == "identity" || k == "static object") return _config.EntityColor;
            if (k == "components" || k == "door / lock") return _config.TechnicalColor;
            if (k == "bounds") return _config.BuildingColor;
            if (k == "owner" || k == "player" || k == "vehicle") return _config.PlayerColor;
            if (k == "building" || k == "turret") return _config.BuildingColor;
            return _config.HeaderColor;
        }
        void AddSection(List<string> lines, string title, string color)
        {
            string sectionColor = color ?? GetSectionColor(title);
            lines.Add($"<color={sectionColor}>——— {title} ———</color>");
        }
        void AddLine(List<string> lines, string label, object value, string valueColor)
        {
            lines.Add(ColorizeText(label, value?.ToString() ?? "—", valueColor));
        }
        string ColorizeText(string label, string value, string valueColor)
        {
            string labelColor = _config.BasicColor;
            return $"<color={labelColor}>{label}:</color> <color={valueColor}>{value}</color>";
        }

        const string AlertColor = "1 0 0 1";

        string GetEntityInfo(BaseEntity entity, float distance, Vector3 position, BasePlayer player)
        {
            var loc = GetLocalization(player);
            if (entity == null) return CreateColoredLine(loc.Get("target_unknown"), AlertColor, _conf
// ... [Code preview truncated due to length. Full source in attached file] ...

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