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

FREE RustRoyale : 1.3.6 1.3.6

Нет прав для скачивания
C#:
using System;
using System.IO;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Collections.Concurrent;
using Facepunch;
using Oxide.Core;
using Oxide.Core.Libraries;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using UnityEngine;
using Newtonsoft.Json;
using Rust;
namespace Oxide.Plugins {
    [Info("RustRoyale", "Potaetobag", "1.3.6"), Description("Rust Royale custom tournament game mode with point-based scoring system.")]
    class RustRoyale : RustPlugin {
        private bool initialized = false;
        private const string ConfigUiPanelName = "RustRoyale_Config_UI";
        private int _langDownloadsCompleted = 0;
        private const int RequiredLangDownloads = 3;
        private readonly Dictionary<string, string> pendingConfig = new Dictionary<string, string>();
        private readonly Dictionary<ulong, int> npcKillCounts = new();
        private readonly Dictionary<ulong, int> animalKillCounts = new();
        private HashSet<ulong> welcomeOptOut = new HashSet<ulong>();
        private string WelcomeOptOutFile => $"{DataDirectory}/WelcomeOptOut.json";
        private Dictionary<ulong, string> teamLeaderNames = new();
        private string TeamLeadersFile => $"{DataDirectory}/TeamLeaders.json";
        private Lang lang;
        private bool hasRebuiltCaps = false;
        private string WithIndefiniteArticle(string word, string langCode) {
            var lang = Interface.Oxide.GetLibrary<Lang>();
            if (string.IsNullOrEmpty(word)) {
                var unknown = lang.GetMessage("UnknownEntity", this, langCode) ?? "unknown entity";
                if (langCode.StartsWith("en")) {
                    char first = unknown.ToLowerInvariant()[0];
                    var fmtTpl = "aeiou".Contains(first) ? lang.GetMessage("ArticleTemplateAE", this, langCode) : lang.GetMessage("ArticleTemplateA", this, langCode);
                    return string.Format(fmtTpl, unknown);
                }
                else {
                    var genericTpl = lang.GetMessage("ArticleTemplate", this, langCode) ?? "a {0}";
                    return string.Format(genericTpl, unknown);
                }
            }
            if (langCode.StartsWith("en")) {
                char first = word.ToLowerInvariant()[0];
                var fmtTpl = "aeiou".Contains(first) ? lang.GetMessage("ArticleTemplateAE", this, langCode) : lang.GetMessage("ArticleTemplateA", this, langCode);
                return string.Format(fmtTpl, word);
            }
            else {
                var genericTpl = lang.GetMessage("ArticleTemplate", this, langCode) ?? "a {0}";
                return string.Format(genericTpl, word);
            }
        }
#region Configuration
        private ConfigData Configuration;
        private class ConfigData {
            public string DefaultLanguage = "en";
            public bool EnableDebugLogs = false;
            public string DiscordWebhookUrl {
                get;
                set;
            }
            = "";
            public string ChatFormat {
                get;
                set;
            }
            = "[<color=#d97559>RustRoyale</color>] {message}";
            public string ChatIconSteamId {
                get;
                set;
            }
            = "76561199815164411";
            public string ChatUsername {
                get;
                set;
            }
            = "[RustRoyale]";
            public string Timezone {
                get;
                set;
            }
            = "UTC";
            public string StartDay {
                get;
                set;
            }
            = "Friday";
            public bool AutoStartEnabled {
                get;
                set;
            }
            = true;
            public bool AutoEnrollEnabled {
                get;
                set;
            }
            = true;
            public bool PenaltyOnExitEnabled {
                get;
                set;
            }
            = true;
            public bool ShowWelcomeUI {
                get;
                set;
            }
            = true;
            public bool EnableAutoUpdate {
                get;
                set;
            }
            = false;
            public int PenaltyPointsOnExit {
                get;
                set;
            }
            = 25;
            public int StartHour {
                get;
                set;
            }
            = 12;
            public int StartMinute {
                get;
                set;
            }
            = 0;
            public int DurationHours {
                get;
                set;
            }
            = 125;
            public int DataRetentionDays {
                get;
                set;
            }
            = 30;
            public int TopPlayersToTrack {
                get;
                set;
            }
            = 3;
            public int TopClansToTrack {
                get;
                set;
            }
            = 3;
            public int JoinCutoffHours {
                get;
                set;
            }
            = 0;
            // 0 = No late‑join cut‑off
            public int NpcKillCap {
                get;
                set;
            }
            = 0;
            public int AnimalKillCap {
                get;
                set;
            }
            = 0;
            public List<int> NotificationIntervals {
                get;
                set;
            }
            = new List<int> {
                600, 60
            }
            ;
            // Default: every 10 minutes (600 seconds) and last minute (60 seconds)
            public Dictionary<string, int> ScoreRules {
                get;
                set;
            }
            = new Dictionary<string, int> {
                {
                    "KILL", 15
                }
                ,  // Human player kills another human player
                {
                    "DEAD", -12
                }
                ,  // Human player is killed by another human player
                {
                    "JOKE", -10
                }
                ,  // Death by traps, self-inflicted damage
                {
                    "NPC", 2
                }
                ,  // Kill an NPC (Murderer, Zombie, Scientist, Scarecrow)
                {
                    "ENT", 30
                }
                ,  // Kill a Helicopter or Bradley Tank
                {
                    "BRUH", -10
                }
                ,  // Death by an NPC, Helicopter, or Bradley
                {
                    "WHY", 3
                }
                // Award points for killing an animal (wolf, boar, bear, stag, deer) from over Xm away
            }
            ;
            public float AnimalKillDistance {
                get;
                set;
            }
            = 150f;
            public Dictionary<string, int> KitPrices {
                get;
                set;
            }
            = new Dictionary<string, int> {
                {
                    "Starter", 5
                }
                , {
                    "Bronze", 20
                }
                , {
                    "Silver", 45
                }
                , {
                    "Gold", 70
                }
                , {
                    "Platinum", 110
                }
                , {
                    "Mystery Box", 65
                }
            }
            ;
        }
#endregion
#region Configuration Handling
        protected override void LoadDefaultConfig() {
            Puts("Creating default configuration...");
            Configuration = new ConfigData();
            SaveConfig();
        }
        protected override void SaveConfig() => Config.WriteObject(Configuration, true);
        protected override void LoadConfig() {
            try {
                base.LoadConfig();
                Configuration = Config.ReadObject<ConfigData>();
            }
            catch {
                PrintWarning("Configuration file is corrupted or missing. Creating a new one.");
                LoadDefaultConfig();
            }
        }
        private void LogInfo(string message) => Puts($"[RustRoyale] {message}");
        private void LogWarn(string message) => PrintWarning($"[RustRoyale] {message}");
        private void LogErr(string message) => PrintError($"[RustRoyale] {message}");
        private void DebugLog(string message) {
            if (Configuration?.EnableDebugLogs != true) return;
            Puts($"[RustRoyale] [Debug] {message}");
        }
        private void ValidateConfiguration() {
            bool updated = false;
            void SetDefault<T>(Func<T> getter, Action<T> setter, T defaultValue, string label, Func<T, bool> isInvalid) {
                if (isInvalid(getter())) {
                    PrintWarning($"Invalid {label}. Defaulting to {defaultValue}.");
                    setter(defaultValue);
                    updated = true;
                }
            }
            if (!Enum.TryParse(Configuration.StartDay, true, out DayOfWeek _)) {
                PrintWarning("Invalid StartDay in configuration. Defaulting to Friday.");
                Configuration.StartDay = "Friday";
                updated = true;
            }
            SetDefault(() => Configuration.PenaltyPointsOnExit, v => Configuration.PenaltyPointsOnExit = v, 25, "PenaltyPointsOnExit", v => v < 0);
            SetDefault(() => Configuration.AnimalKillDistance, v => Configuration.AnimalKillDistance = v, 150f, "AnimalKillDistance", v => v <= 0);
            SetDefault(() => Configuration.StartHour, v => Configuration.StartHour = v, 14, "StartHour", v => v < 0 || v > 23);
            SetDefault(() => Configuration.StartMinute, v => Configuration.StartMinute = v, 0, "StartMinute", v => v < 0 || v > 59);
            SetDefault(() => Configuration.DataRetentionDays, v => Configuration.DataRetentionDays = v, 30, "DataRetentionDays", v => v <= 0);
            SetDefault(() => Configuration.TopPlayersToTrack, v => Configuration.TopPlayersToTrack = v, 3, "TopPlayersToTrack", v => v <= 0);
            SetDefault(() => Configuration.NpcKillCap, v => Configuration.NpcKillCap = v, 0, "NpcKillCap", v => v < 0);
            SetDefault(() => Configuration.AnimalKillCap, v => Configuration.AnimalKillCap = v, 0, "AnimalKillCap", v => v < 0);
            SetDefault(() => Configuration.TopClansToTrack, v => Configuration.TopClansToTrack = v, 3, "TopClansToTrack", v => v <= 0);
            SetDefault(() => Configuration.JoinCutoffHours, v => Configuration.JoinCutoffHours = v, 6, "JoinCutoffHours", v => v < 0 || v > Configuration.DurationHours);
            if (Configuration.DurationHours < 0.0167 || Configuration.DurationHours > 872) {
                PrintWarning($"Invalid DurationHours: {Configuration.DurationHours}. Defaulting to 72.");
                Configuration.DurationHours = 72;
                updated = true;
            }
            else if (Configuration.DurationHours < 1) {
                DebugLog($"Short tournament duration detected: {Configuration.DurationHours} hours. This is valid.");
            }
            if (Configuration.ScoreRules == null || Configuration.ScoreRules.Count == 0) {
                PrintWarning("ScoreRules missing or empty. Using defaults.");
                Configuration.ScoreRules = new Dictionary<string, int> {
                    {
                        "KILL", 5
                    }
                    , {
                        "DEAD", -3
                    }
                    , {
                        "JOKE", -1
                    }
                    , {
                        "NPC", 1
                    }
                    , {
                        "ENT", 20
                    }
                    , {
                        "BRUH", -2
                    }
                    , {
                        "WHY", 5
                    }
                }
                ;
                updated = true;
            }
            if (!typeof(bool).IsAssignableFrom(Configuration.EnableAutoUpdate.GetType())) {
                PrintWarning("EnableAutoUpdate is invalid or missing. Defaulting to false.");
                Configuration.EnableAutoUpdate = false;
                updated = true;
            }
            if (Configuration.KitPrices == null || Configuration.KitPrices.Count == 0) {
                PrintWarning("KitPrices missing or empty. Using defaults.");
                Configuration.KitPrices = new Dictionary<string, int> {
                    {
                        "Starter", 5
                    }
                    , {
                        "Bronze", 25
                    }
                    , {
                        "Silver", 50
                    }
                    , {
                        "Gold", 75
                    }
                    , {
                        "Platinum", 100
                    }
                    , {
                        "Mystery Box", 50
                    }
                }
                ;
                updated = true;
            }
            if (Configuration.NotificationIntervals == null || !Configuration.NotificationIntervals.Any()) {
                PrintWarning("NotificationIntervals is invalid or missing. Defaulting to every 10 minutes and the last minute.");
                Configuration.NotificationIntervals = new List<int> {
                    600, 60
                }
                ;
                updated = true;
            }
            else {
                var cleaned = Configuration.NotificationIntervals.Where(x => x > 0).Distinct().ToList();
                if (cleaned.Count != Configuration.NotificationIntervals.Count) {
                    PrintWarning("NotificationIntervals contained invalid or duplicate values. Cleaning up the list.");
                    Configuration.NotificationIntervals = cleaned;
                    updated = true;
                }
            }
            if (Configuration.ShowWelcomeUI != true && Configuration.ShowWelcomeUI != false) {
                PrintWarning("Invalid ShowWelcomeUI. Defaulting to true.");
                Configuration.ShowWelcomeUI = true;
                updated = true;
            }
            if (updated) {
                SaveConfig();
                Puts("Configuration updated with validated defaults.");
            }
            Puts($"Configuration validated: StartDay={Configuration.StartDay}, StartHour={Configuration.StartHour}, DurationHours={Configuration.DurationHours}, " + $"DataRetentionDays={Configuration.DataRetentionDays}, TopPlayersToTrack={Configuration.TopPlayersToTrack}, " + $"TopClansToTrack={Configuration.TopClansToTrack}, NotificationIntervals={string.Join(", ", Configuration.NotificationIntervals)}.");
        }
#endregion
#region Plugin Handle
        private void OnUnload() {
            if (saveDataTimer != null) {
                saveDataTimer.Destroy();
                saveDataTimer = null;
            }
            CleanupTimers();
            SaveParticipantsData();
            SavePlayerLanguages();
        }
        private void CleanupTimers() {
            if (countdownTimer != null) {
                countdownTimer.Destroy();
                countdownTimer = null;
            }
            if (tournamentDurationTimer != null) {
                tournamentDurationTimer.Destroy();
                tournamentDurationTimer = null;
            }
            if (tournamentHeartbeatTimer != null) {
                tournamentHeartbeatTimer.Destroy();
                tournamentHeartbeatTimer = null;
            }
        }
#endregion
#region Language
        private Dictionary<ulong, string> playerLanguages = new();
        private void SavePlayerLanguages() => Interface.Oxide.DataFileSystem.WriteObject("RustRoyale_PlayerLanguages", playerLanguages);
        private void LoadPlayerLanguages() => playerLanguages = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<ulong, string>>("RustRoyale_PlayerLanguages");
        private string GetPlayerLang(BasePlayer player) {
            if (playerLanguages.TryGetValue(player.userID, out var lang) && Translations.ContainsKey(lang)) return lang;
            return Configuration.DefaultLanguage;
        }
        private string Lang(string key, BasePlayer player = null, Dictionary<string, string> tokens = null, string langOverride = null) {
            string lang = langOverride ?? (player != null ? GetPlayerLang(player) : Configuration.DefaultLanguage);
            if (!Translations.TryGetValue(lang, out var dict) || !dict.TryGetValue(key, out var message)) {
                if (!Translations.TryGetValue("en", out var fallbackDict) || !fallbackDict.TryGetValue(key, out message)) {
                    Puts($"[Warning] Missing translation key '{key}' in '{lang}' and fallback 'en'");
                    return key;
                }
            }
            if (tokens != null) {
                foreach (var token in tokens) message = message.Replace("{" + token.Key + "}", token.Value);
            }
            return message;
        }
        private void EnsureLatestLangFile(string langCode) {
            string url = $"https://raw.githubusercontent.com/potaetobag/RustRoyale/main/lang/{langCode}/RustRoyale.json";
            string fullPath = Path.Combine(Interface.Oxide.RootDirectory, "oxide", "lang", langCode, "RustRoyale.json");
            webrequest.Enqueue(url, null, (code, response) => {
                if (code != 200 || string.IsNullOrEmpty(response)) {
                    Puts($"[LangLoader] Failed to fetch '{langCode}' language file. HTTP Code: {code}");
                }
                else {
                    try {
                        bool shouldWrite = true;
                        if (File.Exists(fullPath)) {
                            string currentContent = File.ReadAllText(fullPath);
                            shouldWrite = currentContent.Trim() != response.Trim();
                        }
                        if (shouldWrite) {
                            string dir = Path.GetDirectoryName(fullPath);
                            if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
                            Puts($"[LangLoader] Attempting to write to: {fullPath}");
                            File.WriteAllText(fullPath, response);
                            Puts($"[LangLoader] Updated language file: oxide/lang/{langCode}/RustRoyale.json");
                        }
                        else {
                            Puts($"[LangLoader] oxide/lang/{langCode}/RustRoyale.json is already up to date.");
                        }
                    }
                    catch (Exception ex) {
                        Puts($"[LangLoader] Error saving oxide/lang/{langCode}/RustRoyale.json: {ex.Message}");
                    }
                }
                _langDownloadsCompleted++;
                Puts($"[LangLoader] Completed {_langDownloadsCompleted}/{RequiredLangDownloads} language downloads.");
                if (_langDownloadsCompleted >= RequiredLangDownloads) {
                    LoadTranslations();
                    Puts("[LangLoader] All language files downloaded. Continuing initialization.");
                    ContinueInitialization();
                }
            }
            , this);
        }
#endregion
#region Timezone Handling
        private TimeZoneInfo GetTimezone() {
            try {
                return TimeZoneInfo.FindSystemTimeZoneById(Configuration.Timezone);
            }
            catch (TimeZoneNotFoundException) {
                PrintWarning($"Invalid Timezone '{Configuration.Timezone}'. Defaulting to UTC.");
                return TimeZoneInfo.Utc;
            }
            catch (Exception ex) {
                PrintError($"Error loading timezone '{Configuration.Timezone}': {ex.Message}");
                return TimeZoneInfo.Utc;
            }
        }
        private DateTime GetLocalTime(DateTime utcTime) {
            var timezone = GetTimezone();
            return TimeZoneInfo.ConvertTimeFromUtc(utcTime, timezone);
        }
        private DateTime GetUtcTime(DateTime localTime) {
            var timezone = GetTimezone();
            return TimeZoneInfo.ConvertTimeToUtc(localTime, timezone);
        }
#endregion
#region UI
        private void AddCuiLabel(CuiElementContainer container, string parent, string anchorMin, string anchorMax, string text, int fontSize = 13, TextAnchor align = TextAnchor.MiddleLeft) {
            container.Add(new CuiLabel {
                RectTransform = {
                    AnchorMin = anchorMin, AnchorMax = anchorMax
                }
                , Text = {
                    Text = text, FontSize = fontSize, Align = align
                }
            }
            , parent);
        }
        private void AddCuiPanel(CuiElementContainer container, string parent, string anchorMin, string anchorMax, string color, string name = null) {
            container.Add(new CuiPanel {
                Image = {
                    Color = color
                }
                , RectTransform = {
                    AnchorMin = anchorMin, AnchorMax = anchorMax
                }
            }
            , parent, name);
        }
        private void AddCuiButton(CuiElementContainer container, string parent, string anchorMin, string anchorMax, string text, string command, string color = "0.5 0.5 0.5 1", int fontSize = 13) {
            container.Add(new CuiButton {
                Button = {
                    Command = command, Color = color
                }
                , RectTransform = {
                    AnchorMin = anchorMin, AnchorMax = anchorMax
                }
                , Text = {
                    Text = text, FontSize = fontSize, Align = TextAnchor.MiddleCenter
                }
            }
            , parent);
        }
        private void AddCuiRawImage(CuiElementContainer container, string parent, string anchorMin, string anchorMax, string url) {
            container.Add(new CuiElement {
                Parent = parent, Components = {
                    new CuiRawImageComponent {
                        Url = url
                    }
                    , new CuiRectTransformComponent {
                        AnchorMin = anchorMin, AnchorMax = anchorMax
                    }
                }
            }
            );
        }
        private void LoadWelcomeOptOut() {
            if (File.Exists(WelcomeOptOutFile)) {
                welcomeOptOut = JsonConvert.DeserializeObject<HashSet<ulong>>(File.ReadAllText(WelcomeOptOutFile)) ?? new HashSet<ulong>();
            }
        }
        private void SaveWelcomeOptOut() {
            EnsureDataDirectory();
            File.WriteAllText(WelcomeOptOutFile, JsonConvert.SerializeObject(welcomeOptOut, Formatting.Indented));
        }
        private const string WelcomeUiPanelName = "RustRoyale_Welcome_UI";
        private void ShowWelcomeUI(BasePlayer player) {
            CuiHelper.DestroyUi(player, WelcomeUiPanelName);
            var container = new CuiElementContainer();
            container.Add(new CuiPanel {
                Image = {
                    Color = "0 0 0 0.6"
                }
                , RectTransform = {
                    AnchorMin = "0 0", AnchorMax = "1 1"
                }
                , CursorEnabled = true
            }
            , "Overlay", WelcomeUiPanelName);
            container.Add(new CuiPanel {
                Image = {
                    Color = "0.1 0.1 0.1 0.95"
                }
                , RectTransform = {
                    AnchorMin = "0.15 0.1", AnchorMax = "0.85 0.9"
                }
            }
            , WelcomeUiPanelName, $"{WelcomeUiPanelName}.main");
            string scrollView = $"{WelcomeUiPanelName}.scroll";
            container.Add(new CuiElement {
                Name = scrollView, Parent = $"{WelcomeUiPanelName}.main", Components = {
                    new CuiScrollViewComponent(), new CuiRectTransformComponent {
                        AnchorMin = "0.05 0.1", AnchorMax = "0.95 0.95"
                    }
                }
            }
            );
            float y = 0.95f;
            void AddTextBlock(string text, int size = 14) {
                container.Add(new CuiLabel {
                    RectTransform = {
                        AnchorMin = $"0 {y - 0.18f}", AnchorMax = $"1 {y}"
                    }
                    , Text = {
                        Text = text, FontSize = size, Align = TextAnchor.UpperLeft
                    }
                }
                , scrollView);
                y -= 0.06f;
            }
            AddCuiRawImage(container, scrollView, "0.4 0.91", "0.6 0.98", "https://panels.twitch.tv/panel-1025512005-image-9ae6176b-7901-47e0-a0a2-2c16fed78df3");
            y -= 0.07f;
            AddTextBlock(Lang("WelcomeTitle", player), 18);
            AddTextBlock(Lang("WelcomeDescription", player));
            y -= 0.1f;
            AddTextBlock(Lang("WelcomeDiscord", player));
            y -= 0.05f;
            float baseY = y;
            float colH = 0.035f;
            float colGap = 0.005f;
            float rowSize = colH + colGap;
            int maxRows = Math.Max(Math.Max(3, Configuration.ScoreRules.Count), Configuration.KitPrices.Count);
            float col1Min = 0f, col1Max = 0.32f;
            float col2Min = 0.34f, col2Max = 0.66f;
            float col3Min = 0.68f, col3Max = 1f;
            AddCuiLabel(container, scrollView, $"{col1Min} {baseY}", $"{col1Max} {baseY + colH}", $"<b>{Lang("StatsHeader",      player)}</b>");
            AddCuiLabel(container, scrollView, $"{col2Min} {baseY}", $"{col2Max} {baseY + colH}", $"<b>{Lang("RulesHeader",      player)}</b>");
            AddCuiLabel(container, scrollView, $"{col3Min} {baseY}", $"{col3Max} {baseY + colH}", $"<b>{Lang("KitPricesHeader",  player)}</b>");
            y = baseY - rowSize;
            for (int i = 0; i < maxRows; i++) {
                if (i < 3) {
                    string statText = "";
                    if (i == 0) {
                        statText = string.Format(Lang("Duration_Label", player), Configuration.DurationHours);
                    }
                    else if (i == 1) {
                        string timeLeft;
                        if (tournamentStartTime > DateTime.MinValue) {
                            var end = tournamentStartTime.AddHours(Configuration.DurationHours);
                            var span = end - DateTime.UtcNow;
                            timeLeft = span.TotalSeconds > 0 ? $"{(int)span.TotalHours}h {span.Minutes}m" : Lang("TournamentEnded", player);
                        }
                        else timeLeft = Lang("NotAvailable", player);
                        statText = string.Format(Lang("TimeLeft_Label", player), timeLeft);
                    }
                    else if (i == 2) {
                        statText = string.Format(Lang("Participants_Label", player), participants.Count);
                    }
                    AddCuiLabel(container, scrollView, $"{col1Min} {y - colH}", $"{col1Max} {y}", $"• {statText}", 13);
                }
                if (i < Configuration.ScoreRules.Count) {
                    var kv = Configuration.ScoreRules.ElementAt(i);
                    string key = kv.Key;
                    int value = kv.Value;
                    string friendly = "";
                    string cap = "";
                    switch (key) {
                        case "KILL" : friendly = Lang("Kill_Friendly", player);
                        break;
                        case "DEAD" : friendly = Lang("Dead_Friendly", player);
                        break;
                        case "JOKE" : friendly = Lang("Joke_Friendly", player);
                        break;
                        case "ENT" : friendly = Lang("Ent_Friendly", player);
                        break;
                        case "BRUH" : friendly = Lang("Bruh_Friendly", player);
                        break;
                        case "NPC" : if (Configuration.NpcKillCap > 0) cap = string.Format(Lang("MaxCapFormat", player), Configuration.NpcKillCap);
                        else cap = Lang("NoCap", player);
                        friendly = string.Format(Lang("NpcLabelFormat", player), cap);
                        break;
                        case "WHY" : if (Configuration.AnimalKillCap > 0) cap = string.Format(Lang("MaxCapFormat", player), Configuration.AnimalKillCap);
                        else cap = Lang("NoCap", player);
                        friendly = string.Format(Lang("AnimalKillFormat", player), Configuration.AnimalKillDistance, cap);
                        break;
                        default : friendly = key;
                        break;
                    }
                    string tpl = Lang("ScoreRuleLine", player);
                    string lineText = string.Format(tpl, friendly, value);
                    AddCuiLabel(container, scrollView, $"{col2Min} {y - colH}", $"{col2Max} {y}", lineText, 13);
                }
                if (i < Configuration.KitPrices.Count) {
                    var kit = Configuration.KitPrices.ElementAt(i);
                    string kitName = Lang($"Kit_{kit.Key}", player) ?? kit.Key;
                    string tpl = Lang("Kit_Label", player);
                    string text = string.Format(tpl, kitName, kit.Value);
                    AddCuiLabel(container, scrollView, $"{col3Min} {y - colH}", $"{col3Max} {y}", $"• {text}", 13);
                }
                y -= rowSize;
            }
            string toggleLabel = welcomeOptOut.Contains(player.userID) ? Lang("Toggle_ShowNextTime", player) : Lang("Toggle_DontShowAgain", player);
            string toggleCmd = $"welcomeui_toggle {player.userID}";
            container.Add(new CuiButton {
                Button = {
                    Command = toggleCmd, Color = "0.2 0.5 0.8 1"
                }
                , RectTransform = {
                    AnchorMin = "0.35 0.01", AnchorMax = "0.49 0.06"
                }
                , Text = {
                    Text = toggleLabel, FontSize = 12, Align = TextAnchor.MiddleCenter
                }
            }
            , $"{WelcomeUiPanelName}.main");
            AddCuiButton(container, $"{WelcomeUiPanelName}.main", "0.51 0.01", "0.65 0.06", Lang("Button_Close", player), "welcomeui_close", "0.7 0.2 0.2 1");
            y -= 0.07f;
            AddTextBlock(Lang("HelpCommands_Text", player));
            var pluginTitles = Interface.Oxide.RootPluginManager .GetPlugins() .Select(p => p.Title) .Where(t => !string.IsNullOrEmpty(t)) .Distinct();
            AddTextBlock($"Plugins: {string.Join(", ", pluginTitles)}");
            CuiHelper.AddUi(player, container);
        }
        [ConsoleCommand("welcomeui_close")]
        private void CloseWelcomeUI(ConsoleSystem.Arg arg) {
            var player = arg.Player();
            if (player != null) {
                CuiHelper.DestroyUi(player, WelcomeUiPanelName);
            }
        }
        [ConsoleCommand("welcomeui_toggle")]
        private void ToggleWelcomeUI(ConsoleSystem.Arg arg) {
            var player = arg.Player();
            if (player == null || arg.Args.Length < 1) return;
            ulong id = player.userID;
            if (welcomeOptOut.Contains(id)) {
                welcomeOptOut.Remove(id);
            }
            else {
                welcomeOptOut.Add(id);
            }
            SaveWelcomeOptOut();
            ShowWelcomeUI(player);
        }
        void AddCuiInput(CuiElementContainer container, string parent, string key, string label, string value, float xLabel, float xInput, float y) {
            container.Add(new CuiLabel {
                RectTransform = {
                    AnchorMin = $"{xLabel} {y}", AnchorMax = $"{xLabel + 0.2f} {y + 0.035f}"
                }
                , Text = {
                    Text = label, FontSize = 13, Align = TextAnchor.MiddleCenter
                }
            }
            , parent);
            container.Add(new CuiPanel {
                Image = {
                    Color = "0.8 0.8 0.8 0.3"
                }
                , RectTransform = {
                    AnchorMin = $"{xInput} {y}", AnchorMax = $"{xInput + 0.2f} {y + 0.035f}"
                }
            }
            , parent);
            container.Add(new CuiElement {
                Name = $"input_{key}", Parent = parent, Components = {
                    new CuiRectTransformComponent {
                        AnchorMin = $"{xInput} {y}", AnchorMax = $"{xInput + 0.2f} {y + 0.035f}"
                    }
                    , new CuiInputFieldComponent {
                        Text = value, Align = TextAnchor.MiddleCenter, Command = $"config_set {key}"
                    }
                }
            }
            );
        }
        private void ShowConfigUI(BasePlayer player) {
            CuiHelper.DestroyUi(player, ConfigUiPanelName);
            var container = new CuiElementContainer();
            container.Add(new CuiPanel {
                Image = {
                    Color = "0 0 0 0.8"
                }
                , RectTransform = {
                    AnchorMin = "0 0", AnchorMax = "1 1"
                }
                , CursorEnabled = true
            }
            , "Overlay", ConfigUiPanelName);
            container.Add(new CuiPanel {
                Image = {
                    Color = "0.1 0.1 0.1 0.95"
                }
                , RectTransform = {
                    AnchorMin = "0.2 0.1", AnchorMax = "0.8 0.9"
                }
            }
            , ConfigUiPanelName, $"{ConfigUiPanelName}.main");
            container.Add(new CuiPanel {
                Image = {
                    Color = "0.25 0.35 0.45 1"
                }
                , RectTransform = {
                    AnchorMin = "0 0.93", AnchorMax = "1 1"
                }
            }
            , $"{ConfigUiPanelName}.main", $"{ConfigUiPanelName}.header");
            AddCuiLabel(container, $"{ConfigUiPanelName}.header", "0.02 0", "0.9 1", "RustRoyale Configuration", 18);
            container.Add(new CuiButton {
                Button = {
                    Color = "0.8 0.2 0.2 1", Command = "config_cancel"
                }
                , RectTransform = {
                    AnchorMin = "0.93 0.1", AnchorMax = "0.98 0.9"
                }
                , Text = {
                    Text = "<b>X</b>", FontSize = 16, Align = TextAnchor.MiddleCenter
                }
            }
            , $"{ConfigUiPanelName}.header");
            float y = 0.81f;
            float xLeftLabel = 0.05f, xLeftInput = 0.25f;
            float xRightLabel = 0.50f, xRightInput = 0.70f;
            void AddToggle(string label, string key, bool value, float xLabel, float xButton, float y) {
                container.Add(new CuiLabel {
                    RectTransform = {
                        AnchorMin = $"{xLabel} {y}", AnchorMax = $"{xLabel + 0.2f} {y + 0.035f}"
                    }
                    , Text = {
                        Text = label, FontSize = 13, Align = TextAnchor.MiddleCenter
                    }
                }
                , $"{ConfigUiPanelName}.main");
                container.Add(new CuiPanel {
                    Image = {
                        Color = "0.8 0.8 0.8 0.3"
                    }
                    , RectTransform = {
                        AnchorMin = $"{xButton} {y}", AnchorMax = $"{xButton + 0.2f} {y + 0.035f}"
                    }
                }
                , $"{ConfigUiPanelName}.main");
                container.Add(new CuiButton {
                    Button = {
                        Command = $"config_set {key} {!value}", Color = value ? "0.3 0.6 0.3 1" : "0.6 0.3 0.3 1"
                    }
                    , RectTransform = {
                        AnchorMin = $"{xButton} {y}", AnchorMax = $"{xButton + 0.2f} {y + 0.035f}"
                    }
                    , Text = {
                        Text = value.ToString(), FontSize = 13, Align = TextAnchor.MiddleCenter
                    }
                }
                , $"{ConfigUiPanelName}.main");
            }
            AddCuiInput(container, $"{ConfigUiPanelName}.main", "Timezone", "Server Timezone", Configuration.Timezone, xLeftLabel, xLeftInput + 0.02f, y);
            y -= 0.05f;
            AddCuiInput(container, $"{ConfigUiPanelName}.main", "StartDay", "Tournament Start Day", Configuration.StartDay, xLeftLabel, xLeftInput + 0.02f, y);
            y -= 0.05f;
            AddCuiInput(container, $"{ConfigUiPanelName}.main", "StartHour", "Start Hour (0–23)", Configuration.StartHour.ToString(), xLeftLabel, xLeftInput + 0.02f, y);
            y -= 0.05f;
            AddCuiInput(container, $"{ConfigUiPanelName}.main", "StartMinute", "Start Minute (0–59)", Configuration.StartMinute.ToString(), xLeftLabel, xLeftInput + 0.02f, y);
            y -= 0.05f;
            AddCuiInput(container, $"{ConfigUiPanelName}.main", "DurationHours", "Tournament Duration (Hours)", Configuration.DurationHours.ToString(), xLeftLabel, xLeftInput + 0.02f, y);
            y -= 0.05f;
            AddCuiInput(container, $"{ConfigUiPanelName}.main", "TopPlayersToTrack", "Top Players to Track", Configuration.TopPlayersToTrack.ToString(), xLeftLabel, xLeftInput + 0.02f, y);
            y -= 0.05f;
            AddCuiInput(container, $"{ConfigUiPanelName}.main", "TopClansToTrack", "Top Clans to Track", Configuration.TopClansToTrack.ToString(), xLeftLabel, xLeftInput + 0.02f, y);
            y -= 0.05f;
            AddCuiInput(container, $"{ConfigUiPanelName}.main", "JoinCutoffHours", "Join Cutoff Time (Hours)", Configuration.JoinCutoffHours.ToString(), xLeftLabel, xLeftInput + 0.02f, y);
            y -= 0.05f;
            AddCuiInput(container, $"{ConfigUiPanelName}.main", "AnimalKillDistance", "Animal Kill Distance (m)", Configuration.AnimalKillDistance.ToString(), xLeftLabel, xLeftInput + 0.02f, y);
            y -= 0.05f;
            AddCuiInput(container, $"{ConfigUiPanelName}.main", "NpcKillCap", "Max NPC Kills Per Player (0 = Unlimited)", Configuration.NpcKillCap.ToString(), xLeftLabel, xLeftInput + 0.02f, y);
            y -= 0.05f;
            AddCuiInput(container, $"{ConfigUiPanelName}.main", "AnimalKillCap", "Max Animal Kills Per Player (0 = Unlimited)", Configuration.AnimalKillCap.ToString(), xLeftLabel, xLeftInput + 0.02f, y);
            y -= 0.05f;
            AddCuiInput(container, $"{ConfigUiPanelName}.main", "PenaltyPointsOnExit", "Penalty Points on Exit", Configuration.PenaltyPointsOnExit.ToString(), xLeftLabel, xLeftInput + 0.02f, y);
            y -= 0.05f;
            AddToggle("Enable Plugin Auto-Update", "EnableAutoUpdate", Configuration.EnableAutoUpdate, xLeftLabel, xLeftInput + 0.02f, y);
            y -= 0.05f;
            AddToggle("Show Welcome Message", "ShowWelcomeUI", Configuration.ShowWelcomeUI, xLeftLabel, xLeftInput + 0.02f, y);
            y -= 0.05f;
            AddToggle("Auto-Start Tournament", "AutoStartEnabled", Configuration.AutoStartEnabled, xLeftLabel, xLeftInput + 0.02f, y);
            y -= 0.05f;
            float toggleY = 0.81f;
            string currentLang = Configuration.DefaultLanguage;
            int currentIndex = Array.IndexOf(SupportedLanguages, currentLang);
            int nextIndex = (currentIndex + 1) % SupportedLanguages.Length;
            string nextLang = SupportedLanguages[nextIndex];
            container.Add(new CuiLabel {
                RectTransform = {
                    AnchorMin = $"{xRightLabel} {toggleY}", AnchorMax = $"{xRightLabel + 0.2f} {toggleY + 0.035f}"
                }
                , Text = {
                    Text = "Default Language", FontSize = 13, Align = TextAnchor.MiddleCenter
                }
            }
            , $"{ConfigUiPanelName}.main");
            container.Add(new CuiButton {
                Button = {
                    Color = "0.2 0.4 0.8 1", Command = $"config_set DefaultLanguage {nextLang}"
                }
                , RectTransform = {
                    AnchorMin = $"{xRightInput + 0.02f} {toggleY}", AnchorMax = $"{xRightInput + 0.22f} {toggleY + 0.035f}"
                }
                , Text = {
                    Text = currentLang, FontSize = 13, Align = TextAnchor.MiddleCenter
                }
            }
            , $"{ConfigUiPanelName}.main");
            toggleY -= 0.05f;
            AddToggle("Auto-Enroll Players", "AutoEnrollEnabled", Configuration.AutoEnrollEnabled, xRightLabel, xRightInput + 0.02f, toggleY);
            toggleY -= 0.05f;
            AddToggle("Penalize Players Who Leave", "PenaltyOnExitEnabled", Configuration.PenaltyOnExitEnabled, xRightLabel, xRightInput + 0.02f, toggleY);
            toggleY -= 0.05f;
            y -= 0.05f;
            float groupY = 0.66f;
            foreach (var kv in Configuration.ScoreRules) {
                float marginX = xRightLabel + 0.22f;
                container.Add(new CuiLabel {
                    RectTransform = {
                        AnchorMin = $"{xRightLabel} {groupY}", AnchorMax = $"{xRightLabel + 0.2f} {groupY + 0.035f}"
                    }
                    , Text = {
                        Text = $"ScoreRules[{kv.Key}]", FontSize = 13, Align = TextAnchor.MiddleCenter
                    }
                }
                , $"{ConfigUiPanelName}.main");
                container.Add(new CuiPanel {
                    Image = {
                        Color = "0.8 0.8 0.8 0.3"
                    }
                    , RectTransform = {
                        AnchorMin = $"{marginX} {groupY}", AnchorMax = $"{marginX + 0.2f} {groupY + 0.035f}"
                    }
                }
                , $"{ConfigUiPanelName}.main");
                container.Add(new CuiElement {
                    Name = $"ScoreRule_{kv.Key}", Parent = $"{ConfigUiPanelName}.main", Components = {
                        new CuiRectTransformComponent {
                            AnchorMin = $"{marginX} {groupY}", AnchorMax = $"{marginX + 0.2f} {groupY + 0.035f}"
                        }
                        , new CuiInputFieldComponent {
                            Text = kv.Value.ToString(), Align = TextAnchor.MiddleCenter, Command = $"config_set ScoreRules.{kv.Key}"
                        }
                    }
                }
                );
                groupY -= 0.05f;
            }
            groupY -= 0.01f;
            foreach (var kv in Configuration.KitPrices) {
                float marginX = xRightLabel + 0.22f;
                container.Add(new CuiLabel {
                    RectTransform = {
                        AnchorMin = $"{xRightLabel} {groupY}", AnchorMax = $"{xRightLabel + 0.2f} {groupY + 0.035f}"
                    }
                    , Text = {
                        Text = $"KitPrices[{kv.Key}]", FontSize = 13, Align = TextAnchor.MiddleCenter
                    }
                }
                , $"{ConfigUiPanelName}.main");
                container.Add(new CuiPanel {
                    Image = {
                        Color = "0.8 0.8 0.8 0.3"
                    }
                    , RectTransform = {
                        AnchorMin = $"{marginX} {groupY}", AnchorMax = $"{marginX + 0.2f} {groupY + 0.035f}"
                    }
                }
                , $"{ConfigUiPanelName}.main");
                container.Add(new CuiElement {
                    Name = $"KitPrice_{kv.Key}", Parent = $"{ConfigUiPanelName}.main", Components = {
                        new CuiRectTransformComponent {
                            AnchorMin = $"{marginX} {groupY}", AnchorMax = $"{marginX + 0.2f} {groupY + 0.035f}"
                        }
                        , new CuiInputFieldComponent {
                            Text = kv.Value.ToString(), Align = TextAnchor.MiddleCenter, Command = $"config_set KitPrices.{kv.Key}"
                        }
                    }
                }
                );
                groupY -= 0.05f;
            }
            container.Add(new CuiButton {
                Button = {
                    Command = "config_save", Color = "0.2 0.6 0.2 1"
                }
                , RectTransform = {
                    AnchorMin = "0.27 0.02", AnchorMax = "0.47 0.07"
                }
                , Text = {
                    Text = Lang("ConfigSave"), FontSize = 13, Align = TextAnchor.MiddleCenter
                }
            }
            , $"{ConfigUiPanelName}.main");
            container.Add(new CuiButton {
                Button = {
                    Command = "config_cancel", Color = "0.6 0.2 0.2 1"
                }
                , RectTransform = {
                    AnchorMin = "0.53 0.02", AnchorMax = "0.73 0.07"
                }
                , Text = {
                    Text = Lang("ConfigCancel"), FontSize = 13, Align = TextAnchor.MiddleCenter
                }
            }
            , $"{ConfigUiPanelName}.main");
            string currentVersion = GetCurrentPluginVersion();
            string versionStatus = "Up to date";
            string versionColor = "#00ff00";
            if (!string.IsNullOrEmpty(LatestPluginVersion)) {
                var current = ParseVersion(currentVersion);
                var latest = ParseVersion(LatestPluginVersion);
                if (latest > current) {
                    versionStatus = $"Update available ({LatestPluginVersion})";
                    versionColor = "#ff0000";
                }
            }
            container.Add(new CuiLabel {
                RectTransform = {
                    AnchorMin = "0.20 0.84", AnchorMax = "0.80 0.93"
                }
                , Text = {
                    Text = $"<color={versionColor}>RustRoyale v{currentVersion} - {versionStatus}</color>", FontSize = 13, Align = TextAnchor.MiddleCenter
                }
            }
            , $"{ConfigUiPanelName}.main");
            CuiHelper.AddUi(player, container);
        }
        private void CloseConfigUI(BasePlayer player) {
            CuiHelper.DestroyUi(player, ConfigUiPanelName);
        }
        [ConsoleCommand("config_set")]
        private void ConsoleConfigSet(ConsoleSystem.Arg arg) {
            var player = arg.Player();
            if (player == null || !HasAdminPermission(player)) return;
            var 
// ... [Code preview truncated due to length. Full source in attached file] ...

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