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

FREE Comet : 1.1.3 1.1.3

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

//  ____  _       _ _       
// / ___|(_) __ _(_) | ___  
// \___ \| |/ _` | | |/ _ \ 
//  ___) | | (_| | | | (_) |
// |____/|_|\__, |_|_|\___/ 
//          |___/             
// ☽✧✵✧✵✧✵✧✵✧✵✧✵✧☾
// ✦    RUST PLUGINS   ✦
// ✦     Sigilo.dev    ✦
// ☽✧✵✧✵✧✵✧✵✧✵✧✵✧☾

namespace Oxide.Plugins
{
    [Info("Comet", "Sigilo", "1.1.3")]
    class Comet : RustPlugin
    {
        const bool en = true;
        static Comet ins;
        private List<GameObject> activeComets = new List<GameObject>();
        private List<CometFall> activeCometComponents = new List<CometFall>();
        
        private static readonly WaitForSeconds Wait0_05 = new WaitForSeconds(0.05f);
        private static readonly WaitForSeconds Wait0_1 = new WaitForSeconds(0.1f);
        private static readonly Dictionary<float, WaitForSeconds> WaitDictionary = new Dictionary<float, WaitForSeconds>();
        
        private static WaitForSeconds GetWait(float time)
        {
            if (time <= 0) return null;
            
            WaitForSeconds wait;
            if (WaitDictionary.TryGetValue(time, out wait))
                return wait;
                
            wait = new WaitForSeconds(time);
            WaitDictionary[time] = wait;
            return wait;
        }
        
        #region Permissions
        
        const string PERMISSION_ADMIN = "comet.admin";
        
        #endregion
        
        #region Signal System
        
        private const string SUPPLY_SIGNAL_PREFAB = "assets/prefabs/tools/supply signal/supplysignal.prefab";
        private const ulong COMET_SIGNAL_SKIN_ID = 2791026167;
        private StoredData storedData;

        private class StoredData
        {
            public Dictionary<ulong, double> Cooldowns { get; set; } = new Dictionary<ulong, double>();
        }

        void SaveData() => Interface.Oxide.DataFileSystem.WriteObject(Name, storedData);

        void LoadData()
        {
            try
            {
                storedData = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name) ?? new StoredData();
                CleanupExpiredCooldowns();
            }
            catch
            {
                storedData = new StoredData();
            }
        }

        void OnServerSave() => SaveData();

        private void CleanupExpiredCooldowns()
        {
            if (storedData?.Cooldowns == null)
                return;

            double currentTime = GetCurrentTime();
            
            foreach (var userId in storedData.Cooldowns
                .Where(kvp => (currentTime - kvp.Value) >= _config.signalConfig.CooldownSeconds)
                .Select(kvp => kvp.Key))
            {
                storedData.Cooldowns.Remove(userId);
            }

            SaveData();
        }

        private bool IsOnCooldown(BasePlayer player)
        {
            if (storedData?.Cooldowns == null || !storedData.Cooldowns.TryGetValue(player.userID, out double lastUsage))
                return false;

            double timeSinceLastUse = GetCurrentTime() - lastUsage;
            float cooldownTime = GetPlayerCooldownTime(player);
            return timeSinceLastUse < cooldownTime;
        }

        private double GetRemainingCooldown(BasePlayer player)
        {
            if (storedData?.Cooldowns == null || !storedData.Cooldowns.TryGetValue(player.userID, out double lastUsage))
                return 0;

            double timeSinceLastUse = GetCurrentTime() - lastUsage;
            float cooldownTime = GetPlayerCooldownTime(player);
            double remainingTime = cooldownTime - timeSinceLastUse;
            return remainingTime > 0 ? remainingTime : 0;
        }

        private float GetPlayerCooldownTime(BasePlayer player)
        {
            return _config.signalConfig.CooldownSeconds;
        }

        private void SetCooldown(BasePlayer player)
        {
            if (storedData?.Cooldowns == null)
                storedData = new StoredData();
            
            storedData.Cooldowns[player.userID] = GetCurrentTime();
            SaveData();
        }

        private double GetCurrentTime() => DateTime.UtcNow.Subtract(DateTime.UnixEpoch).TotalSeconds;

        private void ResetCooldown(BasePlayer player)
        {
            if (storedData?.Cooldowns == null)
                return;
            
            storedData.Cooldowns.Remove(player.userID);
            SaveData();
            SendReply(player, "Your Comet Signal cooldown has been reset.");
        }
        
        private void OnExplosiveThrown(BasePlayer player, BaseEntity entity)
        {
            SupplySignal signal = entity as SupplySignal;
            if (signal == null || signal.skinID != COMET_SIGNAL_SKIN_ID)
                return;
                
            Puts($"COMET: Player {player.displayName} threw a comet signal");
            
            signal.CancelInvoke(signal.Explode);
            
            if (IsOnCooldown(player))
            {
                double remainingMinutes = Math.Ceiling(GetRemainingCooldown(player) / 60.0);
                string cooldownMessage = $"You must wait {remainingMinutes} minutes before using another Comet Signal.";
                    
                SendReply(player, cooldownMessage);
                ReturnCometSignal(player, signal);
                return;
            }
            
            SetCooldown(player);
            
            if (signal != null && !signal.IsDestroyed)
            {
                
                signal.CancelInvoke(signal.KillMessage);
                signal.Invoke(signal.KillMessage, 60f);
                
                timer.Once(3f, () => {
                    if (signal == null || signal.IsDestroyed)
                    {
                        Puts("COMET: Signal was destroyed before it could land");
                        return;
                    }
                    
                    Vector3 signalPosition = signal.transform.position;
                    Puts($"COMET: Signal has landed at position: {signalPosition}");
                    
                    SpawnFlareMarker(signalPosition);
                    
                    timer.Repeat(1.5f, (int)Math.Ceiling(_config.signalConfig.WarmupTime / 1.5f), () => {
                        if (signal != null && !signal.IsDestroyed)
                        {
                            Effect.server.Run("assets/bundled/prefabs/fx/fire/fire_v2.prefab", signalPosition);
                        }
                    });
                    
                    foreach (var nearbyPlayer in BasePlayer.activePlayerList.Where(p => 
                        Vector3.Distance(p.transform.position, signalPosition) <= 100f))
                    {
                        SendReply(nearbyPlayer, $"A Comet has been called! Impact in {_config.signalConfig.WarmupTime} seconds. Take cover!");
                    }
                    
                    timer.Once(_config.signalConfig.WarmupTime, () => {
                        Puts($"COMET: Spawning comet at exact position: {signalPosition}");
                        
                        foreach (var nearbyPlayer in BasePlayer.activePlayerList.Where(p => 
                            Vector3.Distance(p.transform.position, signalPosition) <= 150f))
                        {
                            SendReply(nearbyPlayer, "Comet incoming! TAKE COVER NOW!");
                        }
                        
                        SpawnComet(signalPosition);
                        
                        if (signal != null && !signal.IsDestroyed)
                            signal.Kill();
                    });
                });
            }
        }
        
        private void SpawnFlareMarker(Vector3 position)
        {
            BaseEntity flareEntity = GameManager.server.CreateEntity("assets/prefabs/tools/flareold/flare.deployed.prefab", position, Quaternion.identity);
            if (flareEntity != null)
            {
                flareEntity.Spawn();
                timer.Once(_config.signalConfig.WarmupTime + 10f, () => {
                    if (flareEntity != null && !flareEntity.IsDestroyed)
                        flareEntity.Kill();
                });
            }
            
            Effect.server.Run("assets/bundled/prefabs/fx/fire/fire_v3.prefab", position, Vector3.up, null, true);
        }
        
        private void ReturnCometSignal(BasePlayer player, SupplySignal signal)
        {
            Item item = ItemManager.CreateByName("supply.signal", 1, COMET_SIGNAL_SKIN_ID);
            if (item != null)
            {
                item.name = _config.signalConfig.DisplayName;
                player.GiveItem(item);
            }
            
            if (signal != null && !signal.IsDestroyed)
                signal.Kill();
        }
        
        [ChatCommand("givecometcaller")]
        void GiveCometSignalCommand(BasePlayer player, string command, string[] args)
        {
            if (!permission.UserHasPermission(player.UserIDString, PERMISSION_ADMIN))
            {
                SendReply(player, "You do not have permission to use this command.");
                return;
            }

            BasePlayer targetPlayer = player;
            if (args.Length > 0 && args[0].ToLower() != "me")
            {
                if (args[0].ToLower() == "reset")
                {
                    if (args.Length > 1)
                    {
                        var target = BasePlayer.Find(args[1]);
                        if (target == null)
                        {
                            SendReply(player, "Player not found.");
                            return;
                        }
                        ResetCooldown(target);
                        SendReply(player, $"You've reset the cooldown for {target.displayName}.");
                    }
                    else
                    {
                        ResetCooldown(player);
                    }
                    return;
                }
                
                targetPlayer = BasePlayer.Find(args[0]);
                if (targetPlayer == null)
                {
                    SendReply(player, "Player not found.");
                    return;
                }
                
                args = args.Skip(1).ToArray();
            }

            int amount = 1;
            if (args.Length > 0)
            {
                if (!int.TryParse(args[0], out amount) || amount <= 0)
                {
                    SendReply(player, "Invalid amount. Please specify a positive number.");
                    return;
                }
            }

            GivePlayerCometSignal(targetPlayer, amount);
            
            if (targetPlayer == player)
                SendReply(player, $"You've received {amount} Comet Signal{(amount > 1 ? "s" : "")}.");
            else
            {
                SendReply(player, $"You've given {amount} Comet Signal{(amount > 1 ? "s" : "")} to {targetPlayer.displayName}.");
                SendReply(targetPlayer, $"You've received {amount} Comet Signal{(amount > 1 ? "s" : "")} from an admin.");
            }
        }
        
        void GivePlayerCometSignal(BasePlayer player, int amount = 1)
        {
            Item item = ItemManager.CreateByName("supply.signal", amount, COMET_SIGNAL_SKIN_ID);
            if (item != null)
            {
                item.name = _config.signalConfig.DisplayName;
                item.MarkDirty();
                player.GiveItem(item);
            }
        }
        
        #endregion
        
        #region Hooks
        
        void Init()
        {
            ins = this;
            
            try
            {
                Puts("Initializing Comet plugin...");
                
                permission.RegisterPermission(PERMISSION_ADMIN, this);
                
                LoadConfig();
                LoadData();
                
                _config = Config.ReadObject<PluginConfig>();
                
                if (_config == null)
                {
                    Puts("Config is null, loading default config...");
                    LoadDefaultConfig();
                    _config = Config.ReadObject<PluginConfig>();
                }
                
                if (_config.cometConfig == null)
                {
                    Puts("Comet config is null, loading default config...");
                    _config = PluginConfig.DefaultConfig();
                    SaveConfig();
                }
                
                if (_config.signalConfig == null)
                {
                    Puts("Signal config is null, updating config...");
                    _config.signalConfig = PluginConfig.DefaultConfig().signalConfig;
                    SaveConfig();
                }
                
                Puts("Comet plugin initialized successfully!");
            }
            catch (Exception ex)
            {
                PrintError("Error initializing Comet plugin: " + ex.Message);
                LoadDefaultConfig();
                _config = Config.ReadObject<PluginConfig>();
                permission.RegisterPermission(PERMISSION_ADMIN, this);
            }
        }

        void OnServerInitialized()
        {
            if (ins == null)
                ins = this;
                
            if (storedData == null)
                LoadData();
                
            CleanupExpiredCooldowns();
        }

        void Unload()
        {
            try {
                if (activeComets != null && activeComets.Count > 0)
                {
                    Puts($"Cleaning up {activeComets.Count} tracked comet objects on plugin unload");
                    for (int i = activeComets.Count - 1; i >= 0; i--)
                    {
                        var cometObj = activeComets[i];
                        if (cometObj != null)
                        {
                            UnityEngine.Object.Destroy(cometObj);
                        }
                        activeComets.RemoveAt(i);
                    }
                }
                
                if (activeCometComponents != null && activeCometComponents.Count > 0)
                {
                    Puts($"Cleaning up {activeCometComponents.Count} comet components on plugin unload");
                    for (int i = activeCometComponents.Count - 1; i >= 0; i--)
                    {
                        var component = activeCometComponents[i];
                        if (component != null)
                        {
                            component.CleanupOnPluginUnload();
                            UnityEngine.Object.Destroy(component);
                        }
                        activeCometComponents.RemoveAt(i);
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.LogError($"Error during Comet plugin unload: {ex}");
            }
            finally
            {
                ins = null;
            }
        }
        
        #endregion
        
        #region Commands
        
        [ChatCommand("comet")]
        void SummonCometCommand(BasePlayer player, string command, string[] args)
        {
            if (!permission.UserHasPermission(player.UserIDString, PERMISSION_ADMIN))
            {
                SendReply(player, "You do not have permission to use this command.");
                return;
            }

            Vector3 position;
            
            if (args.Length > 0 && args[0].ToLower() == "here")
            {
                RaycastHit hit;
                if (Physics.Raycast(player.eyes.HeadRay(), out hit, 1000f, LayerMask.GetMask("Terrain", "World", "Construction")))
                {
                    position = hit.point;
                }
                else
                {
                    return;
                }
            }
            else
            {
                position = player.transform.position;
            }

            SpawnComet(position);
            SendReply(player, "A comet is falling from the sky");
        }
        
        [ConsoleCommand("comet.summon")]
        void ConsoleSummonComet(ConsoleSystem.Arg arg)
        {
            BasePlayer player = arg.Player();
            if (player != null && !permission.UserHasPermission(player.UserIDString, PERMISSION_ADMIN))
            {
                SendReply(player, "You do not have permission to use this command.");
                return;
            }

            Vector3 position;
            if (arg.Args != null && arg.Args.Length >= 3)
            {
                float x, y, z;
                if (float.TryParse(arg.Args[0].ToString(), out x) &&
                    float.TryParse(arg.Args[1].ToString(), out y) &&
                    float.TryParse(arg.Args[2].ToString(), out z))
                {
                    position = new Vector3(x, y, z);
                }
                else
                {
                    if (player != null)
                        SendReply(player, "Invalid position coordinates. Format: comet.summon <x> <y> <z>");
                    else
                        Puts("Invalid position coordinates. Format: comet.summon <x> <y> <z>");
                    return;
                }
            }
            else if (player != null)
            {
                position = player.transform.position;
            }
            else
            {
                Puts("When using from server console, you must specify coordinates: comet.summon <x> <y> <z>");
                return;
            }

            SpawnComet(position);
            if (player != null)
                SendReply(player, "A comet is falling from the sky");
            else
                Puts("A comet is falling from the sky");
        }
        
        [ConsoleCommand("comet.give")]
        void ConsoleGiveCometSignal(ConsoleSystem.Arg arg)
        {
            BasePlayer caller = arg.Player();
            if (caller != null && !permission.UserHasPermission(caller.UserIDString, PERMISSION_ADMIN))
            {
                SendReply(caller, "You do not have permission to use this command.");
                return;
            }
            
            if (arg.Args == null || arg.Args.Length < 1)
            {
                string message = "Usage: comet.give <player name/steamID> [amount=1]";
                if (caller != null)
                    SendReply(caller, message);
                else
                    Puts(message);
                return;
            }
            
            BasePlayer targetPlayer = null;
            string playerNameOrId = arg.Args[0].ToString();
            
            if (playerNameOrId.All(char.IsDigit))
            {
                targetPlayer = BasePlayer.FindByID(ulong.Parse(playerNameOrId));
            }
            
            if (targetPlayer == null)
            {
                targetPlayer = BasePlayer.Find(playerNameOrId);
            }
            
            if (targetPlayer == null)
            {
                string message = $"Player '{playerNameOrId}' not found.";
                if (caller != null)
                    SendReply(caller, message);
                else
                    Puts(message);
                return;
            }
            
            int amount = 1;
            if (arg.Args.Length > 1 && (!int.TryParse(arg.Args[1].ToString(), out amount) || amount <= 0))
            {
                string message = "Invalid amount. Please specify a positive number.";
                if (caller != null)
                    SendReply(caller, message);
                else
                    Puts(message);
                return;
            }
            
            GivePlayerCometSignal(targetPlayer, amount);
            
            string successMessage = $"Given {amount} Comet Signal{(amount > 1 ? "s" : "")} to {targetPlayer.displayName}.";
            if (caller != null)
                SendReply(caller, successMessage);
            else    
                Puts(successMessage);
                
            SendReply(targetPlayer, $"You've received {amount} Comet Signal{(amount > 1 ? "s" : "")}.");
        }
        
        [ConsoleCommand("comet.giveall")]
        void ConsoleGiveAllCometSignal(ConsoleSystem.Arg arg)
        {
            BasePlayer caller = arg.Player();
            if (caller != null && !permission.UserHasPermission(caller.UserIDString, PERMISSION_ADMIN))
            {
                SendReply(caller, "You do not have permission to use this command.");
                return;
            }
            
            int amount = 1;
            if (arg.Args != null && arg.Args.Length > 0)
            {
                if (!int.TryParse(arg.Args[0].ToString(), out amount) || amount <= 0)
                {
                    string message = "Invalid amount. Please specify a positive number.";
                    if (caller != null)
                        SendReply(caller, message);
                    else
                        Puts(message);
                    return;
                }
            }
            
            int playerCount = 0;
            foreach (var player in BasePlayer.activePlayerList)
            {
                GivePlayerCometSignal(player, amount);
                playerCount++;
                SendReply(player, $"You've received {amount} Comet Signal{(amount > 1 ? "s" : "")}.");
            }
            
            string successMessage = $"Given {amount} Comet Signal{(amount > 1 ? "s" : "")} to {playerCount} online players.";
            if (caller != null)
                SendReply(caller, successMessage);
            else
                Puts(successMessage);
        }
        
        #endregion
        
        #region Comet Logic
        
        void SpawnComet(Vector3 impactPosition)
        {
            try
            {
                if (ins == null)
                    ins = this;
                    
                GameObject cometObj = new GameObject("Comet");
                activeComets.Add(cometObj);
                CometFall cometFall = cometObj.AddComponent<CometFall>();
                activeCometComponents.Add(cometFall);
                
                Vector3 startPosition = FindStartFallPosition(impactPosition);
                
                float horizontalDistance = Mathf.Sqrt(
                    (startPosition.x - impactPosition.x) * (startPosition.x - impactPosition.x) +
                    (startPosition.z - impactPosition.z) * (startPosition.z - impactPosition.z)
                );
                
                float verticalDistance = startPosition.y - impactPosition.y;
                float trajectoryAngle = Mathf.Atan2(verticalDistance, horizontalDistance) * Mathf.Rad2Deg;
                
                Puts($"Comet spawn info:");
                Puts($" - Target position: {impactPosition.x}, {impactPosition.y}, {impactPosition.z}");
                Puts($" - Start position: {startPosition.x}, {startPosition.y}, {startPosition.z}");
                Puts($" - Horizontal offset: {_config.cometConfig.horizontalOffset}");
                Puts($" - Horizontal distance: {horizontalDistance}");
                Puts($" - Vertical distance: {verticalDistance}");
                Puts($" - Trajectory angle: {trajectoryAngle}° from horizontal");
                
                Vector3 direction = (impactPosition - startPosition).normalized;
                Puts($" - Direction: {direction.x}, {direction.y}, {direction.z}");
                
                cometFall.Init(impactPosition, startPosition);
            }
            catch (Exception ex)
            {
                PrintError("Error spawning comet: " + ex.Message);
            }
        }
        
        Vector3 FindStartFallPosition(Vector3 impactPosition)
        {
            float horizontalOffset = _config.cometConfig.horizontalOffset;
            float fallHeight = UnityEngine.Random.Range(_config.cometConfig.minFallHeight, _config.cometConfig.maxFallHeight);
            
            Vector3 startPos = new Vector3(
                impactPosition.x + horizontalOffset, 
                impactPosition.y + fallHeight, 
                impactPosition.z);
                
            return startPos;
        }
        
        #endregion
        
        #region Comet Class
        
        class CometFall : FacepunchBehaviour
        {
            Vector3 startPosition;
            Vector3 impactPosition;
            Vector3 fallDirection;
            Coroutine cometFallCoroutine;
            List<BaseEntity> cometEntities = new List<BaseEntity>();
            List<BaseEntity> impactEntities = new List<BaseEntity>();
            float impactRadius;
            float explosionDamage;
            float currentFallTime = 0f;
            float totalFallTime = 0f;
            bool impactOccurred = false;

            internal void CleanupOnPluginUnload()
            {
                try
                {
                    if (cometFallCoroutine != null && ServerMgr.Instance != null)
                    {
                        ServerMgr.Instance.StopCoroutine(cometFallCoroutine);
                        cometFallCoroutine = null;
                    }
                    
                    CancelInvoke();
                    
                    foreach (BaseEntity entity in cometEntities)
                    {
                        if (entity != null && !entity.IsDestroyed)
                        {
                            entity.Kill();
                        }
                    }
                    cometEntities.Clear();
                    
                    foreach (BaseEntity entity in impactEntities)
                    {
                        if (entity != null && !entity.IsDestroyed)
                        {
                            entity.Kill();
                        }
                    }
                    impactEntities.Clear();
                }
                catch (Exception ex)
                {
                    Debug.LogError($"Error cleaning up CometFall component: {ex}");
                }
            }

            internal void Init(Vector3 impactPosition, Vector3 startPosition)
            {
                try
                {
                    this.impactPosition = impactPosition;
                    this.startPosition = startPosition;
                    
                    if (ins == null)
                    {
                        throw new Exception("Plugin instance is null");
                    }
                    
                    if (ins._config == null)
                    {
                        ins._config = PluginConfig.DefaultConfig();
                        throw new Exception("Plugin config is null, created default config");
                    }
                    
                    if (ins._config.cometConfig == null)
                    {
                        ins._config.cometConfig = PluginConfig.DefaultConfig().cometConfig;
                        throw new Exception("Comet config is null, created default values");
                    }
                    
                    this.impactRadius = ins._config.cometConfig.impactRadius;
                    this.explosionDamage = ins._config.cometConfig.explosionDamage;
                    DefineFallDirection();
                    CreateCometEntities();
                    
                    float distance = Vector3.Distance(startPosition, impactPosition);
                    totalFallTime = distance / (0.5f * ins._config.cometConfig.fallingSpeedScale);
                    
                    if (ServerMgr.Instance != null)
                        cometFallCoroutine = ServerMgr.Instance.StartCoroutine(CometFallCoroutine());
                    else
                        throw new Exception("ServerMgr.Instance is null");
                    
                    InvokeRepeating(CreatePeriodicEffects, 0.1f, 0.1f);
                }
                catch (Exception ex)
                {
                    Debug.LogError("Error in CometFall.Init: " + ex.Message);
                    throw;
                }
            }

            void DefineFallDirection()
            {
                fallDirection = (impactPosition - startPosition).normalized;
            }

            void CreateCometEntities()
            {
                ins.Puts($"Creating comet entities at position: {startPosition}");
                
                cometEntities.Clear();
                
                BaseEntity mainEntity = GameManager.server.CreateEntity("assets/bundled/prefabs/oilfireballsmall.prefab", startPosition, Quaternion.identity);
                if (mainEntity != null)
                {
                    mainEntity.Spawn();
                    mainEntity.transform.position = startPosition;
                    cometEntities.Add(mainEntity);
                    ins.Puts($"Main comet entity created at {mainEntity.transform.position}");
                }
                else
                {
                    ins.Puts("Failed to create main comet entity!");
                }
                
                int flareCount = Mathf.RoundToInt(ins._config.cometConfig.initialExplosionCount * 0.2f);
                for (int i = 0; i < flareCount; i++)
                {
                    Vector3 offset = new Vector3(
                        UnityEngine.Random.Range(-3f, 3f),
                        UnityEngine.Random.Range(-3f, 3f),
                        UnityEngine.Random.Range(-3f, 3f)
                    );
                    
                    Vector3 flarePosition = startPosition + offset;
                    BaseEntity fireEntity = GameManager.server.CreateEntity("assets/prefabs/tools/flareold/flare.deployed.prefab", 
                        flarePosition, Quaternion.identity);
                        
                    if (fireEntity != null)
                    {
                        fireEntity.Spawn();
                        fireEntity.transform.position = flarePosition;
                        cometEntities.Add(fireEntity);
                    }
                }
                
                ins.Puts($"Total comet entities created: {cometEntities.Count}");
            }
            
            void CreatePeriodicEffects()
            {
                if (cometEntities.Count == 0 || cometEntities[0] == null || cometEntities[0].IsDestroyed || impactOccurred)
                {
                    CancelInvoke(CreatePeriodicEffects);
                    return;
                }
                
                Effect.server.Run("assets/bundled/prefabs/fx/fire/fire_v2.prefab", cometEntities[0].transform.position, Vector3.up);
                
                if (UnityEngine.Random.Range(0, 100) < (ins._config.cometConfig.postImpactEffectCount * 10))
                {
                    Vector3 offsetPos = cometEntities[0].transform.position + new Vector3(
                        UnityEngine.Random.Range(-3f, 3f),
                        UnityEngine.Random.Range(-3f, 3f),
                        UnityEngine.Random.Range(-3f, 3f)
                    );
                    
                    Effect.server.Run("assets/prefabs/npc/patrol helicopter/effects/heli_explosion.prefab", offsetPos, Vector3.up);
                }
                
                Effect.server.Run("assets/bundled/prefabs/fx/fire/fire_v3.prefab", 
                    cometEntities[0].transform.position - (fallDirection * 3f), Vector3.up);
            }

            IEnumerator CometFallCoroutine()
            {
                if (ins == null || ins._config == null || ins._config.cometConfig == null)
                {
                    Debug.LogError("CometFall: Plugin instance or config is null");
                    yield break;
                }
                
                ins.Puts($"Starting comet fall from {startPosition} to {impactPosition}");
                ins.Puts($"Direction vector: {fallDirection}");
                
                Vector3 totalDistanceVector = impactPosition - startPosition;
                float totalDistance = totalDistanceVector.magnitude;
                
                float horizontalDistance = Mathf.Sqrt(
                    (startPosition.x - impactPosition.x) * (startPosition.x - impactPosition.x) +
                    (startPosition.z - impactPosition.z) * (startPosition.z - impactPosition.z)
                );
                float verticalDistance = startPosition.y - impactPosition.y;
                
                ins.Puts($"Total movement: distance={totalDistance}, horizontal={horizontalDistance}, vertical={verticalDistance}");
                ins.Puts($"Movement will be diagonal with {horizontalDistance}m horizontal movement and {verticalDistance}m vertical drop");
                
                float coveredDistance = 0f;
                float baseFallSpeed = 10f;
                
                foreach (BaseEntity entity in cometEntities)
                {
                    if (entity == null || entity.IsDestroyed) continue;
                    
                    if (entity == cometEntities[0])
                    {
                        entity.transform.position = startPosition;
                    }
                    else
                    {
                        Vector3 offset = new Vector3(
                            UnityEngine.Random.Range(-3f, 3f),
                            UnityEngine.Random.Range(-3f, 3f),
                            UnityEngine.Random.Range(-3f, 3f)
                        );
                        entity.transform.position = startPosition + offset;
                    }
                }
                
                float stepDelay = 0.05f;
                
                while (coveredDistance < totalDistance && !impactOccurred)
                {
                    if (ins == null || ins._config == null || ins._config.cometConfig == null)
                    {
                        Debug.LogError("CometFall: Plugin instance or config became null during fall");
                        break;
                    }
                    
                    float progress = coveredDistance / totalDistance;
                    float speedMultiplier = 0.5f + (1.5f * progress);
                    
                    float stepDistance = baseFallSpeed * speedMultiplier * ins._config.cometConfig.fallingSpeedScale;
                    
                    if (coveredDistance + stepDistance > totalDistance)
                    {
                        stepDistance = totalDistance - coveredDistance;
                    }
                    
                    coveredDistance += stepDistance;
                    
                    Vector3 newMainPosition = Vector3.Lerp(startPosition, impactPosition, coveredDistance / totalDistance);
                    
                    if (cometEntities.Count > 0 && cometEntities[0] != null && !cometEntities[0].IsDestroyed)
                    {
                        cometEntities[0].transform.position = newMainPosition;
                        
                        for (int i = 1; i < cometEntities.Count; i++)
                        {
                            BaseEntity entity = cometEntities[i];
                            if (entity == null || entity.IsDestroyed) continue;
                            
                            Vector3 offset = new Vector3(
                                UnityEngine.Random.Range(-3f, 3f),
                                UnityEngine.Random.Range(-3f, 3f),
                                UnityEngine.Random.Range(-3f, 3f)
                            );
                            
                            entity.transform.position = newMainPosition + offset;
                            
                            entity.transform.Rotate(new Vector3(
                                UnityEngine.Random.Range(1f, 3f), 
                                UnityEngine.Random.Range(1f, 3f), 
                                UnityEngine.Random.Range(1f, 3f)
                            ), Space.Self);
                        }
                    }
                    
                    if (coveredDistance >= totalDistance - stepDistance)
                    {
                        ins.Puts("Comet reached target position");
                        
                        if (cometEntities.Count > 0 && cometEntities[0] != null && !cometEntities[0].IsDestroyed)
                        {
                            cometEntities[0].transform.position = impactPosition;
                            
                            for (int i = 1; i < cometEntities.Count; i++)
                            {
                                BaseEntity entity = cometEntities[i];
                                if (entity == null || entity.IsDestroyed) continue;
                                
                                Vector3 offset = new Vector3(
                                    UnityEngine.Random.Range(-3f, 3f),
                                    UnityEngine.Random.Range(-3f, 3f),
                                    UnityEngine.Random.Range(-3f, 3f)
                                );
                                
                                entity.transform.position = impactPosition + offset;
                            }
                        }
                        
                        OnCometImpact(impactPosition);
                        break;
                    }
                    
                    if (cometEntities.Count > 0 && cometEntities[0] != null && !cometEntities[0].IsDestroyed)
                    {
                        RaycastHit hit;
                        if (Physics.Raycast(cometEntities[0].transform.position, fallDirection, out hit, stepDistance, 
                            LayerMask.GetMask("Terrain", "World", "Construction", "Tree", "Deployed")))
                        {
                            ins.Puts($"Comet hit object at {hit.point}");
                            OnCometImpact(hit.point);
                            break;
                        }
                    }
                    
                    yield return Wait0_05;
                }
                
                ins.Puts("Comet fall ended");
                
                if (!impactOccurred)
                {
                    OnCometImpact(impactPosition);
                }
            }
            
            void OnCometImpact(Vector3 exactImpactPos)
            {
                if (impactOccurred) return;
                impactOccurred = true;
                
                try
                {
                    Vector3 finalImpactPos = impactPosition;
                    
                    if (Vector3.Distance(exactImpactPos, impactPosition) > 1f)
                    {
                        ins.Puts($"Impact adjustment: Original={exactImpactPos}, Using={finalImpactPos}");
                    }
                    
                    CancelInvoke(CreatePeriodicEffects);
                    
                    foreach (BaseEntity entity in cometEntities)
                    {
                        if (entity == null || entity.IsDestroyed) continue;
                        entity.Kill();
                    }
                    
                    CreateExplosion(finalImpactPos);
                    
                    DealImpactDamage(finalImpactPos);
                    
                    CreateTemporaryCrater(finalImpactPos);
                    
                    int cleanupTime = ins?._config?.cometConfig?.impactCleanupTime ?? 30;
                    Invoke(() => CleanupImpactSite(), cleanupTime);
                }
                catch (Exception ex)
                {
                    Debug.LogError("Error in OnCometImpact: " + ex.Message);
                    
                    foreach (BaseEntity entity in cometEntities)
                    {
                        if (entity == null || entity.IsDestroyed) continue;
                        entity.Kill();
                    }
                    
                    cometEntities.Clear();
                    impactEntities.Clear();
                }
            }
            
            void CreateExplosion(Vector3 position)
            {
                Effect.server.Run("assets/prefabs/npc/patrol helicopter/effects/heli_explosion.prefab", position, Vector3.up);
                
                ServerMgr.Instance.StartCoroutine(SequentialExplosions(position));
                
                foreach (var player in BasePlayer.activePlayerList)
                {
                    if (Vector3.Distance(player.transform.position, position) < ins._config.cometConfig.playerDamageRadius)
                    {
                        Effect.server.Run("assets/prefabs/npc/patrol helicopter/effects/heli_explosion.prefab", 
                            player.transform.position + Vector3.up * 10, Vector3.up);
                            
                        if (Vector3.Distance(player.transform.position, position) < impactRadius * 2)
                        {
                            Vector3 pushDirection = (player.transform.position - position).normalized;
                            player.Hurt(ins._config.cometConfig.playerDamageAmount, Rust.DamageType.Explosion);
                        }
                    }
                }
            }
            
            void StartFallingExplosionChain(Vector3 startPos)
            {
                ServerMgr.Instance.StartCoroutine(FallingExplosionChain(startPos));
            }
            
            IEnumerator SequentialExplosions(Vector3 position)
            {
                yield return Wait0_1;
                
                for (int i = 0; i < ins._config.cometConfig.numberOfSecondaryExplosions; i++)
                {
                    float angle = i * (360f / ins._config.cometConfig.numberOfSecondaryExplosions);
                    float distance = UnityEngine.Random.Range(2f, impactRadius * 0.8f);
                    
                    Vector3 explosionPos = position + new Vector3(
                        Mathf.Cos(angle * Mathf.Deg2Rad) * distance,
                        2f,
                        Mathf.Sin(angle * Mathf.Deg2Rad) * distance
                    );
                    
                    StartFallingExplosionChain(explosionPos);
                    
                    yield return GetWait(ins._config.cometConfig.chainExplosionDelay);
                }
                
                float endTime = Time.time + ins._config.cometConfig.impactCleanupTime * 0.1f;
                int maxAdditionalExplosions = 3;
                int additionalExplosions = 0;
                
                while (Time.time < endTime && additionalExplosions < maxAdditionalExplosions)
                {
                    Vector3 randomOffset = new Vector3(
                        UnityEngine.Random.Range(-impactRadius * 0.7f, impactRadius * 0.7f),
                        1.5f,
                        UnityEngine.Random.Range(-impactRadius * 0.7f, impactRadius * 0.7f)
                    );
                    
                    Vector3 explosionPos = position + randomOffset;
                    StartFallingExplosionChain(explosionPos);
                    
                    additionalExplosions++;
                    yield return GetWait(ins._config.cometConfig.chainExplosionDelay * 3f);
                }
            }
            
            IEnumerator FallingExplosionChain(Vector3 startPos)
            {
                Vector3 currentPos = startPos;
                bool shouldContinue = true;
                int currentChain = 0;
                float maxRaycastDistance = 10f;
                
                while (shouldContinue && currentChain < ins._config.cometConfig.maxChainLength)
                {
                    Effect.server.Run("assets/bundled/prefabs/fx/fire/fire_v2.prefab", currentPos, Vector3.up);
                    
                    RaycastHit hit;
                    if (Physics.Raycast(currentPos, Vector3.down, out hit, maxRaycastDistance, LayerMask.GetMask("Terrain", "World", "Construction", "Deployed")))
                    {
                        float distanceToHit = Vector3.Distance(currentPos, hit.point);
                        if (distanceToHit <= ins._config.cometConfig.fallSpeed)
                        {
                            Effect.server.Run("assets/prefabs/npc/patrol helicopter/effects/heli_explosion.prefab", hit.point, Vector3.up);
                            Explode(hit.point, impactRadius * 0.5f, explosionDamage * ins._config.cometConfig.secondaryExplosionDamageScale);
                            
                            BaseCombatEntity hitEntity = hit.GetEntity() as BaseCombatEntity;
                            if (hitEntity != null)
                            {
                                if (hitEntity is BuildingBlock)
                                {
                                    hitEntity.Hurt(explosionDamage * ins._config.cometConfig.buildingDamageMultiplier, Rust.DamageType.Explosion, null, true);
                                }
                                
                                yield return GetWait(ins._config.cometConfig.chainExplosionDelay);
                                
                                currentPos = hit.point + new Vector3(
                                    UnityEngine.Random.Range(-2f, 2f),
                                    0.5f,
                                    UnityEngine.Random.Range(-2f, 2f)
                                );
                            }
                            else
                            {
                                shouldContinue = false;
                            }
                            
                            currentChain++;
                        }
                        else
                        {
                            currentPos = Vector3.MoveTowards(currentPos, hit.point, ins._config.cometConfig.fallSpeed);
                        }
                    }
                    else
                    {
                        currentPos += Vector3.down * ins._config.cometConfig.fallSpeed;
                        
                        if (currentPos.y < impactPosition.y - 5f)
                        {
                            shouldContinue = false;
                        }
                    }
                    
                    yield return GetWait(ins._config.cometConfig.chainExplosionDelay);
                }
            }
            
            void Explode(Vector3 position, float radius, float damage)
            {
                List<BaseEntity> entities = new List<BaseEntity>();
                Vis.Entities(position, radius, entities);
                
                foreach (var entity in entities)
                {
                    if (entity == null || entity.IsDestroyed) continue;
                    
                    float distance = Vector3.Distance(position, entity.transform.position);
                    float damageAmount = damage * (1 - Mathf.Clamp01(distance / radius));
                    
                    BaseCombatEntity combatEntity = entity as BaseCombatEntity;
                    if (combatEntit
// ... [Code preview truncated due to length. Full source in attached file] ...

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