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

FREE Timers : 2.1.3 2.1.3

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

namespace Oxide.Plugins
{
    [Info("Timers", "Von", "2.1.3")]
    [Description("Advanced timer plugin with multi-timer and targeting support")]
    public class Timers : RustPlugin
    {
        #region Fields
        private const string PERMISSION_USE = "timers.use";
        private const string TIMERS_UI_PANEL = "TimersUI";
        private const int MAX_TIMERS = 5;

        private Configuration config;
        private List<TimerData> activeTimers = new List<TimerData>();
        private HashSet<BasePlayer> playersWithUI = new HashSet<BasePlayer>();
        private HashSet<BasePlayer> playersWithTimersUI = new HashSet<BasePlayer>();
        private Dictionary<ulong, TimerCreationData> tempTimerData = new Dictionary<ulong, TimerCreationData>();
        private Dictionary<ulong, string> tempEditTimerId = new Dictionary<ulong, string>();
        private Dictionary<ulong, string> playerUiSnapshots = new Dictionary<ulong, string>();
        private Timer updateTimer;
        private Timer saveTimer;
        private HashSet<ulong> playersInSettings = new HashSet<ulong>();
        #endregion

        #region Enums
        public enum TimerPosition
        {
            TopRight,
            TopLeft,
            BottomRight,
            BottomLeft
        }

        public enum TimerState
        {
            Active,
            Paused,
            Inactive
        }
        #endregion

        #region Data Structures
        private class SavedData
        {
            public List<SavedTimer> Timers { get; set; } = new List<SavedTimer>();
        }

        private class SavedTimer
        {
            public string Id { get; set; }
            public DateTime EndTime { get; set; }
            public string Text { get; set; }
            public HashSet<string> TargetSteamIds { get; set; }
            public HashSet<string> TargetGroups { get; set; }
            public string CreatorId { get; set; }
            public bool IsGlobal { get; set; }
            public bool ShowCountdown { get; set; }
            public string TextColor { get; set; }
            public string BackgroundColor { get; set; }
            public TimerState State { get; set; }
            public double PausedTime { get; set; }
            public bool ExecuteCommand { get; set; }
            public string EndCommand { get; set; }

            public static SavedTimer FromTimerData(TimerData timer)
            {
                return new SavedTimer
                {
                    Id = timer.Id,
                    EndTime = timer.EndTime,
                    Text = timer.Text,
                    TargetSteamIds = timer.TargetSteamIds,
                    TargetGroups = timer.TargetGroups,
                    CreatorId = timer.CreatorId,
                    IsGlobal = timer.IsGlobal,
                    ShowCountdown = timer.ShowCountdown,
                    TextColor = timer.TextColor,
                    BackgroundColor = timer.BackgroundColor,
                    State = timer.State,
                    PausedTime = timer.PausedTime,
                    ExecuteCommand = timer.ExecuteCommand,
                    EndCommand = timer.EndCommand
                };
            }

            public TimerData ToTimerData()
            {
                var timerData = new TimerData(Text, 0, CreatorId, IsGlobal, false)
                {
                    Id = Id,
                    EndTime = EndTime,
                    TargetSteamIds = TargetSteamIds,
                    TargetGroups = TargetGroups,
                    ShowCountdown = ShowCountdown,
                    TextColor = TextColor,
                    BackgroundColor = BackgroundColor,
                    State = State,
                    PausedTime = PausedTime,
                    ExecuteCommand = ExecuteCommand,
                    EndCommand = EndCommand ?? string.Empty
                };
                return timerData;
            }
        }

        private class TimerData
        {
            public string Id { get; set; }
            public DateTime EndTime { get; set; }
            public string Text { get; set; }
            public HashSet<string> TargetSteamIds { get; set; }
            public HashSet<string> TargetGroups { get; set; }
            public string CreatorId { get; set; }
            public bool IsGlobal { get; set; }
            public bool ShowCountdown { get; set; }
            public string TextColor { get; set; }
            public string BackgroundColor { get; set; }
            public TimerState State { get; set; }
            public double PausedTime { get; set; }
            public bool ExecuteCommand { get; set; }
            public string EndCommand { get; set; }

            public TimerData(string text, int durationSeconds, string creatorId, bool isGlobal = false, bool startImmediately = true)
            {
                Id = Guid.NewGuid().ToString("N");
                Text = text;
                TargetSteamIds = new HashSet<string>();
                TargetGroups = new HashSet<string>();
                CreatorId = creatorId;
                IsGlobal = isGlobal;
                ShowCountdown = true;
                TextColor = "1 1 1 1";
                BackgroundColor = "0 0 0 0.8";
                ExecuteCommand = false;
                EndCommand = string.Empty;
                State = startImmediately ? TimerState.Active : TimerState.Inactive;
                
                if (startImmediately)
                {
                    EndTime = DateTime.Now.AddSeconds(durationSeconds);
                    PausedTime = 0;
                }
                else
                {
                    EndTime = DateTime.MinValue;
                    PausedTime = durationSeconds;
                }
            }

            public bool ShouldShowToPlayer(BasePlayer player, Oxide.Core.Libraries.Permission permission)
            {
                if (IsGlobal || TargetSteamIds.Count == 0 && TargetGroups.Count == 0)
                    return true;

                if (TargetSteamIds.Contains(player.UserIDString))
                    return true;

                return TargetGroups.Any(group => permission.UserHasGroup(player.UserIDString, group));
            }

            public string GetTimeString()
            {
                TimeSpan remaining;
                
                if (State == TimerState.Active)
                {
                    remaining = EndTime - DateTime.Now;
                    if (remaining.TotalSeconds <= 0)
                        remaining = TimeSpan.Zero;
                }
                else if (State == TimerState.Paused || State == TimerState.Inactive)
                {
                    remaining = TimeSpan.FromSeconds(PausedTime);
                }
                else
                {
                    remaining = TimeSpan.Zero;
                }
                
                if (!ShowCountdown)
                    return Text;

                string timeStr;
                if (remaining.TotalDays >= 1)
                    timeStr = $"{(int)remaining.TotalDays}d {remaining.Hours}h";
                else if (remaining.TotalHours >= 1)
                    timeStr = $"{(int)remaining.TotalHours}h {remaining.Minutes}m";
                else if (remaining.TotalMinutes >= 1)
                    timeStr = $"{remaining.Minutes}m {remaining.Seconds}s";
                else
                    timeStr = $"{remaining.Seconds}s";

                return string.IsNullOrEmpty(Text) ? timeStr : $"{Text}: {timeStr}";
            }
        }

        private class TimerCreationData
        {
            public int Duration { get; set; }
            public string Text { get; set; }
            public HashSet<string> TargetSteamIds { get; set; }
            public HashSet<string> TargetGroups { get; set; }
            public bool IsGlobal { get; set; }
            public bool ShowCountdown { get; set; }
            public string TextColor { get; set; }
            public string BackgroundColor { get; set; }
            public bool StartImmediately { get; set; }
            public bool IsEditing { get; set; }
            public TimerState OriginalState { get; set; }
            public bool ExecuteCommand { get; set; }
            public string EndCommand { get; set; }

            public TimerCreationData()
            {
                Duration = 300;
                Text = string.Empty;
                TargetSteamIds = new HashSet<string>();
                TargetGroups = new HashSet<string>();
                IsGlobal = true;
                ShowCountdown = true;
                TextColor = "1 1 1 1";
                BackgroundColor = "0 0 0 0.8";
                StartImmediately = true;
                IsEditing = false;
                OriginalState = TimerState.Active;
                ExecuteCommand = false;
                EndCommand = string.Empty;
            }
        }

        private class Configuration
        {
            public string BackgroundColor = "0 0 0 0.8";
            public string TextColor = "1 1 1 1";
            public string HighlightColor = "0.2 0.8 0.2 1";
            public bool EnableNotifications = false;
            public int DefaultDuration = 300;
            public int MaxDuration = 86400;
            public int FontSize = 14;
            public float UIScale = 1.0f;
            public bool TransparentBackground = false;
            public TimerPosition TimerPosition = TimerPosition.TopRight;
        }
        #endregion

        #region Data Management
        private void SaveData()
        {
            if (!activeTimers.Any())
            {
                Interface.Oxide.DataFileSystem.DeleteDataFile(Name + "_data");
                return;
            }

            var data = new SavedData
            {
                Timers = activeTimers.Select(SavedTimer.FromTimerData).ToList()
            };

            Interface.Oxide.DataFileSystem.WriteObject(Name + "_data", data);
        }

        private void LoadData()
        {
            try
            {
                var data = Interface.Oxide.DataFileSystem.ReadObject<SavedData>(Name + "_data");
                if (data?.Timers != null)
                {
                    LoadTimersFromSavedData(data);
                    return;
                }

                var oldData = Interface.Oxide.DataFileSystem.ReadObject<List<SavedTimer>>(Name + "_data");
                if (oldData != null)
                {
                    var newData = new SavedData { Timers = oldData };
                    LoadTimersFromSavedData(newData);
                    SaveData();
                    return;
                }
            }
            catch (Exception ex)
            {
                PrintError($"Error loading timer data: {ex.Message}");
                activeTimers.Clear();
            }
        }

        private void LoadTimersFromSavedData(SavedData data)
        {
            var now = DateTime.Now;
            var validTimers = data.Timers
                .Where(t => t.State == TimerState.Active ? t.EndTime > now : (t.State == TimerState.Paused || t.State == TimerState.Inactive))
                .Select(t => t.ToTimerData())
                .ToList();

            activeTimers.Clear();
            activeTimers.AddRange(validTimers);
            activeTimers.Sort((a, b) => a.EndTime.CompareTo(b.EndTime));

            if (validTimers.Count != data.Timers.Count)
            {
                SaveData();
            }
        }
        #endregion

        #region Oxide Hooks
        private void Init()
        {
            permission.RegisterPermission(PERMISSION_USE, this);
            
            cmd.AddConsoleCommand("timer", this, nameof(ConsoleCommand));
            cmd.AddChatCommand("timers", this, nameof(ChatCommand));
            
            LoadConfig();
            LoadData();
            InitializeTimerSystem();
        }

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

            if (saveTimer != null && !saveTimer.Destroyed)
                saveTimer.Destroy();

            saveTimer = timer.Every(300f, SaveData);
        }

        private void OnServerSave() => SaveData();

        private void Unload()
        {
            if (updateTimer != null && !updateTimer.Destroyed)
                updateTimer.Destroy();

            if (saveTimer != null && !saveTimer.Destroyed)
                saveTimer.Destroy();

            SaveData();

            foreach (var player in BasePlayer.activePlayerList)
            {
                DestroyTimerUI(player);
            }
        }

        private void OnPlayerConnected(BasePlayer player)
        {
            if (activeTimers.Any(t => t.State == TimerState.Active && t.ShouldShowToPlayer(player, permission)))
            {
                CreateTimerUI(player);
            }
        }

        private void OnPlayerDisconnected(BasePlayer player)
        {
            DestroyTimerUI(player);
            playersWithUI.Remove(player);
            tempTimerData.Remove(player.userID);
            tempEditTimerId.Remove(player.userID);
            playersInSettings.Remove(player.userID);
        }
        #endregion

        #region Timer System
        private void InitializeTimerSystem()
        {
            if (updateTimer != null && !updateTimer.Destroyed)
            {
                updateTimer.Destroy();
            }

            updateTimer = timer.Every(1f, UpdateTimers);
        }

        private void UpdateTimers()
        {
            var expiredTimers = activeTimers.Where(t => t.State == TimerState.Active && t.EndTime != DateTime.MinValue && t.EndTime <= DateTime.Now).ToList();
            
            foreach (var expiredTimer in expiredTimers)
            {
                HandleTimerExpiration(expiredTimer);
                activeTimers.Remove(expiredTimer);
            }

            if (expiredTimers.Any())
            {
                UpdateAllUI();
                SaveData();
            }
            else if (activeTimers.Any(t => t.State == TimerState.Active))
            {
                var players = BasePlayer.activePlayerList.Where(p => playersWithUI.Contains(p)).ToList();
                foreach (var player in players)
                {
                    var snapshot = BuildPlayerSnapshot(player);
                    if (string.IsNullOrEmpty(snapshot))
                    {
                        DestroyTimerUI(player);
                        playersWithUI.Remove(player);
                        playerUiSnapshots.Remove(player.userID);
                        continue;
                    }

                    if (!playerUiSnapshots.TryGetValue(player.userID, out var previous) || previous != snapshot)
                    {
                        UpdateTimerUI(player);
                        playerUiSnapshots[player.userID] = snapshot;
                    }
                }
            }
        }

        private void HandleTimerExpiration(TimerData timer)
        {
            if (timer.ExecuteCommand && !string.IsNullOrWhiteSpace(timer.EndCommand))
            {
                RunEndCommand(timer.EndCommand);
            }

            if (config.EnableNotifications)
            {
                foreach (var player in BasePlayer.activePlayerList)
                {
                    if (!timer.ShouldShowToPlayer(player, permission))
                        continue;

                    SendReply(player, $"Timer completed: {timer.Text}");
                }
            }

            SaveData();
        }

        private void RunEndCommand(string command)
        {
            if (string.IsNullOrWhiteSpace(command))
                return;

            try
            {
                ConsoleSystem.Run(ConsoleSystem.Option.Server.Quiet(), command.Trim());
            }
            catch (Exception ex)
            {
                PrintError($"Failed to run timer end command '{command}': {ex.Message}");
            }
        }

        private void StartTimer(BasePlayer creator, TimerCreationData data)
        {
            if (creator == null || data == null)
            {
                PrintError("StartTimer called with null creator or data");
                return;
            }

            if (!TryCreateTimer(data, creator.UserIDString, out var timer, out var error))
            {
                SendReply(creator, error);
                return;
            }

            if (timer.State == TimerState.Active)
                SendReply(creator, $"Timer started{GetTargetInfo(timer)}!");
            else
                SendReply(creator, $"Timer created{GetTargetInfo(timer)}!");
        }

        private string GetTargetInfo(TimerData timer)
        {
            if (timer.IsGlobal)
                return " for all players";

            List<string> targets = new List<string>();
            
            if (timer.TargetSteamIds.Count > 0)
                targets.Add($"{timer.TargetSteamIds.Count} players");
            
            if (timer.TargetGroups.Count > 0)
                targets.Add($"{timer.TargetGroups.Count} groups");

            return targets.Count > 0 ? $" for {string.Join(" and ", targets)}" : "";
        }

        private bool TryCreateTimer(TimerCreationData data, string creatorId, out TimerData timer, out string error)
        {
            timer = null;
            error = null;

            if (data == null)
            {
                error = "Invalid timer data";
                return false;
            }

            if (config == null)
            {
                LoadDefaultConfig();
            }

            if (activeTimers.Count >= MAX_TIMERS)
            {
                error = "You have reached the maximum number of active timers!";
                return false;
            }

            if (data.Duration <= 0 || data.Duration > config.MaxDuration)
            {
                error = $"Duration must be between 1 and {config.MaxDuration} seconds!";
                return false;
            }

            timer = new TimerData(data.Text ?? string.Empty, data.Duration, creatorId, data.IsGlobal, data.StartImmediately)
            {
                TargetSteamIds = data.TargetSteamIds ?? new HashSet<string>(),
                TargetGroups = data.TargetGroups ?? new HashSet<string>(),
                ShowCountdown = data.ShowCountdown,
                TextColor = data.TextColor,
                BackgroundColor = data.BackgroundColor,
                ExecuteCommand = data.ExecuteCommand,
                EndCommand = data.EndCommand ?? string.Empty
            };

            activeTimers.Add(timer);
            activeTimers.Sort((a, b) => a.EndTime.CompareTo(b.EndTime));
            UpdateAllUI();
            SaveData();

            return true;
        }

        private void StopTimer(string timerId)
        {
            var timer = activeTimers.FirstOrDefault(t => t.Id == timerId);
            if (timer == null)
                return;

            activeTimers.Remove(timer);
            UpdateAllUI();
            SaveData();
        }

        private void UpdateTimerFromEdit(TimerData timer, TimerCreationData data)
        {
            if (timer == null || data == null) return;

            timer.Text = data.Text ?? string.Empty;
            timer.TargetSteamIds = data.TargetSteamIds ?? new HashSet<string>();
            timer.TargetGroups = data.TargetGroups ?? new HashSet<string>();
            timer.IsGlobal = timer.TargetSteamIds.Count == 0 && timer.TargetGroups.Count == 0;
            timer.ShowCountdown = data.ShowCountdown;
            timer.TextColor = data.TextColor;
            timer.BackgroundColor = data.BackgroundColor;
            timer.ExecuteCommand = data.ExecuteCommand;
            timer.EndCommand = data.EndCommand ?? string.Empty;

            int duration = Math.Max(1, data.Duration);

            if (data.IsEditing && data.OriginalState == TimerState.Paused && !data.StartImmediately)
            {
                timer.State = TimerState.Paused;
                timer.EndTime = DateTime.MinValue;
                timer.PausedTime = duration;
                return;
            }

            if (data.StartImmediately)
            {
                timer.State = TimerState.Active;
                timer.EndTime = DateTime.Now.AddSeconds(duration);
                timer.PausedTime = 0;
            }
            else
            {
                timer.State = TimerState.Inactive;
                timer.EndTime = DateTime.MinValue;
                timer.PausedTime = duration;
            }
        }

        private int GetRemainingSeconds(TimerData timer)
        {
            if (timer == null) return 0;

            if (timer.State == TimerState.Active)
            {
                var remaining = (timer.EndTime - DateTime.Now).TotalSeconds;
                return Math.Max(1, (int)Math.Ceiling(remaining));
            }

            if (timer.State == TimerState.Paused || timer.State == TimerState.Inactive)
                return Math.Max(1, (int)Math.Ceiling(timer.PausedTime));

            return 0;
        }

        private void OpenEditTimerUI(BasePlayer player, TimerData timer)
        {
            if (player == null || timer == null) return;

            if (!tempTimerData.ContainsKey(player.userID))
                tempTimerData[player.userID] = new TimerCreationData();

            var data = tempTimerData[player.userID];
            data.Duration = GetRemainingSeconds(timer);
            data.Text = timer.Text ?? string.Empty;
            data.TargetSteamIds = new HashSet<string>(timer.TargetSteamIds ?? new HashSet<string>());
            data.TargetGroups = new HashSet<string>(timer.TargetGroups ?? new HashSet<string>());
            data.IsGlobal = timer.IsGlobal;
            data.ShowCountdown = timer.ShowCountdown;
            data.TextColor = timer.TextColor;
            data.BackgroundColor = timer.BackgroundColor;
            data.ExecuteCommand = timer.ExecuteCommand;
            data.EndCommand = timer.EndCommand ?? string.Empty;
            data.StartImmediately = timer.State == TimerState.Active;
            data.IsEditing = true;
            data.OriginalState = timer.State;

            tempEditTimerId[player.userID] = timer.Id;
            ShowTimerCreationUI(player);
        }

        private void UpdateAllUI()
        {
            foreach (var player in BasePlayer.activePlayerList)
            {
                if (activeTimers.Any(t => t.State == TimerState.Active && t.ShouldShowToPlayer(player, permission)))
                {
                    if (!playersWithUI.Contains(player))
                    {
                        CreateTimerUI(player);
                    }
                    else
                    {
                        UpdateTimerUI(player);
                    }
                }
                else if (playersWithUI.Contains(player))
                {
                    DestroyTimerUI(player);
                    playersWithUI.Remove(player);
                }
            }
        }
        #endregion

        #region Commands
        private void ConsoleCommand(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            var isRcon = player == null;

            if (!isRcon && !permission.UserHasPermission(player.UserIDString, PERMISSION_USE))
            {
                SendReply(player, "You don't have permission to use this command!");
                return;
            }

            if (!arg.HasArgs())
            {
                SendReply(arg, "Usage: timer <start|stop|list> [duration] [text] [@group1 @group2 steamid1 steamid2 ...]");
                return;
            }

            switch (arg.GetString(0).ToLower())
            {
                case "start":
                    if (!arg.HasArgs(2))
                    {
                        SendReply(arg, "Usage: timer start <seconds> [text] [@group1 @group2 steamid1 steamid2 ...]");
                        return;
                    }

                    if (!int.TryParse(arg.GetString(1), out int seconds))
                    {
                        SendReply(arg, "Please provide a valid number of seconds!");
                        return;
                    }

                    var textParts = new List<string>();
                    var data = new TimerCreationData
                    {
                        Duration = seconds,
                        Text = string.Empty,
                        IsGlobal = true,
                        TargetSteamIds = new HashSet<string>(),
                        TargetGroups = new HashSet<string>()
                    };

                    for (int i = 2; i < arg.Args.Length; i++)
                    {
                        string argValue = arg.GetString(i);
                        if (argValue.StartsWith("@"))
                        {
                            data.TargetGroups.Add(argValue.Substring(1));
                            data.IsGlobal = false;
                        }
                        else if (IsSteamId(argValue))
                        {
                            data.TargetSteamIds.Add(argValue);
                            data.IsGlobal = false;
                        }
                        else
                        {
                            textParts.Add(argValue);
                        }
                    }

                    data.Text = textParts.Count > 0 ? string.Join(" ", textParts) : string.Empty;

                    if (!TryCreateTimer(data, isRcon ? "RCON" : player.UserIDString, out var newTimer, out var error))
                    {
                        SendReply(arg, error);
                        return;
                    }

                    SendReply(arg, $"Timer started{GetTargetInfo(newTimer)}!");
                    break;

                case "stop":
                    if (!activeTimers.Any())
                    {
                        SendReply(arg, "No active timers!");
                        return;
                    }

                    if (arg.Args.Length == 1)
                    {
                        string timerList = string.Join("\n", activeTimers.Select((t, i) => $"{i + 1}. {t.GetTimeString()}"));
                        SendReply(arg, $"Active timers:\n{timerList}\nUse: timer stop <number> or timer stop \"text\"");
                        return;
                    }

                    if (int.TryParse(arg.GetString(1), out int index) && index > 0 && index <= activeTimers.Count)
                    {
                        var timer = activeTimers[index - 1];
                        StopTimer(timer.Id);
                        SendReply(arg, $"Timer {index} stopped!");
                    }
                    else
                    {
                        string text = string.Join(" ", arg.Args.Skip(1));
                        var timer = activeTimers.FirstOrDefault(t => t.Text.Equals(text, StringComparison.OrdinalIgnoreCase));
                        if (timer != null)
                        {
                            StopTimer(timer.Id);
                            SendReply(arg, $"Timer '{text}' stopped!");
                        }
                        else
                        {
                            SendReply(arg, $"No timer found with text '{text}'");
                        }
                    }
                    break;

                case "list":
                    if (!activeTimers.Any())
                    {
                        SendReply(arg, "No active timers!");
                        return;
                    }

                    string list = string.Join("\n", activeTimers.Select((t, i) => $"{i + 1}. {t.GetTimeString()}"));
                    SendReply(arg, $"Active timers:\n{list}");
                    break;

                default:
                    SendReply(arg, "Usage: timer <start|stop|list> [duration] [text] [@group1 @group2 steamid1 steamid2 ...]");
                    break;
            }
        }

        private void ChatCommand(BasePlayer player, string command, string[] args)
        {
            if (!permission.UserHasPermission(player.UserIDString, PERMISSION_USE))
            {
                SendReply(player, "You don't have permission to use this command!");
                return;
            }

            if (playersInSettings.Contains(player.userID))
                ShowSettingsUI(player);
            else
                CreateTimersUI(player);
        }
        #endregion

        #region UI System
        private void CreateTimerUI(BasePlayer player)
        {
            if (player == null || !player.IsConnected) return;
            
            DestroyTimerUI(player);

            if (!activeTimers.Any()) return;

            var elements = new CuiElementContainer();
            float startY = 0.98f;
            float timerHeight = 0.03f;
            float spacing = 0.005f;

            var visibleTimers = activeTimers
                .Where(t => t.State == TimerState.Active && t.ShouldShowToPlayer(player, permission))
                .ToList();
            for (int i = 0; i < visibleTimers.Count; i++)
            {
                var timer = visibleTimers[i];

                float currentY = startY - (i * (timerHeight + spacing));
                string displayText = timer.GetTimeString();
                
                float aspectRatio = (float)Screen.width / Screen.height;
                float baseAspect = 16f / 9f;
                float aspectMultiplier = Math.Min(aspectRatio / baseAspect, 1.5f);
                
                float charWidthRelative = 0.006f;
                float paddingRelative = 0.008f;
                float minWidthRelative = 0.05f;
                
                float textWidth = displayText.Length * charWidthRelative;
                float totalWidth = textWidth + (paddingRelative * 2);
                float width = Math.Max(totalWidth, minWidthRelative) * aspectMultiplier;
                
                string containerName = $"Timer_Container_{i}";

                float margin = 0.001f;
                string anchorMin, anchorMax;
                
                switch (config.TimerPosition)
                {
                    case TimerPosition.TopRight:
                        float rightEdge = 1.0f - margin;
                        anchorMin = $"{rightEdge - width} {currentY - timerHeight}";
                        anchorMax = $"{rightEdge} {currentY}";
                        break;
                    case TimerPosition.TopLeft:
                        float leftEdge = margin;
                        anchorMin = $"{leftEdge} {currentY - timerHeight}";
                        anchorMax = $"{leftEdge + width} {currentY}";
                        break;
                    case TimerPosition.BottomRight:
                        float rightEdgeBottom = 1.0f - margin;
                        float bottomY = 0.15f + (i * (timerHeight + spacing));
                        anchorMin = $"{rightEdgeBottom - width} {bottomY}";
                        anchorMax = $"{rightEdgeBottom} {bottomY + timerHeight}";
                        break;
                    case TimerPosition.BottomLeft:
                        float leftEdgeBottom = margin;
                        float bottomYLeft = 0.02f + (i * (timerHeight + spacing));
                        anchorMin = $"{leftEdgeBottom} {bottomYLeft}";
                        anchorMax = $"{leftEdgeBottom + width} {bottomYLeft + timerHeight}";
                        break;
                    default:
                        float defaultRightEdge = 1.0f - margin;
                        anchorMin = $"{defaultRightEdge - width} {currentY - timerHeight}";
                        anchorMax = $"{defaultRightEdge} {currentY}";
                        break;
                }
                
                elements.Add(new CuiElement
                {
                    Parent = "Hud",
                    Components = {
                        new CuiImageComponent { 
                            Color = timer.BackgroundColor
                        },
                        new CuiRectTransformComponent {
                            AnchorMin = anchorMin,
                            AnchorMax = anchorMax
                        }
                    },
                    Name = containerName
                });

                elements.Add(new CuiElement
                {
                    Parent = containerName,
                    Components = {
                        new CuiTextComponent { 
                            Text = displayText,
                            FontSize = config.FontSize,
                            Align = TextAnchor.MiddleCenter,
                            Color = timer.TextColor
                        },
                        new CuiRectTransformComponent {
                            AnchorMin = "0 0",
                            AnchorMax = "1 1"
                        }
                    },
                    Name = $"Timer_Text_{i}"
                });
            }

            if (elements.Count > 0)
            {
                CuiHelper.AddUi(player, elements);
                playersWithUI.Add(player);
                playerUiSnapshots[player.userID] = BuildPlayerSnapshot(player);
            }
            else
            {
                playerUiSnapshots.Remove(player.userID);
            }
        }

        private void UpdateTimerUI(BasePlayer player)
        {
            if (player == null || !player.IsConnected) return;
            
            CreateTimerUI(player);
        }

        private void DestroyTimerUI(BasePlayer player)
        {
            if (player == null) return;

            int count = Math.Max(MAX_TIMERS, activeTimers.Count);
            for (int i = 0; i < count; i++)
            {
                CuiHelper.DestroyUi(player, $"Timer_Container_{i}");
                CuiHelper.DestroyUi(player, $"Timer_Text_{i}");
            }
            playerUiSnapshots.Remove(player.userID);
        }

        private void DestroyAllUIs(BasePlayer player)
        {
            if (player == null) return;
            
            CuiHelper.DestroyUi(player, TIMERS_UI_PANEL);
            playersWithTimersUI.Remove(player);
            
            CuiHelper.DestroyUi(player, "TimerCreationUI");
            
            CuiHelper.DestroyUi(player, "GroupsOverlay");

            playersInSettings.Remove(player.userID);
        }

        private string BuildPlayerSnapshot(BasePlayer player)
        {
            if (player == null) return string.Empty;

            var visibleTimers = activeTimers
                .Where(t => t.State == TimerState.Active && t.ShouldShowToPlayer(player, permission))
                .Select(t => $"{t.Id}:{t.GetTimeString()}")
                .ToList();

            if (visibleTimers.Count == 0)
                return string.Empty;

            return string.Join("|", visibleTimers);
        }

        private void ShowSettingsUI(BasePlayer player)
        {
            if (player == null || !player.IsConnected) return;

            DestroyAllUIs(player);

            var elements = new CuiElementContainer();

            elements.Add(new CuiPanel
            {
                RectTransform = { AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-400 -250", OffsetMax = "400 250" },
                Image = { Color = "0.1 0.1 0.1 0.98" },
                CursorEnabled = true
            }, "Overlay", TIMERS_UI_PANEL);

            elements.Add(new CuiPanel
            {
                RectTransform = { AnchorMin = "0 0.9", AnchorMax = "1 1" },
                Image = { Color = "0.8 0.8 0.8 0.2" }
            }, TIMERS_UI_PANEL, "Header");

            elements.Add(new CuiLabel
            {
                RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
                Text = { Text = "TIMER SETTINGS", FontSize = 20, Font = "robotocondensed-bold.ttf", Align = TextAnchor.MiddleCenter }
            }, "Header");

            elements.Add(new CuiButton
            {
                RectTransform = { AnchorMin = "0.92 0.92", AnchorMax = "0.98 0.98" },
                Button = { Command = "timer.close", Color = "0.8 0.2 0.2 0.8" },
                Text = { Text = "✕", FontSize = 14, Align = TextAnchor.MiddleCenter }
            }, TIMERS_UI_PANEL);

            elements.Add(new CuiButton
            {
                RectTransform = { AnchorMin = "0.82 0.92", AnchorMax = "0.90 0.98" },
                Button = { Command = "timer.settings.back", Color = "0.2 0.6 0.8 0.8" },
                Text = { Text = "BACK", FontSize = 14, Align = TextAnchor.MiddleCenter }
            }, TIMERS_UI_PANEL);

            elements.Add(new CuiLabel
            {
                RectTransform = { AnchorMin = "0.1 0.75", AnchorMax = "0.9 0.85" },
                Text = { Text = "Timer Position:", FontSize = 18, Align = TextAnchor.MiddleCenter }
            }, TIMERS_UI_PANEL);

            float buttonWidth = 0.35f;
            float buttonHeight = 0.12f;
            float spacingX = 0.1f;
            float spacingY = 0.08f;

            var positions = new[]
            {
                new { Position = TimerPosition.TopLeft, Text = "TOP LEFT", X = 0.1f, Y = 0.55f },
                new { Position = TimerPosition.TopRight, Text = "TOP RIGHT", X = 0.55f, Y = 0.55f },
                new { Position = TimerPosition.BottomLeft, Text = "BOTTOM LEFT", X = 0.1f, Y = 0.35f },
                new { Position = TimerPosition.BottomRight, Text = "BOTTOM RIGHT", X = 0.55f, Y = 0.35f }
            };

            foreach (var pos in positions)
            {
                bool isSelected = config.TimerPosition == pos.Position;
                string buttonColor = isSelected ? "0.2 0.8 0.2 0.8" : "0.2 0.6 0.8 0.8";

                elements.Add(new CuiButton
                {
                    RectTransform = { 
                        AnchorMin = $"{pos.X} {pos.Y}", 
                        AnchorMax = $"{pos.X + buttonWidth} {pos.Y + buttonHeight}" 
                    },
                    Button = { Command = $"timer.settings.position {pos.Position}", Color = buttonColor },
                    Text = { Text = pos.Text, FontSize = 14, Align = TextAnchor.MiddleCenter }
                }, TIMERS_UI_PANEL);

                if (isSelected)
                {
                    elements.Add(new CuiLabel
                    {
                        RectTransform = { 
                            AnchorMin = $"{pos.X} {pos.Y - 0.05f}", 
                            AnchorMax = $"{pos.X + buttonWidth} {pos.Y}" 
                        },
                        Text = { Text = "✓ CURRENT", FontSize = 12, Align = TextAnchor.MiddleCenter, Color = "0.2 0.8 0.2 1" }
                    }, TIMERS_UI_PANEL);
                }
            }

            CuiHelper.AddUi(player, elements);
            playersWithTimersUI.Add(player);
        }

        private void CreateTimersUI(BasePlayer player)
        {
            if (player == null || !player.IsConnected) return;

            DestroyAllUIs(player);

            var elements = new CuiElementContainer();

            elements.Add(new CuiPanel
            {
                RectTransform = { AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-400 -250", OffsetMax = "400 250" },
                Image = { Color = "0.1 0.1 0.1 0.98" },
                CursorEnabled = true
            }, "Overlay", TIMERS_UI_PANEL);

            elements.Add(new CuiPanel
            {
                RectTransform = { AnchorMin = "0 0.9", AnchorMax = "1 1" },
                Image = { Color = "0.8 0.8 0.8 0.2" }
            }, TIMERS_UI_PANEL, "Header");

            elements.Add(new CuiLabel
            {
                RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
                Text = { Text = "TIMERS", FontSize = 20, Font = "robotocondensed-bold.ttf", Align = TextAnchor.MiddleCenter }
            }, "Header");

            elements.Add(new CuiPanel
            {
                RectTransform = { AnchorMin = "0 0.82", AnchorMax = "1 0.88" },
                Image = { Color = "0.1 0.1 0.1 0.8" }
            }, TIMERS_UI_PANEL, "NavMenu");

            elements.Add(new CuiButton
            {
                RectTransform = { AnchorMin = "0.92 0.92", AnchorMax = "0.98 0.98" },
                Button = { Command = "timer.close", Color = "0.8 0.2 0.2 0.8" },
                Text = { Text = "✕", FontSize = 14, Align = TextAnchor.MiddleCenter }
            }, TIMERS_UI_PANEL);

            elements.Add(new CuiButton
            {
                RectTransform = { AnchorMin = "0.82 0.92", AnchorMax = "0.90 0.98" },
                Button = { Command = "timer.settings", Color = "0.2 0.6 0.8 0.8" },
                Text = { Text = "SETTINGS", FontSize = 14, Align = TextAnchor.MiddleCenter }
            }, TIMERS_UI_PANEL);

            if (!activeTimers.Any())
            {
                elements.Add(new CuiLabel
                {
                    RectTransform = { AnchorMin = "0.1 0.4", AnchorMax = "0.9 0.6" },
                    Text = { Text = "No active timers", FontSize = 16, Align = TextAnchor.MiddleCenter }
                }, TIMERS_UI_PANEL);
            }
            else
            {
                float startY = 0.75f;
                float itemHeight = 0.08f;
                float spacing = 0.01f;

                for (int i = 0; i < activeTimers.Count; i++)
                {
                    var timer = activeTimers[i];
                    float currentY = startY - (i * (itemHeight + spacing));
                    string containerName = $"TimerItem_{i}";

                    elements.Add(new CuiPanel
                    {
                        RectTransform = { AnchorMin = $"0.05 {currentY - itemHeight}", AnchorMax = $"0.95 {currentY}" },
                        Image = { Color = timer.ShouldShowToPlayer(player, permission) ? "0.15 0.15 0.15 1" : "0.12 0.12 0.12 1" }
                    }, TIMERS_UI_PANEL, containerName);

                    string timerInfo = timer.GetTimeString();
                    if (!timer.IsGlobal)
                    {
                        string targetInfo = "";
                        if (timer.TargetGroups.Count > 0)
                            targetInfo += $" [Groups: {string.Join(", ", timer.TargetGroups)}]";
                        if (timer.TargetSteamIds.Count > 0)
                            targetInfo += $" [{timer.TargetSteamIds.Count} players]";
                        timerInfo += targetInfo;
                    }

                    string stateText = "";
                    switch (timer.State)
                    {
                        case TimerState.Active:
                            stateText = " [ACTIVE]";
                            break;
                        case TimerState.Paused:
                            stateText = " [PAUSED]";
                            break;
                        case TimerState.Inactive:
                            stateText = " [INACTIVE]";
                            break;
                    }
                    timerInfo += stateText;

                    elements.Add(new CuiLabel
                    {
                        RectTransform = { AnchorMin = "0.02 0", AnchorMax = "0.55 1" },
                        Text = { 
                            Text = timerInfo,
                            FontSize = 14,
                            Align = TextAnchor.MiddleLeft,
                            Color = timer.ShouldShowToPlayer(player, permission) ? "1 1 1 1" : "0.7 0.7 0.7 1"
                        }
                    }, containerName);

                    elements.Add(new CuiButton
                    {
                        RectTransform = { AnchorMin = "0.57 0.2", AnchorMax = "0.68 0.8" },
                        Button = { Command = $"timer.edit {timer.Id}", Color = "0.2 0.6 0.8 0.9" },
                        Text = { Text = "EDIT", FontSize = 11, Align = TextAnchor.MiddleCenter }
                    }, containerName);

                    if (timer.State == TimerState.Active)
                    {
                        elements.Add(new CuiButton
                        {
                            RectTransform = { AnchorMin = "0.70 0.2", AnchorMax = "0.81 0.8" },
                            Button = { Command = $"timer.pause {timer.Id}", Color = "0.8 0.6 0.2 0.9" },
                            Text = { Text = "⏸ PAUSE", FontSize = 11, Align = TextAnchor.MiddleCenter }
                        }, containerName);
                    }
                    else
                    {
                        elements.Add(new CuiButton
                        {
                            RectTransform = { AnchorMin = "0.70 0.2", AnchorMax = "0.81 0.8" },
                            Button = { Command = $"timer.play {timer.Id}", Color = "0.2 0.8 0.2 0.9" },
                            Text = { Text = "▶ PLAY", FontSize = 11, Align = TextAnchor.MiddleCenter }
                        }, containerName);
                    }

                    elements.Add(new CuiButton
                    {
                        RectTransform = { AnchorMin = "0.83 0.2", AnchorMax = "0.98 0.8" },
                        Button = { Command = $"timer.stop {timer.Id}", Color = "0.7 0.2 0.2 0.9" },
                        Text = { Text = "STOP", FontSize = 12, Align = TextAnchor.MiddleCenter }
                    }, containerName);
                }
            }

            elements.Add(new CuiButton
            {
                RectTransform = { AnchorMin = "0.35 0.1", AnchorMax = "0.65 0.15" },
                Button = { Command = "timer.create", Color = "0.2 0.7 0.2 0.9" },
                Text = { Text = "CREATE TIMER", FontSize = 14, Align = TextAnchor.MiddleCenter }
            }, TIMERS_UI_PANEL);

            CuiHelper.AddUi(player, elements);
            playersWithTimersUI.Add(player);
        }

        [ConsoleCommand("timer.close")]
        private void TimerCloseCommand(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            if (player == null) return;
            
            DestroyAllUIs(player);
        }

        [ConsoleCommand("timer.create")]
        private void TimerCreateCommand(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            if (player == null) return;
            
            ShowTimerCreationUI(player);
        }

        private void ShowTimerCreationUI(BasePlayer player)
        {
            if (player == null || !player.IsConnected) return;

            DestroyAllUIs(player);

            if (!tempTimerData.ContainsKey(player.userID))
                tempTimerData[player.userID] = new TimerCreationData();

            var currentData = tempTimerData[player.userID];
            string titleText = currentData.IsEditing ? "EDIT TIMER" : "CREATE TIMER";
            string actionText = currentData.IsEditing ? "SAVE TIMER" : "CREATE TIMER";

            var elements = new CuiElementContainer();

            elements.Add(new CuiPanel
            {
                RectTransform = { AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-400 -250", OffsetMax = "400 250" },
                Image = { Color = "0.1 0.1 0.1 0.98" },
                CursorEnabled = true,
                KeyboardEnabled = true
            }, "Overla
// ... [Code preview truncated due to length. Full source in attached file] ...

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