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

FREE MBKits : 20.0.2 20.0.2

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

namespace Oxide.Plugins
{
    [Info("MBKits", "pluginfuel.ru", "20.0.2")]
    [Description("MBKits")]
    public class MBKits : RustPlugin
    {
        [PluginReference] private Plugin MBCoins, Economics, ServerRewards;

        private Dictionary<string, KitData> kits = new();

        private Dictionary<string, string> kitContainerNames = new();

        private Dictionary<ulong, List<ChatErrorEntry>> playerErrors = new();

        private bool BuildModeDisableUIBackground = false;

        private Dictionary<ulong, Dictionary<string, double>> kitCooldowns = new();

        private Dictionary<ulong, Dictionary<string, int>> kitUses = new();

        private Dictionary<string, KitData> autoKits = new();

        private string kitsFile => $"MBSystem/MBKits/MBKits_kits";
        private string cooldownFile => $"MBSystem/MBKits/MBKits_cooldowns";
        private string usesFile => $"MBSystem/MBKits/MBKits_uses";
        private string autoKitsFile => $"MBSystem/MBKits/MBKits_autokits";

        #region Config

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("NPC Image URL")]
            public string NpcImageUrl = "https://i.ibb.co/r2Ft9sJQ/MBNPC.png";

            [JsonProperty("Scrollbar Handle Color")]
            public string ScrollbarHandleColor = "1 0 0 0.6";

        }

        protected override void LoadDefaultConfig()
        {
            config = new PluginConfig();
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<PluginConfig>();
                if (config == null) throw new Exception();
            }
            catch
            {
                PrintWarning("Config corrupted, generating new one");
                LoadDefaultConfig();
            }

            SaveConfig();
        }

        protected override void SaveConfig() => Config.WriteObject(config, true);

        #endregion

        #region Data
        private class KitData
        {
            public string Name;
            public float Price = 0f;
            public int EconomyType = 0;
            public string CurrencyName = "";
            public string ImageUrl = "https://i.postimg.cc/tJrNqhNX/42808.png";  // URL картинки набора
            public string Permission = "";
            public string CooldownText = "1h";
            public bool HideIfNoPermission = false;
            public int MaxUses = 0;
            public List<ItemData> Items = new();

        }

        private class ItemData
        {
            public string ShortName;
            public int Amount;
            public ulong SkinID = 0;

            public int AmmoAmount;
            public string AmmoShortName;

            public string Container;
            public int Slot;

            public List<ItemData> Contents = new List<ItemData>();

            [JsonProperty("Command (Player identifier {playerid})")]
            public string CustomCommand;
            public string CustomImage;
        }

        private class ChatErrorEntry
        {
            public string Text;
            public string ImageKey;
            public DateTime Timestamp;
        }

        private double GetCooldownSeconds(string text)
        {
            if (TryParseTime(text.ToLower(), out var ts))
                return ts.TotalSeconds;

            return 0;
        }

        [HookMethod("API_GetKitItems")]
        public List<object> API_GetKitItems(string kitName)
        {
            if (string.IsNullOrEmpty(kitName)) return null;
            kitName = kitName.ToLower();

            if (!kits.TryGetValue(kitName, out var kit))
                return null;

            var result = new List<object>();
            foreach (var item in kit.Items)
            {
                result.Add(new Dictionary<string, object>
                {
                    ["ShortName"] = item.ShortName,
                    ["Amount"] = item.Amount
                });
            }
            return result;
        }

        private static bool TryParseTime(string input, out TimeSpan result)
        {
            int d = 0, h = 0, m = 0, s = 0;
            Match matchD = Regex.Match(input, @"(\d+?)d");
            Match matchH = Regex.Match(input, @"(\d+?)h");
            Match matchM = Regex.Match(input, @"(\d+?)m");
            Match matchS = Regex.Match(input, @"(\d+?)s");

            if (matchD.Success) d = int.Parse(matchD.Groups[1].Value);
            if (matchH.Success) h = int.Parse(matchH.Groups[1].Value);
            if (matchM.Success) m = int.Parse(matchM.Groups[1].Value);
            if (matchS.Success) s = int.Parse(matchS.Groups[1].Value);

            if (!matchD.Success && !matchH.Success && !matchM.Success && !matchS.Success)
            {
                result = TimeSpan.Zero;
                return false;
            }

            result = new TimeSpan(d, h, m, s);
            return true;
        }

        private void LoadKits()
        {
            kits = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<string, KitData>>(kitsFile) ?? new();
        }

        private void SaveKits()
        {
            Interface.Oxide.DataFileSystem.WriteObject(kitsFile, kits);
        }

        private void LoadCooldowns()
        {
            kitCooldowns = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<ulong, Dictionary<string, double>>>(cooldownFile) ?? new();
        }

        private void SaveCooldowns()
        {
            Interface.Oxide.DataFileSystem.WriteObject(cooldownFile, kitCooldowns);
        }

        private void LoadUses()
        {
            kitUses = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<ulong, Dictionary<string, int>>>(usesFile) ?? new();
        }

        private void SaveUses()
        {
            Interface.Oxide.DataFileSystem.WriteObject(usesFile, kitUses);
        }

        private void SaveAutoKits()
        {
            Interface.Oxide.DataFileSystem.WriteObject(autoKitsFile, autoKits);
        }

        private void LoadAutoKits()
        {
            try
            {
                autoKits = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<string, KitData>>(autoKitsFile);

                foreach (var kit in autoKits.Values)
                {
                    if (!string.IsNullOrEmpty(kit.Permission))
                    {
                        if (!permission.PermissionExists(kit.Permission))
                            permission.RegisterPermission(kit.Permission, this);
                    }
                }
            }
            catch
            {
                autoKits = new Dictionary<string, KitData>();
            }
        }

        #endregion

        void Init()
        {
            LoadKits();
            SaveKits();
            LoadCooldowns();
            LoadUses(); 
            LoadAutoKits();
            permission.RegisterPermission("mbkits.admin", this);
        }

        private void OnServerInitialized()
        {
            RegisterConfigImages();

            LocalImageLoader.LoadAll(this);

            foreach (var kit in kits.Values)
            {
                if (!string.IsNullOrEmpty(kit.Permission) &&
                    !permission.PermissionExists(kit.Permission))
                {
                    permission.RegisterPermission(kit.Permission, this);
                }
            }

            PrintWarning("[MBKits] Images loading started...");
        }

        // === КОМАНДА ДЛЯ ПЕРЕЗАГРУЗКИ КАРТИНОК ===
        // Используйте: mbkits.reload в консоли для перезагрузки всех картинок
        [ConsoleCommand("mbkits.reload")]
        private void ReloadImagesCommand(ConsoleSystem.Arg arg)
        {
            if (arg.Player() != null && !arg.Player().IsAdmin)
            {
                arg.ReplyWith("You don't have permission to use this command");
                return;
            }

            PrintWarning("[MBKits] Reloading images...");
            RegisterConfigImages();
            LocalImageLoader.LoadAll(this);
            PrintWarning("[MBKits] Images reloaded!");
        }


        private object SetBuildModeFromHub(bool state)
        {
            BuildModeDisableUIBackground = state;
            return null;
        }

        void Unload()
        {
            foreach (var player in BasePlayer.activePlayerList)
            {
                CuiHelper.DestroyUi(player, "MBKits_UI");
                CuiHelper.DestroyUi(player, "MBKits_Inv_UI");
            }

        }

        private void OnPlayerConnected(BasePlayer player) 
        {

            ShowIntroImage(player);
        }

        private void OnNewSave()
        {
            kitUses.Clear();
            SaveUses();

            kitCooldowns.Clear();
            SaveCooldowns();

            Puts("[MBKits] Kit uses and cooldowns reset due to wipe.");
        }

        private void OnPlayerRespawned(BasePlayer player)
        {
            foreach (var kit in autoKits.Values)
                GiveAutoRespawnKit(player, kit);
        }

        #region LocalImageLoader

        private string GetSafeImageKey(string url)
        {
            try
            {
                string file = Path.GetFileNameWithoutExtension(url);
                return string.IsNullOrEmpty(file) ? Guid.NewGuid().ToString() : file.ToLower();
            }
            catch
            {
                return Guid.NewGuid().ToString();
            }
        }

        private Dictionary<string, string> ImageURLs = new Dictionary<string, string>
        {
            { "MBKits", "https://i.ibb.co/yF3npmQx/MBKits-Os.png" },
            { "MBKits_inv", "https://i.ibb.co/b5JTZVVF/MBKits-Inv-Os.png" },
            { "MBKitsBG_Default", "https://i.ibb.co/HpX7V5Kh/MBKits-BG-Default.png" },
            { "error_icon_invalid_kits", "https://i.ibb.co/sTvDbG6/error-icon-invalid.png" },
            { "success_icon_kits", "https://i.ibb.co/4ZMkCDbS/success-icon.png" }
        };

        private void RegisterConfigImages()
        {
            void Add(string key, string url)
            {
                if (!string.IsNullOrEmpty(url))
                {
                    ImageURLs[key] = url;
                }
            }

            Add("MB_NPC", config.NpcImageUrl);

            // === ЗАГРУЗКА КАРТИНОК ИЗ ПАПКИ ДЛЯ НАБОРОВ ===
            // Картинки берутся из: oxide/data/MBSystem/MBKits/Images/название_кита.png
            foreach (var kit in kits.Values)
            {
                string kitName = kit.Name.ToLower();
                string localImagePath = $"oxide/data/MBSystem/MBKits/Images/{kitName}.png";
                
                // Если картинка есть в локальной папке, используем её
                if (File.Exists(localImagePath))
                {
                    ImageURLs[kitName] = localImagePath;  // Без префикса kit_
                }
                else
                {
                    // Если картинка не найдена, выводим сообщение
                    PrintWarning($"[MBKits] ВНИМАНИЕ: Картинка для кита '{kit.Name}' не найдена!");
                    PrintWarning($"[MBKits] Добавьте файл: oxide/data/MBSystem/MBKits/Images/{kitName}.png");
                }

                foreach (var item in kit.Items)
                {
                    if (!string.IsNullOrEmpty(item.CustomImage))
                    {
                        string key = GetSafeImageKey(item.CustomImage);
                        Add($"item_{key}", item.CustomImage);
                    }
                }
            }
        }

        private class LocalImageLoader
        {
            private const string CacheFolder = "MBSystem/MBKits/Images";
            private const string CacheMetaFile = "MBSystem/MBKits/ImageCache";

            private static Dictionary<string, string> UrlCache = new();
            public static Dictionary<string, string> Images = new();

            public static void LoadAll(MBKits plugin)
            {
                UrlCache = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<string, string>>(CacheMetaFile)
                    ?? new Dictionary<string, string>();

                Directory.CreateDirectory($"oxide/data/{CacheFolder}");

                // Сначала загружаем локальные изображения синхронно
                LoadLocalImagesSync(plugin);

                // Потом загружаем остальные асинхронно
                ServerMgr.Instance.StartCoroutine(Load(plugin));
            }

            private static void LoadLocalImagesSync(MBKits plugin)
            {
                foreach (var entry in plugin.ImageURLs)
                {
                    string key = entry.Key;
                    string url = entry.Value;
                    string filePath = $"oxide/data/{CacheFolder}/{key}.png";

                    // Если URL содержит путь к файлу (начинается с oxide/data), загружаем синхронно
                    if (url.StartsWith("oxide/data"))
                    {
                        if (File.Exists(url))
                        {
                            byte[] png = File.ReadAllBytes(url);

                            string id = FileStorage.server.Store(
                                png,
                                FileStorage.Type.png,
                                CommunityEntity.ServerInstance.net.ID
                            ).ToString();

                            Images[key] = id;
                        }
                        else
                        {
                            plugin.PrintError($"[MBKits] Local image not found: {url}");
                        }
                    }
                    // Проверяем есть ли картинка в локальной папке кэша
                    else if (File.Exists(filePath))
                    {
                        byte[] png = File.ReadAllBytes(filePath);

                        string id = FileStorage.server.Store(
                            png,
                            FileStorage.Type.png,
                            CommunityEntity.ServerInstance.net.ID
                        ).ToString();

                        Images[key] = id;
                    }
                }
            }

            private static IEnumerator Load(MBKits plugin)
            {
                foreach (var entry in plugin.ImageURLs)
                {
                    string key = entry.Key;
                    string url = entry.Value;
                    string filePath = $"oxide/data/{CacheFolder}/{key}.png";

                    // Пропускаем, если уже загружено синхронно
                    if (Images.ContainsKey(key))
                    {
                        continue;
                    }

                    // Если нет локальной картинки, пытаемся скачать с сайта
                    bool needsDownload = true;

                    if (UrlCache.TryGetValue(key, out string oldUrl) &&
                        oldUrl == url &&
                        File.Exists(filePath))
                        needsDownload = false;

                    if (needsDownload)
                    {
                        UnityWebRequest www = UnityWebRequestTexture.GetTexture(url);
                        www.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
                        yield return www.SendWebRequest();

                        if (www.isNetworkError || www.isHttpError)
                        {
                            plugin.PrintError($"[MBKits] Failed to download image: {key} | Error: {www.error}");
                            continue;
                        }

                        Texture2D tex = DownloadHandlerTexture.GetContent(www);
                        if (tex != null)
                        {
                            File.WriteAllBytes(filePath, tex.EncodeToPNG());
                            UrlCache[key] = url;
                            UnityEngine.Object.DestroyImmediate(tex);
                        }
                        else
                        {
                            plugin.PrintError($"[MBKits] Failed to get texture for: {key}");
                            continue;
                        }
                    }

                    byte[] pngData = File.ReadAllBytes(filePath);

                    string imageId = FileStorage.server.Store(
                        pngData,
                        FileStorage.Type.png,
                        CommunityEntity.ServerInstance.net.ID
                    ).ToString();

                    Images[key] = imageId;
                }

                Interface.Oxide.DataFileSystem.WriteObject(CacheMetaFile, UrlCache);

                CleanupUnusedImages(plugin);
            }

            private static void CleanupUnusedImages(MBKits plugin)
            {
                string folder = $"oxide/data/{CacheFolder}";

                if (!Directory.Exists(folder))
                    return;

                var files = Directory.GetFiles(folder, "*.png");

                foreach (string file in files)
                {
                    string filename = Path.GetFileNameWithoutExtension(file);

                    if (!plugin.ImageURLs.ContainsKey(filename))
                    {
                        try
                        {
                            File.Delete(file);
                            plugin.Puts($"[MBKits] Removed unused image: {filename}.png");

                            if (UrlCache.ContainsKey(filename))
                                UrlCache.Remove(filename);
                        }
                        catch (Exception ex)
                        {
                            plugin.PrintError($"[MBKits] Cleanup error {filename}: {ex.Message}");
                        }
                    }
                }

                Interface.Oxide.DataFileSystem.WriteObject(CacheMetaFile, UrlCache);
            }

            public static string Get(string name)
            {
                return Images.TryGetValue(name, out var id) ? id : null;
            }
        }

        #endregion

        #region Preloading images to the player before connecting

        private void ShowIntroImage(BasePlayer player)
        {
            CuiHelper.DestroyUi(player, "MBKits_IntroUI");

            var container = new CuiElementContainer();

            string[] imageKeys = new[]
            {
                "MBKits",
                "MBKits_inv",
                "MBKitsBG_Default",
                "error_icon_invalid_kits",
                "success_icon_kits",
            };

            float startY = 0f;
            float height = 0.2f;

            for (int i = 0; i < imageKeys.Length; i++)
            {
                string key = imageKeys[i];
                string imageId = LocalImageLoader.Get(key);

                if (string.IsNullOrEmpty(imageId))
                {
                    PrintWarning($"[MBKits] Image '{key}' not loaded");
                    continue;
                }

                container.Add(new CuiElement
                {
                    Name = $"MBKits_IntroUI_{i}",
                    Parent = ".Mains",
                    Components =
                    {
                        new CuiRawImageComponent
                        {
                            Png = imageId,
                            Color = "1 1 1 1"
                        },
                        new CuiRectTransformComponent
                        {
                            AnchorMin = $"-1 {startY + (i * height)}",
                            AnchorMax = $"-0.7 {startY + ((i + 1) * height)}"
                        }
                    }
                });
            }

            CuiHelper.AddUi(player, container);

            timer.Once(5f, () =>
            {
                for (int i = 0; i < imageKeys.Length; i++)
                    CuiHelper.DestroyUi(player, $"MBKits_IntroUI_{i}");
            });
        }
        #endregion

        #region MBKitsUI

        [HookMethod("OpenMBKitsForPlayer")]
        private void ShowKitsUI(BasePlayer player)
        {
            CuiHelper.DestroyUi(player, "MBKits_UI");

            var container = new CuiElementContainer();

            string root = container.Add(new CuiPanel
            {
                Image = { Color = "0 0 0 0" },
                RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "-228 -124", OffsetMax = "216 120" },
                CursorEnabled = true
            }, ".Mains", "MBKits_UI");

            /*if (!BuildModeDisableUIBackground)
            {
                container.Add(new CuiPanel
                {
                    Image = { Color = "0 0 0 0.0", Material = "assets/content/ui/uibackgroundblur.mat" },
                    RectTransform = { AnchorMin = "-1 -1", AnchorMax = "2 2" },
                    CursorEnabled = true
                }, root);
            }*/

            string imageId = LocalImageLoader.Get("MBKits");
            if (!string.IsNullOrEmpty(imageId))
            {
                container.Add(new CuiElement
                {
                    Name = CuiHelper.GetGuid(),
                    Parent = root,
                    Components =
                    {
                        new CuiRawImageComponent { Png = imageId },
                        new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "0 0", OffsetMax = "0 0" }
                    }
                });
            }

            string npcImageId = LocalImageLoader.Get("");
            if (!string.IsNullOrEmpty(npcImageId))
            {
                container.Add(new CuiElement
                {
                    Parent = root,
                    Components =
                    {
                        new CuiRawImageComponent
                        {
                            Png = npcImageId
                        },
                        new CuiRectTransformComponent
                        {
                            AnchorMin = "-0.03 0.004", AnchorMax = "0.314 1"
                        }
                    }
                });
            }

            if (!BuildModeDisableUIBackground)
            {
                container.Add(new CuiButton
                {
                    Button = { Color = "0 0 0 0", Close = "Menu_UI" },
                    RectTransform = { AnchorMin = "0.8 0.804", AnchorMax = "0.817 0.832" },
                    Text = { Text = "", FontSize = 0 }
                }, root);
            }

           string centerPanel = container.Add(new CuiPanel
            {
                Image = { Color = "0.1 0.1 0.1 0" },
                RectTransform = { AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-318 -165", OffsetMax = "390 165" },
                CursorEnabled = true
            }, root);

            container.Add(new CuiLabel
            {
                RectTransform = { AnchorMin = "0.441 0.86", AnchorMax = "0.75 0.955" },
                Text = { Text = Msg("MB_Kits_Player", player.userID.ToString()), Color = "1 1 1 0.0", Align = TextAnchor.MiddleLeft, FontSize = 30 }
            }, centerPanel);

            container.Add(new CuiLabel
            {
                RectTransform = { AnchorMin = "0.341 0.8", AnchorMax = "0.9 0.868" },
                Text = { Text = Msg("", player.userID.ToString()), Color = "1 1 1 0.0", Align = TextAnchor.MiddleLeft, FontSize = 21, Font = "permanentmarker.ttf" }
            }, centerPanel);

            container.Add(new CuiLabel
            {
                Text = { Text = Msg("MB_Close_Kits", player.userID.ToString()), FontSize = 13, Color = "1 1 1 0.4", Align = TextAnchor.MiddleCenter },
                RectTransform = { AnchorMin = "0.2 -0.06", AnchorMax = "0.8 -0.02" }
            }, centerPanel);

            // === РАЗМЕР И РАССТОЯНИЕ МЕЖДУ НАБОРАМИ ===
            float blockHeight = 120f;      // Высота одного набора (уменьшено с 152 на 120)
            float blockMargin = 8f;        // Расстояние между наборами (в пикселях)
            float verticalShift = 3f;      // Вертикальный сдвиг (обычно не меняется)
            
            // ИНСТРУКЦИЯ ПО ИЗМЕНЕНИЮ:
            // - Уменьшите blockHeight для более компактного вида (например: 100, 110, 120)
            // - Увеличьте blockHeight для более крупных наборов (например: 150, 160, 180)
            // - Измените blockMargin для изменения расстояния между наборами

            var kitList = kits.Values .Where(k => string.IsNullOrEmpty(k.Permission) || !k.HideIfNoPermission || permission.UserHasPermission(player.UserIDString, k.Permission)) .ToList();
            int total = kitList.Count;
            float totalWidth = total * (blockHeight + blockMargin);
            float minVisibleWidth = 500f;
            float scrollContentWidth = Mathf.Max(totalWidth, minVisibleWidth);

            container.Add(new CuiElement
            {
                Name = "MBMenuScrollableKits",
                Parent = centerPanel,
                Components =
                {
                    new CuiScrollViewComponent
                    {
                        MovementType = ScrollRect.MovementType.Elastic,
                        Vertical = false,
                        Horizontal = true,
                        Inertia = true,
                        Elasticity = 0.10f,           // Увеличено для более плавной прокрутки по 5
                        DecelerationRate = 0.5f,     // Увеличено для замедления прокрутки
                        ScrollSensitivity = 15f,      // Уменьшено с 30 на 5 для прокрутки по 5 наборов
                        // === НАСТРОЙКА ПРОКРУТКИ ===
                        // ScrollSensitivity - чувствительность прокрутки (меньше = медленнее)
                        // Elasticity - упругость (больше = более плавно)
                        // DecelerationRate - замедление (больше = медленнее останавливается)
                        ContentTransform = new CuiRectTransform
                        {
                            AnchorMin = "0 0",
                            AnchorMax = "0 1",
                            OffsetMin = $"0 0",
                            OffsetMax = $"{scrollContentWidth} 0"
                        },
                        VerticalScrollbar = new CuiScrollbar
                        {
                            Size = 5f,
                            AutoHide = false,
                            HandleColor = config.ScrollbarHandleColor,
                            TrackColor = "0.1 0.1 0.1 0"
                        },
                        // === ГОРИЗОНТАЛЬНЫЙ СКРОЛЛБАР ===
                        // Закомментируйте эти строки чтобы скрыть бегунок прокрутки
                        /*
                        HorizontalScrollbar = new CuiScrollbar
                        {
                            Size = 5f,
                            AutoHide = false,
                            HandleColor = config.ScrollbarHandleColor,
                            TrackColor = "0.1 0.1 0.1 0"
                        }
                        */
                    },
                    new CuiRectTransformComponent
                    {
                        // === ПОЗИЦИЯ И РАЗМЕР НАБОРОВ ===
                        // AnchorMin = "-0.05 0.15" - левый край (-0.05) и нижний край (0.15)
                        // AnchorMax = "0.961 0.85" - правый край (0.961) и верхний край (0.85)
                        // 
                        // ДЛЯ ИЗМЕНЕНИЯ ШИРИНЫ: меняйте -0.05 (влево) и 0.961 (вправо)
                        // ДЛЯ ИЗМЕНЕНИЯ ВЫСОТЫ: меняйте 0.15 (вниз) и 0.85 (вверх)
                        // Примеры:
                        // - Выше: измените 0.15 на 0.05 и 0.85 на 0.95
                        // - Ниже: измените 0.15 на 0.25 и 0.85 на 0.75
                        AnchorMin = "-0.05 0.15",
                        AnchorMax = "0.961 0.75"
                    },
                    new CuiNeedsCursorComponent()
                }
            });

            container.Add(new CuiPanel
            {
                Image = { Color = "0 0 0 0" },
                RectTransform =
                {
                    AnchorMin = "0 0",
                    AnchorMax = "0 1",
                    OffsetMin = $"0 0",
                    OffsetMax = $"{scrollContentWidth} 0"
                }
            }, "MBMenuScrollableKits", "MBMenuKitsContent");

            for (int i = 0; i < total; i++)
            {
                var kit = kitList[i];

                float leftOffset = i * (blockHeight + blockMargin);
                float rightOffset = leftOffset + blockHeight;

                string bgImg = LocalImageLoader.Get("MBKitsBG_Default");
                string containerName = $"KitContainer_{i}";
                kitContainerNames[kit.Name.ToLower()] = containerName;


                container.Add(new CuiPanel
                {
                    Image = { Color = "0 0 0 0" },
                    RectTransform = { AnchorMin = "0 0", AnchorMax = "0 1", OffsetMin = $"{leftOffset} 0", OffsetMax = $"{rightOffset} 0" }
                }, "MBMenuKitsContent", containerName);

                if (!string.IsNullOrEmpty(bgImg))
                {
                    container.Add(new CuiElement
                    {
                        Name = $"KitBG_{i}",
                        Parent = containerName,
                        Components =
                        {
                            new CuiRawImageComponent
                            {
                                Png = bgImg,
                                Sprite = "assets/content/textures/generic/fulltransparent.tga"
                            },
                            new CuiRectTransformComponent
                            {
                                AnchorMin = "0 0",
                                AnchorMax = "1 1"
                            }
                        }
                    });
                }

                string kitImg = LocalImageLoader.Get(kit.Name.ToLower());  // Без префикса kit_

                if (!string.IsNullOrEmpty(kitImg))
                {
                    container.Add(new CuiElement
                    {
                        Parent = containerName,
                        Components =
                        {
                            new CuiRawImageComponent
                            {
                                Png = kitImg,
                                Sprite = "assets/content/textures/generic/fulltransparent.tga"
                            },
                            new CuiRectTransformComponent
                            {
                                AnchorMin = "0.18 0.22",
                                AnchorMax = "0.75 0.85"
                            }
                        }
                    });
                }

                container.Add(new CuiLabel
                {
                    Text = { Text = kit.Name.ToUpper(), FontSize = 17, Align = TextAnchor.MiddleCenter, Color = "1 1 1 0.9" },
                    RectTransform = { AnchorMin = "0.05 0.85", AnchorMax = "0.95 0.98" }
                }, containerName);

                bool hasPermission = string.IsNullOrEmpty(kit.Permission) || permission.UserHasPermission(player.UserIDString, kit.Permission);

                double cooldownUntil = 0;
                bool isOnCooldown = kitCooldowns.TryGetValue(player.userID, out var cooldowns) &&
                    cooldowns.TryGetValue(kit.Name.ToLower(), out cooldownUntil) &&
                    cooldownUntil > Facepunch.Math.Epoch.Current;


                if (!hasPermission)
                {
                    container.Add(new CuiButton
                    {
                        Button = { Color = "0.3 0.3 0.3 0" },
                        RectTransform = { AnchorMin = "0.05 0.04", AnchorMax = "0.72 0.18" },
                        Text = { Text = Msg("MB_NoAccess", player.UserIDString), FontSize = 14, Align = TextAnchor.MiddleCenter, Color = "0.8 0.8 0.8 1" }
                    }, containerName);
                }
                
                else if (isOnCooldown)
                {
                    double timeLeft = cooldownUntil - Facepunch.Math.Epoch.Current;
                    if (timeLeft < 0) timeLeft = 0;
                    TimeSpan ts = TimeSpan.FromSeconds(timeLeft);
                    string cooldownText = string.Format("{0:D2}h {1:D2}m", ts.Hours, ts.Minutes);

                    container.Add(new CuiButton
                    {
                        Button = { Color = "0.8 0.6 0.1 0" },
                        RectTransform = { AnchorMin = "0.05 0.04", AnchorMax = "0.72 0.18" },
                        Text = { Text = cooldownText, FontSize = 14, Align = TextAnchor.MiddleCenter, Color = "1 0.4 0.4 1" }
                    }, containerName);
                }
                else
                {
                    bool isBuyKit =
                        kit.EconomyType != 0 &&
                        kit.Price > 0f &&
                        !string.IsNullOrEmpty(kit.CurrencyName);

                    string cmd = isBuyKit
                        ? $"mbkits.buykit {kit.Name}"
                        : $"mbkits.givekit {kit.Name}";

                    string text;

                    if (isBuyKit)
                    {
                        text =
                            $"{Msg("MB_Buy", player.UserIDString)}  " +
                            $"<color=#ffd479>{kit.Price}</color> " +
                            $"<color=#cfcfcf>{kit.CurrencyName}</color>";
                    }
                    else
                    {
                        text = Msg("MB_Claim", player.UserIDString);
                    }

                    container.Add(new CuiButton
                    {
                        Button =
                        {
                            Color = isBuyKit ? "0.25 0.6 0.25 0" : "0.2 0.6 0.2 0",
                            Command = cmd
                        },
                        RectTransform =
                        {
                            AnchorMin = "0.05 0.04",
                            AnchorMax = "0.72 0.18"
                        },
                        Text =
                        {
                            Text = text,
                            FontSize = 14,
                            Align = TextAnchor.MiddleCenter,
                            Color = "1 1 1 1"
                        }
                    }, containerName, $"KitButton_{kit.Name}");


                }

                container.Add(new CuiButton
                {
                    Button = { Color = "0.2 0.2 0.8 0", Command = $"mbkits.viewkit {kit.Name}" },
                    RectTransform = { AnchorMin = "0.75 0.04", AnchorMax = "0.948 0.18" },
                }, containerName);

            }

            CuiHelper.AddUi(player, container);
        }
        #endregion

        #region MBKitsInvUI

        private void ShowKitsInvUI(BasePlayer player, KitData kit)
        {
            CuiHelper.DestroyUi(player, "MBKits_Inv_UI");
            CuiHelper.DestroyUi(player, "MBKits_UI");

            var container = new CuiElementContainer();

            string root = container.Add(new CuiPanel
            {
                Image = { Color = "0 0 0 0" },
                RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "-228 -124", OffsetMax = "216 120" },
                CursorEnabled = true
            }, ".Mains", "MBKits_Inv_UI");

            if (!BuildModeDisableUIBackground)
            {
                container.Add(new CuiPanel
                {
                    Image = { Color = "0 0 0 0.0", Material = "assets/content/ui/uibackgroundblur.mat" },
                    RectTransform = { AnchorMin = "-1 -1", AnchorMax = "2 2" },
                    CursorEnabled = true
                }, root);
            }

            string imageId = LocalImageLoader.Get("MBKits_inv");
            if (!string.IsNullOrEmpty(imageId))
            {
                container.Add(new CuiElement
                {
                    Name = CuiHelper.GetGuid(),
                    Parent = root,
                    Components =
                    {
                        new CuiRawImageComponent { Png = imageId },
                        new CuiRectTransformComponent
                        { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "0 0", OffsetMax = "0 0" }
                    }
                });
            }

            string npcImageId = LocalImageLoader.Get("");
            if (!string.IsNullOrEmpty(npcImageId))
            {
                container.Add(new CuiElement
                {
                    Parent = root,
                    Components =
                    {
                        new CuiRawImageComponent
                        {
                            Png = npcImageId
                        },
                        new CuiRectTransformComponent
                        {
                            AnchorMin = "-0.03 0.004", AnchorMax = "0.314 1"
                        }
                    }
                });
            }

            if (!BuildModeDisableUIBackground)
            {
                container.Add(new CuiButton
                {
                    Button = { Color = "0 0 0 0", Close = "Menu_UI" },
                    RectTransform = { AnchorMin = "0.8 0.804", AnchorMax = "0.817 0.832" },
                    Text = { Text = "", FontSize = 0 }
                }, root);
            }

            string centerPanel = container.Add(new CuiPanel
            {
                Image = { Color = "0.1 0.1 0.1 0" },
                RectTransform = { AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-418 -165", OffsetMax = "240 165" },
                CursorEnabled = true
            }, root);

            container.Add(new CuiLabel
            {
                Text = { Text = Msg("MB_Close_Kits", player.userID.ToString()), FontSize = 13, Color = "1 1 1 0.4", Align = TextAnchor.MiddleCenter },
                RectTransform = { AnchorMin = "0.2 -0.06", AnchorMax = "0.8 -0.02" }
            }, centerPanel);

            container.Add(new CuiButton
            {
                Button = { Command = "mbkits_inv.back", Color = "0.2 0.5 0.8 0" },
                RectTransform = { AnchorMin = "0.865 0.04", AnchorMax = "0.947 0.12" },
                Text = { Text = Msg("MB_Back", player.UserIDString), FontSize = 15, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1" }
            }, centerPanel);

            container.Add(new CuiLabel
            {
                Text = { Text = $"{kit.Name.ToUpper()}", FontSize = 15, Align = TextAnchor.MiddleCenter, Color = "1 1 1 0.9" },
                RectTransform = { AnchorMin = "0.411 0.8411", AnchorMax = "0.5345 0.897" }
            }, centerPanel);

            string textPrefix = Msg("CooldownPrefix", player.UserIDString);
            string textPrefix_2 = Msg("CooldownPrefix_2", player.UserIDString);
            string textH = Msg("TimeHours", player.UserIDString);
            string textM = Msg("TimeMinutes", player.UserIDString);
            string textS = Msg("TimeSeconds", player.UserIDString);

            string FormatCooldown(TimeSpan time)
            {
                return $"{(int)time.TotalHours}{textH} {(int)time.Minutes}{textM} {(int)time.Seconds}{textS}";
            }

            double cooldownUntil = 0;
            bool isOnCooldown = kitCooldowns.TryGetValue(player.userID, out var cooldowns) &&
                cooldowns.TryGetValue(kit.Name.ToLower(), out cooldownUntil) &&
                cooldownUntil > Facepunch.Math.Epoch.Current;

            string cooldownText;

            if (isOnCooldown)
            {
                double remaining = cooldownUntil - Facepunch.Math.Epoch.Current;

                if (remaining < 0) remaining = 0;
                TimeSpan remainingTime = TimeSpan.FromSeconds(remaining);
                cooldownText = $"{textPrefix}: {FormatCooldown(remainingTime)}";
            }
            else
            {
                TimeSpan fullTime = TimeSpan.FromSeconds(GetCooldownSeconds(kit.CooldownText));
                cooldownText = $"{textPrefix_2}: {FormatCooldown(fullTime)}";

            }

            container.Add(new CuiLabel
            {
                Text = { Text = cooldownText, FontSize = 13, Align = TextAnchor.MiddleLeft, Color = isOnCooldown ? "1 0.4 0.4 1" : "1 1 1 0.7" },
                RectTransform = { AnchorMin = "0.547 0.8411", AnchorMax = "0.792 0.897" }
            }, centerPanel);

            var main = kit.Items.Where(i => i.Container == "main").ToList();
            for (int i = 0; i < 24; i++)
            {
                int row = i / 6;
                int col = i % 6;

                float xMin = 0.412f + col * 0.0645f;
                float yMax = 0.827f - row * 0.1075f;
                float xMax = xMin + 0.0585f;
                float yMin = yMax - 0.0955f;

                AddItemSlot(container, centerPanel, $"Main_{i}", xMin, yMin, xMax, yMax, i < main.Count ? main[i] : null);
            }

            var wear = kit.Items.Where(i => i.Container == "wear").ToList();
            for (int i = 0; i < 7; i++)
            {
                float xMin = 0.38f + i * 0.0645f;
                float yMin = 0.298f;
                float xMax = xMin + 0.0585f;
                float yMax = yMin + 0.0955f;

                AddItemSlot(container, centerPanel, $"Wear_{i}", xMin, yMin, xMax, yMax, i < wear.Count ? wear[i] : null);
            }

            var belt = kit.Items.Where(i => i.Container == "belt").ToList();
            for (int i = 0; i < 6; i++)
            {
                float xMin = 0.412f + i * 0.0645f;
                float yMin = 0.187f;
                float xMax = xMin + 0.0585f;
                float yMax = yMin + 0.0955f;

                AddItemSlot(container, centerPanel, $"Belt_{i}", xMin, yMin, xMax, yMax, i < belt.Count ? belt[i] : null);
            }

            CuiHelper.AddUi(player, container);
        }

        #endregion

        #region ErrorMessage

        private void AddErrorMessage(BasePlayer player, string text, string imageKey)
        {
            ulong id = player.userID;

            if (!playerErrors.ContainsKey(id))
                playerErrors[id] = new List<ChatErrorEntry>();

            var entry = new ChatErrorEntry
            {
                Text = text,
                ImageKey = imageKey,
                Timestamp = DateTime.UtcNow
            };

            playerErrors[id].Add(entry);

            if (playerErrors[id].Count > 5)
                playerErrors[id].RemoveAt(0);

            RedrawErrorUI(player);

            timer.Once(3f, () =>
            {
                if (playerErrors.TryGetValue(id, out var list) && list.Contains(entry))
                {
                    list.Remove(entry);
                    RedrawErrorUI(player);
                }
            });
        }

        private void RedrawErrorUI(BasePlayer player)
        {
            CuiHelper.DestroyUi(player, "MB_ErrorChatUI");

            if (!playerErrors.TryGetValue(player.userID, out var messages) || messages.Count == 0)
                return;

            var container = new CuiElementContainer();

            string parent = container.Add(new CuiPanel
            {
                Image = { Color = "0 0 0 0" },
                RectTransform = { AnchorMin = "0 0", AnchorMax = "0 0", OffsetMin = "5 96", OffsetMax = "200 500" }
            }, "OverlayNonScaled", "MB_ErrorChatUI");

            float blockHeight = 50f;
            float spacing = 5f;
            float yStart = 0f;

            for (int i = 0; i < messages.Count; i++)
            {
                var error = messages[messages.Count - 1 - i];
                float offsetY = yStart + i * (blockHeight + spacing);

                string panel = container.Add(new CuiPanel
                {
                    Image = { Color = "0 0 0 0" },
                    RectTransform = { AnchorMin = "0 0", AnchorMax = "1 0", OffsetMin = $"0 {offsetY}", OffsetMax = $"0 {offsetY + blockHeight}" }
                }, parent);

                container.Add(new CuiElement
                {
                    Parent = panel,
                    Components =
                    {
                        new CuiRawImageComponent
                        {
                            Png = LocalImageLoader.Get(error.ImageKey),
                            Color = "1 1 1 1"
                        },
                        new CuiRectTransformComponent
                        { AnchorMin = "0 0", AnchorMax = "1 1", }
                    }
                });

                string titleKey = error.ImageKey switch
                {
                    "success_icon_kits" => "SuccessTitle",
                    "error_icon_invalid_kits" => "ErrorTitle",
                    "error_icon_expired" => "ErrorTitle",
                    _ => "ErrorTitle"
                };

                string title = Msg(titleKey, player.UserIDString);

                container.Add(new CuiLabel
                {
                    RectTransform = { AnchorMin = "0.31 0.56", AnchorMax = "1 0.9" },
                    Text = { Text = title, Align = TextAnchor.MiddleLeft, FontSize = 12, Color = "1 1 1 0.9" }
                }, panel);

                container.Add(new CuiLabel
                {
                    RectTransform = { AnchorMin = "0.31 0", AnchorMax = "0.98 0.54" },
                    Text = { Text = error.Text, Align = TextAnchor.UpperLeft, FontSize = 9, Color = "1 1 1 0.8" }
                }, panel);
            }

            CuiHelper.AddUi(player, container);
        }

        #endregion

        #region Command

        [ConsoleCommand("mbkits.viewkit")]
        private void CmdViewKit(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            if (player == null || !arg.HasArgs()) return;

            string kitName = arg.Arg
// ... [Code preview truncated due to length. Full source in attached file] ...

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