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

FREE NewsBroadcaster : 3.6.0 3.6.0

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Oxide.Core;
using Oxide.Game.Rust.Cui;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("NewsBroadcaster", "YourName", "3.6.0")]
    [Description("Modern popup news broadcaster with sleek pop-up design and advanced text scaling")]
    public class NewsBroadcaster : RustPlugin
    {
        private const string PanelName = "NewsBroadcasterUI";
        private const string HistoryPanel = "NewsHistoryUI";
        private const string BackdropName = "NewsBackdropUI";
        private const string DataFile = "NewsBroadcaster_Data";

        private ConfigData config;
        private readonly List<Announcement> announcements = new List<Announcement>();
        private readonly Dictionary<ulong, HashSet<string>> playerUIs = new Dictionary<ulong, HashSet<string>>();
        private readonly Dictionary<ulong, Timer> autoCloseTimers = new Dictionary<ulong, Timer>();

        #region Data
        class Announcement
        {
            public string Title;
            public string ImageUrl;
            public string Text;
            public string Date;
            public string Author;
            public AnnouncementType Type;
            public int Priority; // 1=Low, 2=Normal, 3=High, 4=Critical
        }

        enum AnnouncementType
        {
            Info,
            Warning,
            Alert,
            Update,
            Event
        }
        #endregion

        #region Enhanced Config
        class ConfigData
        {
            public GeneralSettings General { get; set; } = new GeneralSettings();
            public UISettings UI { get; set; } = new UISettings();
            public TextScalingSettings TextScaling { get; set; } = new TextScalingSettings();
            public SoundSettings Sounds { get; set; } = new SoundSettings();
            public AnimationSettings Animations { get; set; } = new AnimationSettings();
            public AdvancedSettings Advanced { get; set; } = new AdvancedSettings();
        }

        class GeneralSettings
        {
            public int AnnouncementsPerPage { get; set; } = 4;
            public int MaxStoredAnnouncements { get; set; } = 100;
            public bool EnableDebugMode { get; set; } = false;
            public List<string> AllowedCommands { get; set; } = new List<string> { "news", "announcements", "updates" };
        }

        class UISettings
        {
            public PositionSettings Position { get; set; } = new PositionSettings();
            public BrandingSettings Branding { get; set; } = new BrandingSettings();
            public BehaviorSettings Behavior { get; set; } = new BehaviorSettings();
            public ColorSettings Colors { get; set; } = new ColorSettings();
            public LayoutSettings Layout { get; set; } = new LayoutSettings();
        }

        class TextScalingSettings
        {
            public float GlobalTextScale { get; set; } = 1.0f; // Global multiplier for all text
            public FontSizeSettings FontSizes { get; set; } = new FontSizeSettings();
            public ResponsiveSettings Responsive { get; set; } = new ResponsiveSettings();
            public AccessibilitySettings Accessibility { get; set; } = new AccessibilitySettings();
        }

        class FontSizeSettings
        {
            public int BaseTitleSize { get; set; } = 18;
            public int BaseHeaderSize { get; set; } = 14;
            public int BaseParagraphSize { get; set; } = 12;
            public int BaseSmallSize { get; set; } = 10;
            public int BaseTinySize { get; set; } = 8;
            
            // Font family preferences
            public string PrimaryFont { get; set; } = "robotocondensed-bold.ttf";
            public string SecondaryFont { get; set; } = "robotocondensed-regular.ttf";
            public string MonospaceFont { get; set; } = "droidsansmono.ttf";
        }

        class ResponsiveSettings
        {
            public bool EnableResponsiveText { get; set; } = true;
            public Dictionary<string, float> ScreenSizeMultipliers { get; set; } = new Dictionary<string, float>
            {
                ["small"] = 0.8f,   // For smaller UI scales
                ["normal"] = 1.0f,  // Default
                ["large"] = 1.2f,   // For better readability
                ["xlarge"] = 1.4f   // For accessibility
            };
            public string DefaultScreenSize { get; set; } = "normal";
        }

        class AccessibilitySettings
        {
            public bool HighContrastMode { get; set; } = false;
            public bool BoldText { get; set; } = false;
            public float LineSpacing { get; set; } = 1.0f;
            public int MaxTextWidth { get; set; } = 50; // Characters per line
            public bool UseAccessibleColors { get; set; } = false;
        }

        class LayoutSettings
        {
            public PopupSizePresets PopupSize { get; set; } = PopupSizePresets.Medium;
            public bool ShowImagePreviews { get; set; } = true;
            public bool ShowTypeIcons { get; set; } = true;
            public bool ShowPriorityIndicators { get; set; } = true;
            public float ContentPadding { get; set; } = 0.02f;
            public float ElementSpacing { get; set; } = 0.01f;
        }

        enum PopupSizePresets
        {
            Compact,    // 0.35-0.65
            Medium,     // 0.3-0.7
            Large,      // 0.25-0.75
            FullWidth   // 0.1-0.9
        }

        class PositionSettings
        {
            public string Preset { get; set; } = "Center";
            public string CustomAnchorMin { get; set; } = "0.3 0.3";
            public string CustomAnchorMax { get; set; } = "0.7 0.7";
            public Dictionary<string, PositionPreset> Presets { get; set; } = new Dictionary<string, PositionPreset>
            {
                ["Center"] = new PositionPreset { AnchorMin = "0.3 0.3", AnchorMax = "0.7 0.7" },
                ["TopCenter"] = new PositionPreset { AnchorMin = "0.3 0.7", AnchorMax = "0.7 0.95" },
                ["BottomCenter"] = new PositionPreset { AnchorMin = "0.3 0.05", AnchorMax = "0.7 0.3" },
                ["LeftSide"] = new PositionPreset { AnchorMin = "0.05 0.3", AnchorMax = "0.45 0.7" },
                ["RightSide"] = new PositionPreset { AnchorMin = "0.55 0.3", AnchorMax = "0.95 0.7" },
                ["FullScreen"] = new PositionPreset { AnchorMin = "0.1 0.1", AnchorMax = "0.9 0.9" }
            };
        }

        class PositionPreset
        {
            public string AnchorMin { get; set; }
            public string AnchorMax { get; set; }
        }

        class BrandingSettings
        {
            public string LogoUrl { get; set; } = "";
            public string HeaderImageUrl { get; set; } = "";
            public string ServerName { get; set; } = "Server News";
            public string WatermarkText { get; set; } = "";
            public bool ShowServerLogo { get; set; } = true;
            public LogoPosition LogoPosition { get; set; } = LogoPosition.HeaderLeft;
        }

        enum LogoPosition
        {
            HeaderLeft,
            HeaderCenter,
            HeaderRight,
            BottomLeft,
            BottomRight
        }

        class BehaviorSettings
        {
            public bool AutoCloseEnabled { get; set; } = true;
            public int AutoCloseSeconds { get; set; } = 15;
            public bool ShowCloseTimer { get; set; } = true;
            public bool EnableSounds { get; set; } = true;
            public bool EnableAnimations { get; set; } = true;
            public bool BlockGameInput { get; set; } = false;
            public bool ShowOnPlayerConnect { get; set; } = false;
            public bool RememberPlayerPreferences { get; set; } = false;
        }

        class ColorSettings
        {
            // Base UI colors with better organization
            public UIPanelColors Panels { get; set; } = new UIPanelColors();
            public TextColors Text { get; set; } = new TextColors();
            public ButtonColors Buttons { get; set; } = new ButtonColors();
            public PriorityColors Priority { get; set; } = new PriorityColors();
            public AccessibilityColors Accessibility { get; set; } = new AccessibilityColors();
        }

        class UIPanelColors
        {
            public string Backdrop { get; set; } = "0 0 0 0.6";
            public string PopupPanel { get; set; } = "0.1 0.1 0.12 0.95";
            public string PopupBorder { get; set; } = "0.95 0.5 0.2 0.8";
            public string HeaderGradient { get; set; } = "0.2 0.2 0.25 0.9";
            public string ContentPanel { get; set; } = "0.08 0.08 0.1 0.85";
            public string HistoryPanel { get; set; } = "0.12 0.12 0.15 0.95";
            public string CardPanel { get; set; } = "0.15 0.15 0.18 0.9";
            public string AccentBar { get; set; } = "0.95 0.5 0.2 1";
        }

        class TextColors
        {
            public string TitleText { get; set; } = "1 1 1 1";
            public string BodyText { get; set; } = "0.9 0.9 0.9 1";
            public string MutedText { get; set; } = "0.7 0.7 0.7 1";
            public string AccentText { get; set; } = "0.95 0.6 0.3 1";
            public string ErrorText { get; set; } = "0.9 0.3 0.3 1";
            public string SuccessText { get; set; } = "0.3 0.9 0.3 1";
            public string WarningText { get; set; } = "0.9 0.9 0.3 1";
        }

        class ButtonColors
        {
            public string PrimaryButton { get; set; } = "0.25 0.6 0.9 0.9";
            public string SecondaryButton { get; set; } = "0.5 0.5 0.5 0.8";
            public string DangerButton { get; set; } = "0.8 0.3 0.3 0.9";
            public string CloseButton { get; set; } = "0.9 0.4 0.4 0.95";
            public string ButtonText { get; set; } = "1 1 1 1";
            public string ButtonHover { get; set; } = "0.3 0.7 1 0.95";
        }

        class PriorityColors
        {
            public string InfoColor { get; set; } = "0.2 0.7 0.9 1";
            public string WarningColor { get; set; } = "0.9 0.7 0.2 1";
            public string AlertColor { get; set; } = "0.9 0.3 0.2 1";
            public string EventColor { get; set; } = "0.6 0.3 0.9 1";
            public string UpdateColor { get; set; } = "0.3 0.9 0.6 1";
        }

        class AccessibilityColors
        {
            public bool UseHighContrast { get; set; } = false;
            public string HighContrastBackground { get; set; } = "0 0 0 1";
            public string HighContrastText { get; set; } = "1 1 1 1";
            public string HighContrastAccent { get; set; } = "1 1 0 1";
        }

        class SoundSettings
        {
            public string PopupSound { get; set; } = "assets/bundled/prefabs/fx/notice/item.select.fx.prefab";
            public string CloseSound { get; set; } = "assets/bundled/prefabs/fx/notice/item.deselect.fx.prefab";
            public string AlertSound { get; set; } = "assets/bundled/prefabs/fx/explosions/explosion_01.prefab";
            public string WarningSound { get; set; } = "assets/bundled/prefabs/fx/notice/item.notice.fx.prefab";
            public float SoundVolume { get; set; } = 1.0f;
            public bool EnableSpatialAudio { get; set; } = false;
        }

        class AnimationSettings
        {
            public float SlideInDuration { get; set; } = 0.3f;
            public float FadeInDuration { get; set; } = 0.2f;
            public float SlideOutDuration { get; set; } = 0.2f;
            public string AnimationType { get; set; } = "SlideFromTop"; // SlideFromTop, SlideFromBottom, FadeIn, ScaleIn
            public bool EnableTransitions { get; set; } = true;
        }

        class AdvancedSettings
        {
            public int UIUpdateRate { get; set; } = 30; // Updates per second for animations
            public bool UseAdvancedRendering { get; set; } = false;
            public int MaxConcurrentPopups { get; set; } = 3;
            public bool EnablePerformanceMode { get; set; } = false;
            public Dictionary<string, object> CustomSettings { get; set; } = new Dictionary<string, object>();
        }
        #endregion

        #region Config I/O
        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<ConfigData>();
                if (config == null) throw new Exception("Null config");
                EnsureConfigDefaults();
            }
            catch
            {
                PrintError("[NewsBroadcaster] Invalid config.Creating defaults.");
                config = new ConfigData();
            }
            SaveConfig();
        }

        private void EnsureConfigDefaults()
        {
            // Ensure all nested objects are initialized
            if (config.General == null) config.General = new GeneralSettings();
            if (config.UI == null) config.UI = new UISettings();
            if (config.TextScaling == null) config.TextScaling = new TextScalingSettings();
            if (config.Sounds == null) config.Sounds = new SoundSettings();
            if (config.Animations == null) config.Animations = new AnimationSettings();
            if (config.Advanced == null) config.Advanced = new AdvancedSettings();

            // UI sub-objects
            if (config.UI.Position == null) config.UI.Position = new PositionSettings();
            if (config.UI.Branding == null) config.UI.Branding = new BrandingSettings();
            if (config.UI.Behavior == null) config.UI.Behavior = new BehaviorSettings();
            if (config.UI.Colors == null) config.UI.Colors = new ColorSettings();
            if (config.UI.Layout == null) config.UI.Layout = new LayoutSettings();

            // TextScaling sub-objects
            if (config.TextScaling.FontSizes == null) config.TextScaling.FontSizes = new FontSizeSettings();
            if (config.TextScaling.Responsive == null) config.TextScaling.Responsive = new ResponsiveSettings();
            if (config.TextScaling.Accessibility == null) config.TextScaling.Accessibility = new AccessibilitySettings();

            // Color sub-objects
            if (config.UI.Colors.Panels == null) config.UI.Colors.Panels = new UIPanelColors();
            if (config.UI.Colors.Text == null) config.UI.Colors.Text = new TextColors();
            if (config.UI.Colors.Buttons == null) config.UI.Colors.Buttons = new ButtonColors();
            if (config.UI.Colors.Priority == null) config.UI.Colors.Priority = new PriorityColors();
            if (config.UI.Colors.Accessibility == null) config.UI.Colors.Accessibility = new AccessibilityColors();

            // Initialize dictionaries if null
            if (config.UI.Position.Presets == null) 
            {
                config.UI.Position.Presets = new Dictionary<string, PositionPreset>
                {
                    ["Center"] = new PositionPreset { AnchorMin = "0.3 0.3", AnchorMax = "0.7 0.7" },
                    ["TopCenter"] = new PositionPreset { AnchorMin = "0.3 0.7", AnchorMax = "0.7 0.95" },
                    ["BottomCenter"] = new PositionPreset { AnchorMin = "0.3 0.05", AnchorMax = "0.7 0.3" }
                };
            }

            if (config.TextScaling.Responsive.ScreenSizeMultipliers == null)
            {
                config.TextScaling.Responsive.ScreenSizeMultipliers = new Dictionary<string, float>
                {
                    ["small"] = 0.8f,
                    ["normal"] = 1.0f,
                    ["large"] = 1.2f,
                    ["xlarge"] = 1.4f
                };
            }
        }

        protected override void SaveConfig() => Config.WriteObject(config, true);
        protected override void LoadDefaultConfig() => config = new ConfigData();
        #endregion

        #region Text Scaling Helpers
        private int GetScaledFontSize(FontType fontType, BasePlayer player = null)
        {
            var fontSizes = config.TextScaling.FontSizes;
            var globalScale = config.TextScaling.GlobalTextScale;
            var responsiveScale = GetResponsiveScale(player);
            var accessibilityScale = config.TextScaling.Accessibility.BoldText ? 1.1f : 1.0f;
            
            int baseSize = fontType switch
            {
                FontType.Title => fontSizes.BaseTitleSize,
                FontType.Header => fontSizes.BaseHeaderSize,
                FontType.Paragraph => fontSizes.BaseParagraphSize,
                FontType.Small => fontSizes.BaseSmallSize,
                FontType.Tiny => fontSizes.BaseTinySize,
                _ => fontSizes.BaseParagraphSize
            };

            float finalScale = globalScale * responsiveScale * accessibilityScale;
            return Mathf.RoundToInt(baseSize * finalScale);
        }

        private float GetResponsiveScale(BasePlayer player = null)
        {
            if (!config.TextScaling.Responsive.EnableResponsiveText)
                return 1.0f;

            var screenSize = config.TextScaling.Responsive.DefaultScreenSize;
            
            // You could implement player-specific scaling here based on their preferences
            // For now, we'll use the default screen size
            
            if (config.TextScaling.Responsive.ScreenSizeMultipliers.TryGetValue(screenSize, out float multiplier))
                return multiplier;
                
            return 1.0f;
        }

        private string GetScaledFont(FontStyle style)
        {
            var fonts = config.TextScaling.FontSizes;
            var accessibility = config.TextScaling.Accessibility;
            
            // Use bold fonts if accessibility mode is enabled
            if (accessibility.BoldText && style == FontStyle.Regular)
                style = FontStyle.Bold;

            return style switch
            {
                FontStyle.Bold => fonts.PrimaryFont,
                FontStyle.Regular => fonts.SecondaryFont,
                FontStyle.Monospace => fonts.MonospaceFont,
                _ => fonts.SecondaryFont
            };
        }

        private string GetAccessibleTextColor(string defaultColor)
        {
            if (!config.UI.Colors.Accessibility.UseHighContrast)
                return defaultColor;
                
            return config.UI.Colors.Accessibility.HighContrastText;
        }

        private string GetPositionAnchors(string type)
        {
            var position = config.UI.Position;
            var layout = config.UI.Layout;
            
            if (type == "popup")
            {
                // Handle popup size presets
                return layout.PopupSize switch
                {
                    PopupSizePresets.Compact => "0.35 0.35|0.65 0.65",
                    PopupSizePresets.Medium => "0.3 0.3|0.7 0.7",
                    PopupSizePresets.Large => "0.25 0.25|0.75 0.75",
                    PopupSizePresets.FullWidth => "0.1 0.1|0.9 0.9",
                    _ => "0.3 0.3|0.7 0.7"
                };
            }
            
            // Handle position presets
            if (position.Presets.TryGetValue(position.Preset, out var preset))
                return $"{preset.AnchorMin}|{preset.AnchorMax}";
                
            return $"{position.CustomAnchorMin}|{position.CustomAnchorMax}";
        }

        enum FontType
        {
            Title,
            Header,
            Paragraph,
            Small,
            Tiny
        }

        enum FontStyle
        {
            Regular,
            Bold,
            Monospace
        }
        #endregion

        #region Data File
        private void SaveAnnouncements() 
        {
            // Limit stored announcements to prevent excessive memory usage
            while (announcements.Count > config.General.MaxStoredAnnouncements)
            {
                announcements.RemoveAt(0);
            }
            Interface.Oxide.DataFileSystem.WriteObject(DataFile, announcements);
        }

        private void LoadAnnouncements()
        {
            announcements.Clear();
            var list = Interface.Oxide.DataFileSystem.ReadObject<List<Announcement>>(DataFile);
            if (list != null) announcements.AddRange(list);
        }
        #endregion

        #region Hooks
        void Init() 
        {
            LoadAnnouncements();
            
            // Register additional chat commands from config
            foreach (var command in config.General.AllowedCommands)
            {
                cmd.AddChatCommand(command, this, "CmdNewsHistory");
            }
        }

        void OnPlayerDisconnected(BasePlayer player, string reason)
        {
            if (player != null)
            {
                playerUIs.Remove(player.userID);
                if (autoCloseTimers.ContainsKey(player.userID))
                {
                    autoCloseTimers[player.userID]?.Destroy();
                    autoCloseTimers.Remove(player.userID);
                }
            }
        }

        void OnPlayerConnected(BasePlayer player)
        {
            if (config.UI.Behavior.ShowOnPlayerConnect && announcements.Count > 0)
            {
                timer.Once(2f, () => 
                {
                    if (player?.IsConnected == true)
                    {
                        var latestAnnouncement = announcements.OrderByDescending(a => a.Date).FirstOrDefault();
                        if (latestAnnouncement != null)
                            ShowPopupAnnouncement(player, latestAnnouncement);
                    }
                });
            }
        }
        #endregion

        #region Commands
        [ConsoleCommand("news.show")]
        private void CmdNewsShow(ConsoleSystem.Arg arg)
        {
            if (!IsConsoleOrAdmin(arg)) return;

            if (arg.Args == null || arg.Args.Length < 3)
            {
                SendReply(arg, "Usage: news.show \"Title\" \"ImageURL or -\" \"Text\" [type] [priority] [author]");
                SendReply(arg, "Types: info, warning, alert, update, event");
                SendReply(arg, "Priority: 1-4 (1=Low, 2=Normal, 3=High, 4=Critical)");
                return;
            }

            string title = arg.GetString(0) ?? "";
            string img = arg.GetString(1);
            if (img == "-") img = string.Empty;
            string text = (arg.GetString(2) ?? "").Replace("\\n", "\n");
            
            // Parse optional parameters
            var typeStr = arg.GetString(3, "info").ToLowerInvariant();
            var priority = arg.GetInt(4, 2);
            var author = arg.GetString(5, "Server Admin");

            if (!Enum.TryParse(typeStr, true, out AnnouncementType type))
                type = AnnouncementType.Info;

            priority = Mathf.Clamp(priority, 1, 4);

            var ann = new Announcement
            {
                Title = title,
                ImageUrl = img,
                Text = text,
                Date = DateTime.Now.ToString("yyyy-MM-dd HH:mm"),
                Author = author,
                Type = type,
                Priority = priority
            };

            announcements.Add(ann);
            SaveAnnouncements();

            foreach (var player in BasePlayer.activePlayerList)
                ShowPopupAnnouncement(player, ann);
                
            SendReply(arg, $"Announcement '{title}' sent to {BasePlayer.activePlayerList.Count} players.");
        }

        [ChatCommand("news")]
        private void CmdNewsHistory(BasePlayer player, string cmd, string[] args)
        {
            if (announcements.Count == 0)
            {
                SendReply(player, "No announcements available.");
                return;
            }
            ShowHistory(player, 0);
        }

        [ConsoleCommand("news.page")]
        private void CmdNewsPage(ConsoleSystem.Arg arg)
        {
            var player = arg.Connection?.player as BasePlayer;
            if (player == null) return;
            int page = arg.GetInt(0, 0);
            ShowHistory(player, page);
        }

        [ConsoleCommand("news.view")]
        private void CmdNewsView(ConsoleSystem.Arg arg)
        {
            var player = arg.Connection?.player as BasePlayer;
            if (player == null) return;
            int index = arg.GetInt(0, -1);
            if (index < 0 || index >= announcements.Count) return;
            ShowPopupAnnouncement(player, announcements[index]);
        }

        [ConsoleCommand("news.clear")]
        private void CmdNewsClear(ConsoleSystem.Arg arg)
        {
            if (!IsConsoleOrAdmin(arg)) return;
            announcements.Clear();
            SaveAnnouncements();
            SendReply(arg, "All announcements have been cleared.");
        }

        [ConsoleCommand("news.close")]
        private void CmdNewsClose(ConsoleSystem.Arg arg)
        {
            var player = arg.Connection?.player as BasePlayer;
            if (player == null) return;
            CloseAllUI(player);
        }

        [ConsoleCommand("news.config")]
        private void CmdNewsConfig(ConsoleSystem.Arg arg)
        {
            if (!IsConsoleOrAdmin(arg)) return;
            
            if (arg.Args == null || arg.Args.Length < 2)
            {
                SendReply(arg, "Usage: news.config <setting> <value>");
                SendReply(arg, "Examples:");
                SendReply(arg, "news.config textscale 1.2");
                SendReply(arg, "news.config fontsize.title 20");
                SendReply(arg, "news.config position TopCenter");
                return;
            }

            string setting = arg.GetString(0).ToLowerInvariant();
            string value = arg.GetString(1);

            bool changed = false;

            switch (setting)
            {
                case "textscale":
                case "globalscale":
                    if (float.TryParse(value, out float scale))
                    {
                        config.TextScaling.GlobalTextScale = Mathf.Clamp(scale, 0.5f, 3.0f);
                        changed = true;
                    }
                    break;
                    
                case "fontsize.title":
                    if (int.TryParse(value, out int titleSize))
                    {
                        config.TextScaling.FontSizes.BaseTitleSize = Mathf.Clamp(titleSize, 10, 50);
                        changed = true;
                    }
                    break;
                    
                case "position":
                    if (config.UI.Position.Presets.ContainsKey(value))
                    {
                        config.UI.Position.Preset = value;
                        changed = true;
                    }
                    break;
                    
                case "popupsize":
                    if (Enum.TryParse<PopupSizePresets>(value, true, out var size))
                    {
                        config.UI.Layout.PopupSize = size;
                        changed = true;
                    }
                    break;
            }

            if (changed)
            {
                SaveConfig();
                SendReply(arg, $"Setting '{setting}' changed to '{value}'.Changes will apply to new announcements.");
            }
            else
            {
                SendReply(arg, $"Invalid setting or value.Setting: {setting}, Value: {value}");
            }
        }
        #endregion

        #region Enhanced Modern Popup UI
        private void ShowPopupAnnouncement(BasePlayer player, Announcement ann)
        {
            CloseAllUI(player);

            var colors = config.UI.Colors;
            var elements = new CuiElementContainer();

            // Play sound effect
            if (config.UI.Behavior.EnableSounds)
            {
                string soundToPlay = ann.Priority >= 4 ? config.Sounds.AlertSound : config.Sounds.PopupSound;
                Effect.server.Run(soundToPlay, player.transform.position);
            }

            // Backdrop with blur effect
            elements.Add(new CuiPanel
            {
                Image = { Color = GetAccessibleBackgroundColor(colors.Panels.Backdrop) },
                RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
                CursorEnabled = true
            }, "Overlay", BackdropName);

            // Get dynamic positioning based on config
            var anchors = GetPositionAnchors("popup").Split('|');
            string popupAnchorMin = anchors[0];
            string popupAnchorMax = anchors[1];

            // Modern popup container with scaled positioning
            string popupContainer = CuiHelper.GetGuid();
            elements.Add(new CuiPanel
            {
                Image = { Color = GetAccessibleBackgroundColor(colors.Panels.PopupPanel) },
                RectTransform = { AnchorMin = popupAnchorMin, AnchorMax = popupAnchorMax },
                CursorEnabled = true
            }, BackdropName, popupContainer);

            // Elegant border effect
            elements.Add(new CuiPanel
            {
                Image = { Color = colors.Panels.PopupBorder },
                RectTransform = { AnchorMin = "-0.002 -0.003", AnchorMax = "1.002 1.003" }
            }, popupContainer, CuiHelper.GetGuid());

            // Priority indicator bar (left side) - only show if enabled
            if (config.UI.Layout.ShowPriorityIndicators)
            {
                string priorityColor = GetPriorityColor(ann.Type, ann.Priority);
                elements.Add(new CuiPanel
                {
                    Image = { Color = priorityColor },
                    RectTransform = { AnchorMin = "0 0", AnchorMax = "0.008 1" }
                }, popupContainer, CuiHelper.GetGuid());
            }

            // Header section with gradient
            string headerSection = CuiHelper.GetGuid();
            elements.Add(new CuiPanel
            {
                Image = { Color = GetAccessibleBackgroundColor(colors.Panels.HeaderGradient) },
                RectTransform = { AnchorMin = "0.008 0.82", AnchorMax = "1 1" }
            }, popupContainer, headerSection);

            // Type badge - only show if enabled
            if (config.UI.Layout.ShowTypeIcons)
            {
                string priorityColor = GetPriorityColor(ann.Type, ann.Priority);
                string typeBadge = CuiHelper.GetGuid();
                elements.Add(new CuiPanel
                {
                    Image = { Color = priorityColor },
                    RectTransform = { AnchorMin = "0.02 0.15", AnchorMax = "0.25 0.85" }
                }, headerSection, typeBadge);

                elements.Add(new CuiLabel
                {
                    Text = { 
                        Text = ann.Type.ToString().ToUpper(), 
                        FontSize = GetScaledFontSize(FontType.Small, player), 
                        Align = TextAnchor.MiddleCenter, 
                        Color = GetAccessibleTextColor(colors.Text.TitleText),
                        Font = GetScaledFont(FontStyle.Bold)
                    },
                    RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" }
                }, typeBadge, CuiHelper.GetGuid());
            }

            // Title with server branding
            string serverName = !string.IsNullOrEmpty(config.UI.Branding.ServerName) ? config.UI.Branding.ServerName : "Server News";
            float titleStartX = config.UI.Layout.ShowTypeIcons ? 0.28f : 0.05f;
            
            elements.Add(new CuiLabel
            {
                Text = { 
                    Text = $"<color=#FFA500>{serverName}</color> • {SafeTrim(ann.Title, GetDynamicTextLength(35))}", 
                    FontSize = GetScaledFontSize(FontType.Title, player), 
                    Align = TextAnchor.MiddleLeft, 
                    Color = GetAccessibleTextColor(colors.Text.TitleText),
                    Font = GetScaledFont(FontStyle.Bold)
                },
                RectTransform = { AnchorMin = $"{titleStartX} 0", AnchorMax = "0.92 1" }
            }, headerSection, CuiHelper.GetGuid());

            // Close button (X) in top right
            elements.Add(new CuiButton
            {
                Button = { Color = colors.Buttons.CloseButton, Command = "news.close" },
                Text = { 
                    Text = "✕", 
                    FontSize = GetScaledFontSize(FontType.Header, player), 
                    Align = TextAnchor.MiddleCenter, 
                    Color = GetAccessibleTextColor(colors.Buttons.ButtonText),
                    Font = GetScaledFont(FontStyle.Bold)
                },
                RectTransform = { AnchorMin = "0.92 0.1", AnchorMax = "0.98 0.9" }
            }, headerSection, CuiHelper.GetGuid());

            // Content area with configurable padding
            string contentArea = CuiHelper.GetGuid();
            float padding = config.UI.Layout.ContentPadding;
            elements.Add(new CuiPanel
            {
                Image = { Color = GetAccessibleBackgroundColor(colors.Panels.ContentPanel) },
                RectTransform = { AnchorMin = $"{padding} 0.15", AnchorMax = $"{1-padding} 0.8" }
            }, popupContainer, contentArea);

            // Image handling (if provided and enabled)
            bool hasImage = !string.IsNullOrEmpty(ann.ImageUrl) && config.UI.Layout.ShowImagePreviews;
            if (hasImage)
            {
                elements.Add(new CuiElement
                {
                    Name = CuiHelper.GetGuid(),
                    Parent = contentArea,
                    Components =
                    {
                        new CuiRawImageComponent { Url = ann.ImageUrl },
                        new CuiRectTransformComponent { AnchorMin = "0.03 0.45", AnchorMax = "0.45 0.95" }
                    }
                });
            }

            // Main text content with advanced text scaling
            float textStartX = hasImage ? 0.48f : 0.05f;
            int textWidth = GetDynamicTextLength(hasImage ? 25 : 35);
            
            elements.Add(new CuiLabel
            {
                Text = { 
                    Text = WrapText(ann.Text ?? "No content provided.", textWidth), 
                    FontSize = GetScaledFontSize(FontType.Paragraph, player), 
                    Align = TextAnchor.UpperLeft, 
                    Color = GetAccessibleTextColor(colors.Text.BodyText),
                    Font = GetScaledFont(FontStyle.Regular)
                },
                RectTransform = { AnchorMin = $"{textStartX} 0.1", AnchorMax = "0.95 0.95" }
            }, contentArea, CuiHelper.GetGuid());

            // Bottom info bar
            string bottomBar = CuiHelper.GetGuid();
            elements.Add(new CuiPanel
            {
                Image = { Color = "0.05 0.05 0.05 0.8" },
                RectTransform = { AnchorMin = $"{padding} 0.02", AnchorMax = $"{1-padding} 0.13" }
            }, popupContainer, bottomBar);

            // Author info
            elements.Add(new CuiLabel
            {
                Text = { 
                    Text = $"By: {ann.Author ?? "Server Admin"}", 
                    FontSize = GetScaledFontSize(FontType.Small, player), 
                    Align = TextAnchor.MiddleLeft, 
                    Color = GetAccessibleTextColor(colors.Text.MutedText),
                    Font = GetScaledFont(FontStyle.Regular)
                },
                RectTransform = { AnchorMin = "0.03 0", AnchorMax = "0.4 1" }
            }, bottomBar, CuiHelper.GetGuid());

            // Date stamp
            elements.Add(new CuiLabel
            {
                Text = { 
                    Text = ann.Date ?? DateTime.Now.ToString("yyyy-MM-dd HH:mm"), 
                    FontSize = GetScaledFontSize(FontType.Small, player), 
                    Align = TextAnchor.MiddleCenter, 
                    Color = GetAccessibleTextColor(colors.Text.MutedText),
                    Font = GetScaledFont(FontStyle.Regular)
                },
                RectTransform = { AnchorMin = "0.4 0", AnchorMax = "0.7 1" }
            }, bottomBar, CuiHelper.GetGuid());

            // History button
            elements.Add(new CuiButton
            {
                Button = { Color = colors.Buttons.SecondaryButton, Command = "news.page 0" },
                Text = { 
                    Text = "History", 
                    FontSize = GetScaledFontSize(FontType.Small, player), 
                    Align = TextAnchor.MiddleCenter, 
                    Color = GetAccessibleTextColor(colors.Buttons.ButtonText),
                    Font = GetScaledFont(FontStyle.Regular)
                },
                RectTransform = { AnchorMin = "0.75 0.15", AnchorMax = "0.97 0.85" }
            }, bottomBar, CuiHelper.GetGuid());

            // Auto-close timer display
            if (config.UI.Behavior.ShowCloseTimer && config.UI.Behavior.AutoCloseEnabled)
            {
                StartAutoCloseTimer(player, config.UI.Behavior.AutoCloseSeconds);
            }

            CuiHelper.AddUi(player, elements);
            TrackUI(player, BackdropName);
            TrackUI(player, popupContainer);

            // Auto-close functionality
            if (config.UI.Behavior.AutoCloseEnabled)
            {
                if (autoCloseTimers.ContainsKey(player.userID))
                    autoCloseTimers[player.userID]?.Destroy();

                autoCloseTimers[player.userID] = timer.Once(config.UI.Behavior.AutoCloseSeconds, () =>
                {
                    if (player?.IsConnected == true) 
                    {
                        CloseAllUI(player);
                        if (config.UI.Behavior.EnableSounds)
                            Effect.server.Run(config.Sounds.CloseSound, player.transform.position);
                    }
                });
            }
        }

        private void StartAutoCloseTimer(BasePlayer player, int seconds)
        {
            // Enhanced timer with better scaling
            var timerElement = new CuiElementContainer();
            
            timerElement.Add(new CuiLabel
            {
                Text = { 
                    Text = $"Auto-close in {seconds}s", 
                    FontSize = GetScaledFontSize(FontType.Tiny, player), 
                    Align = TextAnchor.MiddleCenter, 
                    Color = GetAccessibleTextColor("0.7 0.7 0.7 0.8"),
                    Font = GetScaledFont(FontStyle.Regular)
                },
                RectTransform = { AnchorMin = "0.75 0.85", AnchorMax = "0.98 0.9" }
            }, BackdropName, "TimerDisplay");

            CuiHelper.AddUi(player, timerElement);
        }

        private string GetPriorityColor(AnnouncementType type, int priority)
        {
            var colors = config.UI.Colors.Priority;
            
            // Priority overrides type for critical alerts
            if (priority >= 4) return colors.AlertColor; // Critical
            if (priority >= 3) return colors.WarningColor; // High
            
            // Use type-based colors for normal/low priority
            return type switch
            {
                AnnouncementType.Alert => colors.AlertColor,
                AnnouncementType.Warning => colors.WarningColor,
                AnnouncementType.Event => colors.EventColor,
                AnnouncementType.Update => colors.UpdateColor,
                _ => colors.InfoColor
            };
        }

        private void ShowHistory(BasePlayer player, int page)
        {
            if (!HasUI(player, BackdropName))
            {
                var backdropElements = new CuiElementContainer();
                backdropElements.Add(new CuiPanel
                {
                    Image = { Color = GetAccessibleBackgroundColor(config.UI.Colors.Panels.Backdrop) },
                    RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
                    CursorEnabled = true
                }, "Overlay", BackdropName);
                CuiHelper.AddUi(player, backdropElements);
                TrackUI(player, BackdropName);
            }

            CuiHelper.DestroyUi(player, HistoryPanel);
            UntrackUI(player, HistoryPanel);

            var colors = config.UI.Colors;
            var elements = new CuiElementContainer();

            // History panel with dynamic sizing
            var historyAnchors = GetPositionAnchors("history").Split('|');
            if (historyAnchors.Length < 2)
                historyAnchors = new[] { "0.15 0.1", "0.85 0.9" };

            elements.Add(new CuiPanel
            {
                Image = { Color = GetAccessibleBackgroundColor(colors.Panels.HistoryPanel) },
                RectTransform = { AnchorMin = historyAnchors[0], AnchorMax = historyAnchors[1] },
                CursorEnabled = true
            }, BackdropName, HistoryPanel);

            // Header with close button
            string headerPanel = CuiHelper.GetGuid();
            elements.Add(new CuiPanel
            {
                Image = { Color = GetAccessibleBackgroundColor(colors.Panels.HeaderGradient) },
                RectTransform = { AnchorMin = "0 0.92", AnchorMax = "1 1" }
            }, HistoryPanel, headerPanel);

            // Logo in header (if enabled and provided)
            if (config.UI.Branding.ShowServerLogo && !string.IsNullOrEmpty(config.UI.Branding.LogoUrl))
            {
                var logoPos = GetLogoPosition();
                elements.Add(new CuiElement
                {
                    Name = CuiHelper.GetGuid(),
                    Parent = headerPanel,
                    Components =
                    {
                        new CuiRawImageComponent { Url = config.UI.Branding.LogoUrl },
                        new CuiRectTransformComponent { AnchorMin = logoPos.min, AnchorMax = logoPos.max }
                    }
                });
            }

            // Pagination setup
            int perPage = config.General.AnnouncementsPerPage;
            var ordered = announcements.OrderByDescending(a => a.Date).ToList();
            int totalPages = Mathf.Max(1, Mathf.CeilToInt(ordered.Count / (float)perPage));
            page = Mathf.Clamp(page, 0, totalPages - 1);

            // Header title with improved scaling
            elements.Add(new CuiLabel
            {
                Text = { 
                    Text = $"News Archive - Page {page + 1} of {totalPages} ({ordered.Count} total)", 
                    FontSize = GetScaledFontSize(FontType.Title, player), 
                    Align = TextAnchor.MiddleLeft, 
                    Color = GetAccessibleTextColor(colors.Text.TitleText),
                    Font = GetScaledFont(FontStyle.Bold)
                },
                RectTransform = { AnchorMin = "0.15 0", AnchorMax = "0.85 1" }
            }, headerPanel, CuiHelper.GetGuid());

            // Close button
            elements.Add(new CuiButton
            {
                Button = { Color = colors.Buttons.CloseButton, Command = "news.close" },
                Text = { 
                    Text = "✕", 
                    FontSize = GetScaledFontSize(FontType.Header, player), 
                    Align = TextAnchor.MiddleCenter, 
                    Color = GetAccessibleTextColor(colors.Buttons.ButtonText),
                    Font = GetScaledFont(FontStyle.Bold)
                },
                RectTransform = { AnchorMin = "0.92 0.1", AnchorMax = "0.98 0.9" }
            }, headerPanel, CuiHelper.GetGuid());

            // Content area
            string contentArea = CuiHelper.GetGuid();
            float padding = config.UI.Layout.ContentPadding;
            elements.Add(new CuiPanel
            {
                Image = { Color = GetAccessibleBackgroundColor(colors.Panels.ContentPanel) },
                RectTransform = { AnchorMin = $"{padding} 0.12", AnchorMax = $"{1-padding} 0.9" }
            }, HistoryPanel, contentArea);

            // News cards with improved scaling
            int start = page * perPage;
            int end = Mathf.Min(start + perPage, ordered.Count);
            float cardHeight = 0.22f;
            float cardSpacing = config.UI.Layout.ElementSpacing;
            float startY = 0.96f;

            for (int i = start; i < end; i++)
            {
                var ann = ordered[i];
                int realIndex = announcements.IndexOf(ann);
                float cardY = startY - (i - start) * (cardHeight + cardSpacing);
                
                CreateNewsCard(elements, contentArea, ann, realIndex, colors, player, 
                    0f, cardY - cardHeight, 1f, cardY);
            }

            // Navigation buttons with better styling
            string buttonArea = CuiHelper.GetGuid();
            elements.Add(new CuiPanel
            {
                Image = { Color = "0 0 0 0" },
                RectTransform = { AnchorMin = $"{padding} 0.02", AnchorMax = $"{1-padding} 0.1" }
            }, HistoryPanel, buttonArea);

            // Previous button
            if (page > 0)
            {
                AddStyledButton(elements, buttonArea, colors.Buttons.SecondaryButton, colors.Buttons.ButtonText, 
                    "◀ Previous", $"news.page {page - 1}", GetScaledFontSize(FontType.Header, player), 
                    0f, 0.2f, 0.25f, 0.8f, player);
            }

            // Page info
            elements.Add(new CuiLabel
            {
                Text = { 
                    Text = $"{start + 1}-{end} of {ordered.Count}", 
                    FontSize = GetScaledFontSize(FontType.Small, player), 
                    Align = TextAnchor.MiddleCenter, 
                    Color = GetAccessibleTextColor(colors.Text.MutedText),
                    Font = GetScaledFont(FontStyle.Regular)
                },
                RectTransform = { AnchorMin = "0.35 0.2", AnchorMax = "0.65 0.8" }
            }, buttonArea, CuiHelper.GetGuid());

            // Next button
            if (page < totalPages - 1)
            {
                AddStyledButton(elements, buttonArea, colors.Buttons.SecondaryButton, colors.Buttons.ButtonText, 
                    "Next ▶", $"news.page {page + 1}", GetScaledFontSize(FontType.Header, player), 
                    0.75f, 0.2f, 1f, 0.8f, player);
            }

            CuiHelper.AddUi(player, elements);
            TrackUI(player, HistoryPanel);
        }

        private void CreateNewsCard(CuiElementContainer elements, string parent, Announcement ann, int index, 
            ColorSettings colors, BasePlayer player, float minX, float minY, float maxX, float maxY)
        {
            string card = CuiHelper.GetGuid();
            
            // Card background with accessibility support
            elements.Add(new CuiPanel
            {
                Image = { Color = GetAccessibleBackgroundColor(colors.Panels.CardPanel) },
                RectTransform = { AnchorMin = $"{minX} {minY}", AnchorMax = $"{maxX} {maxY}" }
            }, parent, card);

            // Priority indicator and type badge
            if (config.UI.Layout.ShowPriorityIndicators)
            {
                string priorityColor = GetPriorityColor(ann.Type, ann.Priority);
                elements.Add(new CuiPanel
                {
                    Image = { Color = priorityColor },
                    RectTransform = { AnchorMin = "0 0.85", AnchorMax = "1 1" }
                }, card, CuiHelper.GetGuid());

                // Type badge in corner
                if (config.UI.Layout.ShowTypeIcons)
                {
                    string typeBa
// ... [Code preview truncated due to length. Full source in attached file] ...

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