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

FREE Siren : 2.1.3 2.1.3

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

namespace Oxide.Plugins
{
    [Info("Siren", "GNGGOD", "2.1.3")]
    [Description("A plugin to play a siren sound in Rust")]
    public class Siren : RustPlugin
    {
        #region Fields
        private ConfigData configData;
        private readonly Dictionary<BasePlayer, IOEntity> AlarmSound = new Dictionary<BasePlayer, IOEntity>();
        private readonly List<IOEntity> MonumentAlarms = new List<IOEntity>();
        private readonly List<BaseEntity> SpawnedEffectEntities = new List<BaseEntity>();
        private List<Timer> effectTimers = new List<Timer>(); 
        private bool isPlaying = false;
        private Timer activeTimer;
        private string previousCommandName;
        private const string PermissionUse = "siren.use";  
        private const string PermissionAdmin = "siren.admin";
        
        private Timer scheduleCheckTimer;
        private HashSet<string> executedServerSchedulesToday = new HashSet<string>();
        private HashSet<string> executedGameSchedulesToday = new HashSet<string>();
        private const string DataFileName = "Siren_ScheduleData";
        private string lastServerDate;
        private int lastGameDay = -1;
        #endregion
        
        #region Schedule Data
        private class ScheduleData
        {
            public string LastServerDate { get; set; }
            public int LastGameDay { get; set; }
            public List<string> ExecutedServerSchedules { get; set; } = new List<string>();
            public List<string> ExecutedGameSchedules { get; set; } = new List<string>();
        }
        #endregion

        #region Configuration
        public class ConfigData
        {
            public GeneralSettings General { get; set; }
            public SirenSettings Siren { get; set; }
            public NotificationSettings Notifications { get; set; }
            public ServerTimeScheduleSettings ServerTimeSchedules { get; set; }
            public GameTimeScheduleSettings GameTimeSchedules { get; set; }
            public bool Debug { get; set; }
        }

        public class GeneralSettings
        {
            public string Command { get; set; }
        }

        public class SirenSettings
        {
            public string SoundPath { get; set; }
            public float Duration { get; set; }
            public float Height { get; set; }
            public bool UseMonumentSirens { get; set; }
            public bool RepeatSound { get; set; }
            public float RepeatEverySeconds { get; set; }
        }

        public class NotificationSettings
        {
            public bool Enabled { get; set; }
            public string Message { get; set; }
            public bool UseGUIAnnouncements { get; set; }
            public string GUIBannerColor { get; set; }
            public string GUITextColor { get; set; }
        }

        public class ServerTimeScheduleSettings
        {
            public bool Enabled { get; set; }
            public float CheckIntervalSeconds { get; set; }
            public string TimeFormat { get; set; }
            public List<string> Times { get; set; }
        }

        public class GameTimeScheduleSettings
        {
            public bool Enabled { get; set; }
            public float CheckIntervalSeconds { get; set; }
            public string TimeFormat { get; set; }
            public List<string> Times { get; set; }
        }

        protected override void LoadDefaultConfig()
        {
            PrintWarning("Creating a new configuration file.");
            configData = GetDefaultConfig();
            SaveConfig();
        }

        protected override void SaveConfig()
        {
            Config.WriteObject(configData, true);
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();

            try
            {
                configData = Config.ReadObject<ConfigData>();
        
                var defaultConfig = GetDefaultConfig();
                var configChanged = false;
        
                foreach (var prop in typeof(ConfigData).GetProperties())
                {
                    var currentValue = prop.GetValue(configData);
                    if (currentValue == null || 
                        (prop.PropertyType.IsValueType && currentValue.Equals(Activator.CreateInstance(prop.PropertyType))))
                    {
                        prop.SetValue(configData, prop.GetValue(defaultConfig));
                        configChanged = true;
                        DebugLog($"Added missing config value: {prop.Name}");
                    }
                }
        
                if (configChanged)
                {
                    SaveConfig();
                }
            }
            catch (Exception ex)
            {
                PrintError($"Error loading configuration: {ex.Message}. Creating a new one.");
                configData = GetDefaultConfig();
                SaveConfig();
            }
        }

        private ConfigData GetDefaultConfig()
        {
            return new ConfigData
            {
                General = new GeneralSettings
                {
                    Command = "siren"
                },
                Siren = new SirenSettings
                {
                    SoundPath = "assets/prefabs/io/electric/other/alarmsound.prefab",
                    Duration = 30.0f,
                    Height = -10.0f,
                    UseMonumentSirens = false,
                    RepeatSound = false,
                    RepeatEverySeconds = 1.0f
                },
                Notifications = new NotificationSettings
                {
                    Enabled = true,
                    Message = "The siren has been triggered!",
                    UseGUIAnnouncements = false,
                    GUIBannerColor = "Red",
                    GUITextColor = "White"
                },
                ServerTimeSchedules = new ServerTimeScheduleSettings
                {
                    Enabled = false,
                    CheckIntervalSeconds = 20.0f,
                    TimeFormat = "24hr format HH:mm (e.g., 14:30, 06:00, 22:15)",
                    Times = new List<string>()
                },
                GameTimeSchedules = new GameTimeScheduleSettings
                {
                    Enabled = false,
                    CheckIntervalSeconds = 20.0f,
                    TimeFormat = "Decimal format HH.XX (e.g., 6.00, 12.50, 18.75)",
                    Times = new List<string>()
                },
                Debug = false
            };
        }
        #endregion

        #region Localization
        private Dictionary<string, string> messages = new Dictionary<string, string>
        {
            ["NoPermission"] = "You don't have permission to use this command.", 
            ["SirenActivated"] = "Siren sound activated! It will play for {0} seconds.",
            ["SirenDeactivated"] = "Siren sound deactivated.",
            ["SirenAlreadyActive"] = "The siren is already active. Please wait until it finishes.",
            ["SirenStatus"] = "Siren status: {0}. Duration: {1}s. Repeat: {2} ({3}s). Mode: {4}.",
            ["DurationChanged"] = "Siren duration set to {0} seconds.",
            ["SoundPathChanged"] = "Siren sound path set to: {0}",
            ["NotificationMessageChanged"] = "Siren notification message set to: {0}",
            ["NotificationToggled"] = "Siren notification enabled: {0}",
            ["InvalidDuration"] = "Invalid duration. Please enter a valid number.",
            ["UsageSetDuration"] = "Usage: siren.setduration <duration>",
            ["UsageSetSound"] = "Usage: siren.setsound <soundpath>",
            ["UsageSetNotification"] = "Usage: siren.setnotification <message>",
            ["UsageToggleNotification"] = "Usage: siren.enablenotification <true|false>", 
            ["InvalidToggle"] = "Invalid input. Please enter 'true' or 'false'.",
            ["SirenCommandExecuted"] = "Siren sound command executed!",
            ["CommandHelp"] = "Siren Commands:\n" +
                               "/siren - Activate the siren\n" +
                               "siren - Activate the siren\n" +
                               "siren status - Show the current siren status and settings\n" +
                               "siren stop - Stop the siren\n" +
                              "siren.setduration <seconds> - Set siren duration\n" +
                              "siren.setsound <path> - Set siren sound path\n" +
                              "siren.setrepeat <true|false> <interval> - Configure effect repeating\n" +
                              "siren.setnotification <message> - Set notification message\n" +
                              "siren.enablenotification <true|false> - Toggle notifications\n" +
                              "siren.setguiannouncements <true|false> [bannerColor] [textColor] - Configure GUIAnnouncements"
        };

        private string Lang(string key, string id = null, params object[] args) => 
            string.Format(lang.GetMessage(key, this, id), args);

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(messages, this);
        }
        #endregion

        #region Oxide Hooks
        private void Init()
        {
            LoadConfig();
            LoadDefaultMessages();
            LoadScheduleData();
            
            permission.RegisterPermission(PermissionUse, this);
            permission.RegisterPermission(PermissionAdmin, this);
            
            previousCommandName = configData.General.Command;
        }

        private void OnServerInitialized()
        {
            RegisterCommands();
            ValidateSirenPrefab();
            StartScheduleTimer();
        }

        private void Unload()
        {
            if (isPlaying)
            {
                StopSiren();
            }
            
            activeTimer?.Destroy();
            StopScheduleTimer();
            SaveScheduleData();
            
            UnregisterCommands();
        }
        #endregion
        
        #region Plugin Hooks
        [PluginReference]
        private Plugin GUIAnnouncements;

        private bool IsGUIAnnouncementsLoaded()
        {
            return GUIAnnouncements != null && GUIAnnouncements.IsLoaded;
        }
        
        // Track entities spawned by effects during siren playback
        private void OnEntitySpawned(BaseEntity entity)
        {
            if (!isPlaying || entity == null) return;
            
            // Skip players and items
            if (entity is BasePlayer || entity is DroppedItem || entity is WorldItem) return;
            
            // Only track entities that match our configured siren prefab path
            if (string.IsNullOrEmpty(entity.PrefabName) || 
                !entity.PrefabName.Equals(configData.Siren.SoundPath, StringComparison.OrdinalIgnoreCase))
            {
                return;
            }
            
            // Track entities spawned by this plugin for cleanup
            SpawnedEffectEntities.Add(entity);
            DebugLog($"Tracked spawned siren entity: {entity.ShortPrefabName}");
        }
        #endregion

        #region Commands
        private void RegisterCommands()
        {
            UnregisterCommands();
    
            cmd.AddConsoleCommand(configData.General.Command, this, nameof(PlaySirenConsoleCommand));
            cmd.AddChatCommand(configData.General.Command, this, nameof(PlaySirenChatCommand));
            cmd.AddConsoleCommand("siren.setduration", this, nameof(SetSirenDurationCommand));
            cmd.AddConsoleCommand("siren.setsound", this, nameof(SetSirenSoundCommand));
            cmd.AddConsoleCommand("siren.setrepeat", this, nameof(SetSirenRepeatCommand));
            cmd.AddConsoleCommand("siren.setnotification", this, nameof(SetSirenNotificationCommand));
            cmd.AddConsoleCommand("siren.enablenotification", this, nameof(EnableNotificationCommand));
            cmd.AddConsoleCommand("siren.setguiannouncements", this, nameof(SetGUIAnnouncementsCommand));
            cmd.AddConsoleCommand("siren.help", this, nameof(SirenHelpCommand));
    
            previousCommandName = configData.General.Command;
    
            DebugLog($"Registered commands: {configData.General.Command}, siren.setduration, siren.setsound, siren.setnotification, siren.enablenotification, siren.setguiannouncements, siren.help");
        }

        private void UnregisterCommands()
        {
            if (!string.IsNullOrEmpty(previousCommandName))
            {
                cmd.RemoveConsoleCommand(previousCommandName, this);
                cmd.RemoveChatCommand(previousCommandName, this);
                DebugLog($"Unregistered command: {previousCommandName}");
            }
    
            cmd.RemoveConsoleCommand("siren.setduration", this);
            cmd.RemoveConsoleCommand("siren.setsound", this);
            cmd.RemoveConsoleCommand("siren.setrepeat", this);
            cmd.RemoveConsoleCommand("siren.setnotification", this);
            cmd.RemoveConsoleCommand("siren.enablenotification", this);
            cmd.RemoveConsoleCommand("siren.setguiannouncements", this);
            cmd.RemoveConsoleCommand("siren.help", this);
    
            DebugLog("Unregistered administrative commands");
        }

        private void PlaySirenConsoleCommand(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
    
            if (player != null && !HasPermission(player, PermissionUse))
            {
                SendReply(arg, Lang("NoPermission", player?.UserIDString));
                return;
            }

            if (arg.GetString(0, string.Empty).Equals("status", StringComparison.OrdinalIgnoreCase))
            {
                SendReply(arg, GetSirenStatus(player?.UserIDString));
                return;
            }

            if (arg.GetString(0, string.Empty).Equals("stop", StringComparison.OrdinalIgnoreCase))
            {
                if (player != null && !HasPermission(player, PermissionAdmin))
                {
                    SendReply(arg, Lang("NoPermission", player?.UserIDString));
                    return;
                }
        
                StopSiren();
                SendReply(arg, Lang("SirenDeactivated", player?.UserIDString));
                return;
            }

            if (isPlaying)
            {
                SendReply(arg, Lang("SirenAlreadyActive", player?.UserIDString));
                return;
            }

            StartSiren();
            DebugLog("Siren sound command executed for all players.");
            SendReply(arg, Lang("SirenActivated", player?.UserIDString, configData.Siren.Duration));
        }

        private void PlaySirenChatCommand(BasePlayer player, string command, string[] args)
        {
            if (!HasPermission(player, PermissionUse))
            {
                player.ChatMessage(Lang("NoPermission", player.UserIDString));
                return;
            }

            if (args.Length > 0 && args[0].Equals("status", StringComparison.OrdinalIgnoreCase))
            {
                player.ChatMessage(GetSirenStatus(player.UserIDString));
                return;
            }

            if (args.Length > 0 && args[0].Equals("stop", StringComparison.OrdinalIgnoreCase))
            {
                if (!HasPermission(player, PermissionAdmin))
                {
                    player.ChatMessage(Lang("NoPermission", player.UserIDString));
                    return;
                }
        
                StopSiren();
                player.ChatMessage(Lang("SirenDeactivated", player.UserIDString));
                return;
            }

            if (isPlaying)
            {
                player.ChatMessage(Lang("SirenAlreadyActive", player.UserIDString));
                return;
            }

            StartSiren();
            DebugLog($"Siren sound command executed by {player.displayName} for all players.");
            player.ChatMessage(Lang("SirenActivated", player.UserIDString, configData.Siren.Duration));
        }

        private string GetSirenStatus(string playerId)
        {
            return Lang("SirenStatus", playerId,
                isPlaying ? "active" : "inactive",
                configData.Siren.Duration,
                configData.Siren.RepeatSound ? "enabled" : "disabled",
                configData.Siren.RepeatEverySeconds,
                configData.Siren.UseMonumentSirens ? "monuments" : "players");
        }

        private void SirenHelpCommand(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            SendReply(arg, Lang("CommandHelp", player?.UserIDString));
        }

        private void SetSirenDurationCommand(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            if (player != null && !HasPermission(player, PermissionAdmin))
            {
                SendReply(arg, Lang("NoPermission", player.UserIDString));
                return;
            }

            if (arg.Args == null || arg.Args.Length < 1)
            {
                SendReply(arg, Lang("UsageSetDuration", player?.UserIDString));
                return;
            }

            if (!float.TryParse(arg.GetString(0, string.Empty), out float duration))
            {
                SendReply(arg, Lang("InvalidDuration", player?.UserIDString));
                return;
            }

            configData.Siren.Duration = duration;
            SaveConfig();
            SendReply(arg, Lang("DurationChanged", player?.UserIDString, duration));
        }

        private void SetSirenSoundCommand(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            if (player != null && !HasPermission(player, PermissionAdmin))
            {
                SendReply(arg, Lang("NoPermission", player.UserIDString));
                return;
            }

            if (arg.Args == null || arg.Args.Length < 1)
            {
                SendReply(arg, Lang("UsageSetSound", player?.UserIDString));
                return;
            }

            configData.Siren.SoundPath = arg.GetString(0, string.Empty);
            SaveConfig();
            SendReply(arg, Lang("SoundPathChanged", player?.UserIDString, configData.Siren.SoundPath));
        }
        
        private void SetSirenRepeatCommand(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            if (player != null && !HasPermission(player, PermissionAdmin))
            {
                SendReply(arg, Lang("NoPermission", player.UserIDString));
                return;
            }

            if (arg.Args == null || arg.Args.Length < 2)
            {
                SendReply(arg, "Usage: siren.setrepeat <true|false> <interval>");
                return;
            }

            if (!bool.TryParse(arg.GetString(0, string.Empty), out bool repeat))
            {
                SendReply(arg, "Invalid first argument. Please use 'true' or 'false'.");
                return;
            }

            if (!float.TryParse(arg.GetString(1, string.Empty), out float interval) || interval <= 0)
            {
                SendReply(arg, "Invalid interval. Please provide a positive number.");
                return;
            }

            configData.Siren.RepeatSound = repeat;
            configData.Siren.RepeatEverySeconds = interval;
            SaveConfig();

            SendReply(arg, $"Siren repeat settings updated: Repeat = {repeat}, Interval = {interval}s");
        }

        private void SetSirenNotificationCommand(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            if (player != null && !HasPermission(player, PermissionAdmin))
            {
                SendReply(arg, Lang("NoPermission", player.UserIDString));
                return;
            }

            if (arg.Args == null || arg.Args.Length < 1)
            {
                SendReply(arg, Lang("UsageSetNotification", player?.UserIDString));
                return;
            }

            configData.Notifications.Message = string.Join(" ", Enumerable.Range(0, arg.Args.Length).Select(index => arg.GetString(index, string.Empty)));
            SaveConfig();
            SendReply(arg, Lang("NotificationMessageChanged", player?.UserIDString, configData.Notifications.Message));
        }

        private void EnableNotificationCommand(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            if (player != null && !HasPermission(player, PermissionAdmin))
            {
                SendReply(arg, Lang("NoPermission", player.UserIDString));
                return;
            }

            if (arg.Args == null || arg.Args.Length < 1)
            {
                SendReply(arg, Lang("UsageToggleNotification", player?.UserIDString));
                return;
            }

            if (!bool.TryParse(arg.GetString(0, string.Empty), out bool enable))
            {
                SendReply(arg, Lang("InvalidToggle", player?.UserIDString));
                return;
            }

            configData.Notifications.Enabled = enable;
            SaveConfig();
            SendReply(arg, Lang("NotificationToggled", player?.UserIDString, enable));
        }
        #endregion

        #region Helper Methods
        private bool HasPermission(BasePlayer player, string perm)
        {
            return player != null && permission.UserHasPermission(player.UserIDString, perm);
        }

        private void DebugLog(string message)
        {
            if (configData.Debug)
            {
                Puts($"[Siren Debug] {message}");
            }
        }
        
        private void ValidateSirenPrefab()
        {
            if (string.IsNullOrEmpty(configData.Siren.SoundPath))
            {
                PrintError("Siren sound path is empty! Please set a valid path.");
                return;
            }
    
            var prefab = GameManager.server.FindPrefab(configData.Siren.SoundPath);
            if (prefab == null)
            {
                PrintWarning($"Could not find prefab at path: {configData.Siren.SoundPath}");
                PrintWarning("The siren might not work correctly. Please check the path in your config.");
        
                PrintWarning("Try one of these common siren prefabs:");
                PrintWarning("- assets/prefabs/io/electric/other/alarmsound.prefab");
                PrintWarning("- assets/prefabs/misc/easter/painted eggs/effects/gold_open.prefab");
                PrintWarning("- assets/prefabs/npc/autoturret/effects/online.prefab");
            }
        }
        #endregion

        #region Scheduled Sirens Logic
        private void LoadScheduleData()
        {
            try
            {
                var data = Interface.Oxide.DataFileSystem.ReadObject<ScheduleData>(DataFileName);
                if (data != null)
                {
                    lastServerDate = data.LastServerDate;
                    lastGameDay = data.LastGameDay;
                    executedServerSchedulesToday = new HashSet<string>(data.ExecutedServerSchedules ?? new List<string>());
                    executedGameSchedulesToday = new HashSet<string>(data.ExecutedGameSchedules ?? new List<string>());
                    DebugLog($"Loaded schedule data: ServerDate={lastServerDate}, GameDay={lastGameDay}");
                }
                else
                {
                    lastServerDate = DateTime.Now.ToString("yyyy-MM-dd");
                    lastGameDay = GetCurrentGameDay();
                    executedServerSchedulesToday = new HashSet<string>();
                    executedGameSchedulesToday = new HashSet<string>();
                }
            }
            catch
            {
                lastServerDate = DateTime.Now.ToString("yyyy-MM-dd");
                lastGameDay = GetCurrentGameDay();
                executedServerSchedulesToday = new HashSet<string>();
                executedGameSchedulesToday = new HashSet<string>();
                DebugLog("No existing schedule data found, starting fresh.");
            }
        }

        private void SaveScheduleData()
        {
            var data = new ScheduleData
            {
                LastServerDate = lastServerDate,
                LastGameDay = lastGameDay,
                ExecutedServerSchedules = executedServerSchedulesToday.ToList(),
                ExecutedGameSchedules = executedGameSchedulesToday.ToList()
            };
            Interface.Oxide.DataFileSystem.WriteObject(DataFileName, data);
            DebugLog($"Saved schedule data: ServerDate={lastServerDate}, GameDay={lastGameDay}");
        }

        private void StartScheduleTimer()
        {
            bool hasServerSchedules = configData.ServerTimeSchedules.Enabled && 
                configData.ServerTimeSchedules.Times != null && 
                configData.ServerTimeSchedules.Times.Count > 0;
                
            bool hasGameSchedules = configData.GameTimeSchedules.Enabled && 
                configData.GameTimeSchedules.Times != null && 
                configData.GameTimeSchedules.Times.Count > 0;

            if (!hasServerSchedules && !hasGameSchedules)
            {
                DebugLog("No scheduled sirens configured or enabled.");
                return;
            }

            // Use the smaller check interval of the two
            float checkInterval = float.MaxValue;
            if (hasServerSchedules)
                checkInterval = Math.Min(checkInterval, configData.ServerTimeSchedules.CheckIntervalSeconds);
            if (hasGameSchedules)
                checkInterval = Math.Min(checkInterval, configData.GameTimeSchedules.CheckIntervalSeconds);

            scheduleCheckTimer?.Destroy();
            scheduleCheckTimer = timer.Repeat(checkInterval, 0, CheckSchedules);
            
            if (hasServerSchedules)
                Puts($"[Siren] Server time schedules enabled. {configData.ServerTimeSchedules.Times.Count} time(s) configured.");
            if (hasGameSchedules)
                Puts($"[Siren] Game time schedules enabled. {configData.GameTimeSchedules.Times.Count} time(s) configured.");
        }

        private void StopScheduleTimer()
        {
            scheduleCheckTimer?.Destroy();
            scheduleCheckTimer = null;
        }

        private void CheckSchedules()
        {
            // Check server time schedules
            if (configData.ServerTimeSchedules.Enabled && 
                configData.ServerTimeSchedules.Times != null && 
                configData.ServerTimeSchedules.Times.Count > 0)
            {
                CheckServerTimeSchedules();
            }
            
            // Check game time schedules
            if (configData.GameTimeSchedules.Enabled && 
                configData.GameTimeSchedules.Times != null && 
                configData.GameTimeSchedules.Times.Count > 0)
            {
                CheckGameTimeSchedules();
            }
        }

        private void CheckServerTimeSchedules()
        {
            // Check for server day reset
            string currentServerDate = DateTime.Now.ToString("yyyy-MM-dd");
            if (lastServerDate != currentServerDate)
            {
                Puts($"[Siren] New server day detected. Resetting server time schedules.");
                executedServerSchedulesToday.Clear();
                lastServerDate = currentServerDate;
                SaveScheduleData();
            }

            DateTime now = DateTime.Now;
            float currentHour = now.Hour;
            float currentMinute = now.Minute + (now.Second / 60f);
            float currentTimeInMinutes = (currentHour * 60) + currentMinute;

            DebugLog($"Server time check: {currentHour:00}:{currentMinute:00.0}");

            foreach (var scheduleTime in configData.ServerTimeSchedules.Times)
            {
                if (executedServerSchedulesToday.Contains(scheduleTime))
                    continue;

                if (!TryParseServerTime(scheduleTime, out float scheduleHour, out float scheduleMinute))
                {
                    DebugLog($"Invalid server time format: {scheduleTime} (expected HH:mm)");
                    continue;
                }

                float scheduleTimeInMinutes = (scheduleHour * 60) + scheduleMinute;
                float timeDifferenceMinutes = currentTimeInMinutes - scheduleTimeInMinutes;
                
                // Grace period: 2x check interval converted to minutes
                float gracePeriodMinutes = (configData.ServerTimeSchedules.CheckIntervalSeconds * 2f) / 60f;

                DebugLog($"Server schedule '{scheduleTime}': diff={timeDifferenceMinutes:F1}min (grace=0 to {gracePeriodMinutes:F1})");

                if (timeDifferenceMinutes >= 0 && timeDifferenceMinutes <= gracePeriodMinutes)
                {
                    Puts($"[Siren] Triggering server time schedule: {scheduleTime}");
                    executedServerSchedulesToday.Add(scheduleTime);
                    SaveScheduleData();
                    TriggerScheduledSiren();
                }
            }
        }

        private void CheckGameTimeSchedules()
        {
            // Check for game day reset
            int currentGameDay = GetCurrentGameDay();
            if (lastGameDay != currentGameDay)
            {
                Puts($"[Siren] New game day detected (Day {currentGameDay}). Resetting game time schedules.");
                executedGameSchedulesToday.Clear();
                lastGameDay = currentGameDay;
                SaveScheduleData();
            }

            float gameHour = TOD_Sky.Instance.Cycle.Hour;

            DebugLog($"Game time check: {gameHour:F2}");

            foreach (var scheduleTime in configData.GameTimeSchedules.Times)
            {
                if (executedGameSchedulesToday.Contains(scheduleTime))
                    continue;

                if (!TryParseGameTime(scheduleTime, out float scheduleHour))
                {
                    DebugLog($"Invalid game time format: {scheduleTime} (expected HH.XX like 18.50)");
                    continue;
                }

                float timeDifference = gameHour - scheduleHour;
                
                // Grace period: 5 game-time units (since game time is in decimal hours, this is generous)
                float gracePeriod = 0.5f;  // Half an in-game hour

                DebugLog($"Game schedule '{scheduleTime}': current={gameHour:F2}, target={scheduleHour:F2}, diff={timeDifference:F2} (grace=0 to {gracePeriod:F2})");

                if (timeDifference >= 0 && timeDifference <= gracePeriod)
                {
                    Puts($"[Siren] Triggering game time schedule: {scheduleTime} (current: {gameHour:F2})");
                    executedGameSchedulesToday.Add(scheduleTime);
                    SaveScheduleData();
                    TriggerScheduledSiren();
                }
            }
        }

        private void TriggerScheduledSiren()
        {
            if (!isPlaying)
            {
                StartSiren();
            }
            else
            {
                DebugLog("Siren already playing, skipping scheduled trigger.");
            }
        }

        private int GetCurrentGameDay()
        {
            return (int)(TOD_Sky.Instance?.Cycle?.Day ?? 0);
        }

        private bool TryParseServerTime(string timeStr, out float hour, out float minute)
        {
            hour = 0;
            minute = 0;

            if (string.IsNullOrEmpty(timeStr))
                return false;

            // Server time uses colon format: "HH:mm" like "14:30"
            var parts = timeStr.Split(':');
            if (parts.Length != 2)
                return false;

            if (!float.TryParse(parts[0], out hour) || !float.TryParse(parts[1], out minute))
                return false;

            if (hour < 0 || hour > 23 || minute < 0 || minute > 59)
                return false;

            return true;
        }

        private bool TryParseGameTime(string timeStr, out float hour)
        {
            hour = 0;

            if (string.IsNullOrEmpty(timeStr))
                return false;

            // Game time uses decimal format: "HH.XX" like "18.50" (Rust's native format)
            if (!float.TryParse(timeStr, out hour))
                return false;

            if (hour < 0 || hour >= 24)
                return false;

            return true;
        }
        #endregion

        #region Siren Logic
        private void StartSiren()
        {
            isPlaying = true;

            if (configData.Siren.RepeatSound && configData.Siren.RepeatEverySeconds <= 0)
            {
                PrintWarning("RepeatEverySeconds must be greater than 0. Disabling repeat.");
                configData.Siren.RepeatSound = false;
            }

            if (configData.Notifications.UseGUIAnnouncements && IsGUIAnnouncementsLoaded())
            {
                DebugLog("Using GUIAnnouncements for notification");
                GUIAnnouncements.Call("CreateAnnouncement", configData.Notifications.Message, 
                    configData.Notifications.GUIBannerColor, configData.Notifications.GUITextColor);
            }

            if (configData.Siren.UseMonumentSirens)
            { 
                StartMonumentSirens();
            }
            else
            {
                foreach (var player in BasePlayer.activePlayerList)
                {
                    if (IsEffectPath(configData.Siren.SoundPath))
                    {
                        DebugLog($"Using EFFECT path for player {player.displayName}: {configData.Siren.SoundPath}");
                        PlayEffectForPlayer(player);
                    }
                    else
                    {
                        DebugLog($"Using ENTITY path for player {player.displayName}: {configData.Siren.SoundPath}");
                        AddSoundEntityToPlayer(player);
                    }
                }
            }

            activeTimer?.Destroy();
            activeTimer = timer.Once(configData.Siren.Duration, StopSiren);
        }

        private bool IsEffectPath(string path)
        {
            return path.Contains("/effects/") || 
                   path.Contains("/fx/") || 
                   (path.EndsWith(".prefab") && path.ToLower().Contains("effect"));
        }

        private void PlayEffectForPlayer(BasePlayer player)
        {
            Vector3 position = player.transform.position + new Vector3(0f, configData.Siren.Height, 0f);
            Effect.server.Run(configData.Siren.SoundPath, position);

            DebugLog($"Effect started for player {player.displayName} at {position}.");

            if (configData.Siren.RepeatSound)
            {
                Timer effectTimer = timer.Repeat(configData.Siren.RepeatEverySeconds, 
                    (int)(configData.Siren.Duration / configData.Siren.RepeatEverySeconds), () =>
                    {
                        if (!isPlaying || player == null || !player.IsConnected) return;

                        position = player.transform.position + new Vector3(0f, configData.Siren.Height, 0f);
                        Effect.server.Run(configData.Siren.SoundPath, position);
                    });

                effectTimers.Add(effectTimer);
            }

            if (configData.Notifications.Enabled && !(configData.Notifications.UseGUIAnnouncements && IsGUIAnnouncementsLoaded()))
            {
                SendReply(player, configData.Notifications.Message);
            }
        }

        private void StartMonumentSirens()
        {
            List<MonumentInfo> monuments = TerrainMeta.Path.Monuments;
            DebugLog($"Found {monuments.Count} total monuments on the map");
            
            List<string> excludeKeywords = new List<string> 
            { 
                "lake", "canyon", "river", "mountain", "cliff", "beach", "road", "forest", "swamp"
            };
            
            int sirenCount = 0;
            bool isEffect = IsEffectPath(configData.Siren.SoundPath);
            
            foreach (var monument in monuments)
            {
                if (monument.name == null)
                    continue;
                    
                string monumentName = monument.name.ToLower();
                
                if (excludeKeywords.Any(keyword => monumentName.Contains(keyword)) || 
                    monument.Bounds.size.magnitude < 20f)
                {
                    DebugLog($"Skipping monument: {monument.name}");
                    continue;
                }
                
                Vector3 position = monument.transform.position + new Vector3(0f, 15f, 0f);
                
                if (isEffect)
                {
                    Effect.server.Run(configData.Siren.SoundPath, position);
            
                    if (configData.Siren.RepeatSound)
                    {
                        Timer effectTimer = timer.Repeat(configData.Siren.RepeatEverySeconds, 
                            (int)(configData.Siren.Duration / configData.Siren.RepeatEverySeconds), () =>
                            {
                                if (!isPlaying) return;
                                Effect.server.Run(configData.Siren.SoundPath, position);
                            });
                
                        effectTimers.Add(effectTimer);
                    }
                }
                else
                {
                    IOEntity alarm = GameManager.server.CreateEntity(configData.Siren.SoundPath, position) as IOEntity;
                    
                    if (alarm == null)
                    {
                        DebugLog($"Failed to create siren entity for monument {monument.name}.");
                        continue;
                    }
                    
                    alarm.enableSaving = false;
                    alarm.Spawn();
                    
                    MonumentAlarms.Add(alarm);

                    alarm.gameObject.SetActive(true);
                    alarm.SetFlagLocal(BaseEntity.Flags.On, true);
                    alarm.UpdateFromInput(400, 0);
                    alarm.SendNetworkUpdateImmediate();
                }
                
                sirenCount++;
                DebugLog($"Siren started at monument: {monument.name}");
            }
            
            DebugLog($"Successfully placed {sirenCount} sirens at suitable monuments");

            if (configData.Notifications.Enabled)
            {
                foreach (var target in BasePlayer.activePlayerList)
                {
                    SendReply(target, configData.Notifications.Message);
                }
            }
        }

        private void StopSiren()
        {
            DebugLog("Stopping the siren sound for all players.");

            int playerAlarmsKilled = 0;
            foreach (var entry in AlarmSound.ToList())
            {
                if (entry.Value != null && !entry.Value.IsDestroyed)
                {
                    entry.Value.Kill();
                    playerAlarmsKilled++;
                }
            }
            DebugLog($"Cleaned up {playerAlarmsKilled} player alarm entities.");
    
            int monumentAlarmsKilled = 0;
            foreach (var alarm in MonumentAlarms)
            {
                if (alarm != null && !alarm.IsDestroyed)
                {
                    alarm.Kill();
                    monumentAlarmsKilled++;
                }
            }
            DebugLog($"Cleaned up {monumentAlarmsKilled} monument alarm entities.");
            MonumentAlarms.Clear();
            
            int effectEntitiesKilled = 0;
            foreach (var entity in SpawnedEffectEntities)
            {
                if (entity != null && !entity.IsDestroyed)
                {
                    entity.Kill();
                    effectEntitiesKilled++;
                }
            }
            DebugLog($"Cleaned up {effectEntitiesKilled} spawned effect entities.");
            SpawnedEffectEntities.Clear();
    
            int timersDestroyed = 0;
            foreach (var effectTimer in effectTimers)
            {
                if (effectTimer != null)
                {
                    effectTimer.Destroy();
                    timersDestroyed++;
                }
            }
            DebugLog($"Destroyed {timersDestroyed} effect timers.");

            AlarmSound.Clear();
            effectTimers.Clear();
            isPlaying = false;
            activeTimer?.Destroy();
            activeTimer = null;
            
            DebugLog("Siren cleanup complete.");
        }

        private void AddSoundEntityToPlayer(BasePlayer player)
        {
            foreach (var target in AlarmSound.ToList())
            {
                if (target.Key == null || target.Key == player) continue;

                float distance = Vector3.Distance(player.transform.position, target.Key.transform.position);
                if (distance < 20)
                {
                    DebugLog($"Skipping creation of siren entity for player {player.displayName} as one is already nearby.");
                    return;
                }
            }

            IOEntity alarm = GameManager.server.CreateEntity(configData.Siren.SoundPath, 
                player.transform.position + new Vector3(0f, configData.Siren.Height, 0f)) as IOEntity;

            if (alarm == null)
            {
                DebugLog($"Failed to create siren entity for player {player.displayName}.");
                return;
            }

            alarm.enableSaving = false;
            alarm.Spawn();
            
            // Parent to player so the alarm follows them
            alarm.SetParent(player, true, true);
            alarm.transform.localPosition = new Vector3(0f, configData.Siren.Height, 0f);
            
            AlarmSound[player] = alarm;

            alarm.gameObject.SetActive(true);
            alarm.SetFlagLocal(BaseEntity.Flags.On, true);
            alarm.UpdateFromInput(400, 0);
            alarm.SendNetworkUpdateImmediate();

            DebugLog($"Siren sound started for player {player.displayName} (parented, offset: {configData.Siren.Height}).");

            if (configData.Notifications.Enabled && !(configData.Notifications.UseGUIAnnouncements && IsGUIAnnouncementsLoaded()))
            {
                SendReply(player, configData.Notifications.Message);
            }
        }
        
        private void SetGUIAnnouncementsCommand(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            if (player != null && !HasPermission(player, PermissionAdmin))
            {
                SendReply(arg, Lang("NoPermission", player.UserIDString));
                return;
            }

            if (arg.Args == null || arg.Args.Length < 1)
            {
                SendReply(arg, "Usage: siren.setguiannouncements <true|false> [bannerColor] [textColor]");
                return;
            }

            if (!bool.TryParse(arg.GetString(0, string.Empty), out bool useGUI))
            {
                SendReply(arg, "Invalid first argument. Please use 'true' or 'false'.");
                return;
            }

            configData.Notifications.UseGUIAnnouncements = useGUI;
    
            if (arg.Args.Length > 1)
            {
                configData.Notifications.GUIBannerColor = arg.GetString(1, string.Empty);
            }
    
            if (arg.Args.Length > 2)
            {
                configData.Notifications.GUITextColor = arg.GetString(2, string.Empty);
            }
    
            SaveConfig();
    
            SendReply(arg, $"GUIAnnouncements settings updated: Enabled = {useGUI}, BannerColor = {configData.Notifications.GUIBannerColor}, TextColor = {configData.Notifications.GUITextColor}");
        }
        
        #endregion
    }
}

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