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

FREE MinicopterCombat : 2.6.0 2.6.0

Нет прав для скачивания
C#:
using System;
using System.Collections.Generic;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using UnityEngine;
using Facepunch;
using Network;
//  ____  _       _ _       
// / ___(_) __ _(_) | ___  
// \___ \| |/ _` | | |/ _ \ 
//  ___) | | (_| | | | (_) |
// |____/|_|\__, |_|_|\___/ 
//          |___/             
// ☽✧✵✧✵✧✵✧✵✧✵✧✵✧☾
// ✦    RUST PLUGINS   ✦
// ✦     https://sigilo.dev    ✦
// ☽✧✵✧✵✧✵✧✵✧✵✧✵✧☾
namespace Oxide.Plugins
{
    [Info("MinicopterCombat", "Sigilo", "2.6.0")]
    [Description("Adds realistic frontal minigun with bullet travel time and side missiles to the Minicopter.")]
    public class MinicopterCombat : RustPlugin
    {
        #region Configuration
        private class PluginConfig
        {
            public bool RequirePermission = true;
            public float MinigunDamage = 20.0f;
            public float MinigunRange = 400.0f;
            public float MinigunBulletVelocity = 500.0f;
            public float MinigunBulletGravity = 0.6f;
            public float MinigunAimconeDegrees = 0.4f;
            public float MinigunRoundsPerSecond = 10.0f;
            public float MinigunDamageFalloffStart = 250.0f;
            public float MinigunDamageFalloffEnd = 400.0f;
            public float MinigunMinDamageMultiplier = 0.6f;
            public float MinigunMuzzleForward = 1.5f;
            public float MinigunMuzzleUp = 0.3f;
            public float MinigunMuzzleSide = 0.0f;
            public int MinigunMagazineSize = 100;
            public float MinigunReloadTime = 30.0f;
            public bool MinigunEnableMuzzleFlash = true;
            public bool MinigunEnableTracers = false;
            public bool MinigunEnableSound = true;
            public bool MinigunEnableImpactEffects = true;
            public float MinigunTracerChance = 0.2f;
            public string MinigunSoundEffect = "assets/prefabs/weapons/ak47u/effects/attack.prefab";
            public string MinigunMuzzleFlashEffect = "assets/prefabs/weapons/ak47u/effects/attack.prefab";
            public string MinigunTracerEffect = "";
            public string MinigunImpactEffect = "assets/bundled/prefabs/fx/impacts/additive/explosion.prefab";
            public string MinigunFleshImpactEffect = "assets/bundled/prefabs/fx/impacts/additive/fire.prefab";
            public bool ExplosiveBulletsEnabled = true;
            public float ExplosiveBulletChance = 0.01f;
            public float ExplosiveBulletRadius = 1.5f;
            public float ExplosiveBulletDamage = 30.0f;
            public string ExplosiveBulletEffect = "assets/bundled/prefabs/fx/explosions/explosion_01.prefab";
            public float MissileCooldown = 1.5f;
            public float MissileSpeed = 100.0f;
            public string RocketPrefab = "assets/prefabs/ammo/rocket/rocket_hv.prefab";
            public float RocketOffsetForward = 1.5f;
            public float RocketOffsetSide = 1.0f;
            public float RocketOffsetUp = -0.3f;
            public int MaxMissilesPerReload = 6;
            public float MissileReloadCooldown = 30.0f;
            public string MissileReloadStartSound = "assets/prefabs/deployable/repair bench/effects/skinchange_spraypaint.prefab";
            public string MissileReloadCompleteSound = "assets/bundled/prefabs/fx/invite_notice.prefab";
            public bool InheritHelicopterVelocity = false;
            public bool HeadshotMultiplier = true;
            public float HeadshotDamageMultiplier = 1.5f;
            public bool DebugMode = false;
            public bool EnableHUD = true;
            public bool EnableLockOn = true;
            public float LockOnTime = 0.35f;
            public float LockOnMaxRange = 400.0f;
            public float LockOnMaxAngle = 10.0f;
            public string HomingRocketPrefab = "assets/prefabs/ammo/rocket/rocket_hv.prefab";
            public bool EnableRocketBoost = true;
            public float BoostThrustMultiplier = 1.5f;
            public float BoostFuelDuration = 1.0f;
            public float BoostRechargeTime = 15.0f;
            public float BoostCrashSpeedThreshold = 15.0f;
            public string BoostActivateSound = "assets/prefabs/npc/patrol helicopter/effects/rocket_fire.prefab";
            public string BoostLoopEffect = "assets/prefabs/ammo/rocket/rocket_basic.prefab";
            public string BoostCrashExplosionEffect = "assets/bundled/prefabs/fx/explosions/explosion_01.prefab";
            public string BoostCrashFireEffect = "assets/bundled/prefabs/fx/fire/fire_v3_2x2.prefab";
            public bool EnableBombs = true;
            public int MaxBombsPerReload = 4;
            public float BombReloadTime = 20.0f;
            public float BombCooldown = 1.0f;
            public float BombDropForwardOffset = 0.0f;
            public float BombDropDownOffset = 1.5f;
            public float BombDamage = 150.0f;
            public float BombRadius = 6.0f;
            public string BombPrefab = "assets/prefabs/ammo/rocket/rocket_basic.prefab";
            public string BombDropSound = "assets/prefabs/locks/keypad/effects/lock.code.lock.prefab";
            public string BombExplosionEffect = "assets/bundled/prefabs/fx/gas_explosion_small.prefab";
            public float ManualReloadCooldown = 45.0f;
        }
        private PluginConfig config;
        #endregion
        #region Constants
        private const string HUD_PANEL = "MinicopterCombat_HUD";
        private const string PermissionUse = "minicoptercombat.use";
        private static readonly int BulletLayerMask = ~(
            (1 << 2) |
            (1 << 29)
        );
        #endregion
        #region Data Storage
        private readonly Dictionary<ulong, Timer> minigunTimersByPlayer = new Dictionary<ulong, Timer>();
        private readonly Dictionary<ulong, float> lastMissileFire = new Dictionary<ulong, float>();
        private readonly Dictionary<ulong, bool> nextRocketLeftByMini = new Dictionary<ulong, bool>();
        private readonly Dictionary<ulong, int> missileCountByMini = new Dictionary<ulong, int>();
        private readonly Dictionary<ulong, bool> isMissileReloading = new Dictionary<ulong, bool>();
        private readonly Dictionary<ulong, Timer> missileReloadTimers = new Dictionary<ulong, Timer>();
        private readonly Dictionary<ulong, int> minigunAmmoByMini = new Dictionary<ulong, int>();
        private readonly Dictionary<ulong, bool> isMinigunReloading = new Dictionary<ulong, bool>();
        private readonly Dictionary<ulong, Timer> minigunReloadTimers = new Dictionary<ulong, Timer>();
        private readonly Dictionary<ulong, Timer> hudUpdateTimers = new Dictionary<ulong, Timer>();
        private readonly Dictionary<ulong, LockOnState> lockOnByPlayer = new Dictionary<ulong, LockOnState>();
        private readonly Dictionary<ulong, BoostState> boostByMini = new Dictionary<ulong, BoostState>();
        private readonly HashSet<ulong> boostedMinisVulnerable = new HashSet<ulong>();
        private readonly Dictionary<ulong, int> bombCountByMini = new Dictionary<ulong, int>();
        private readonly Dictionary<ulong, bool> isBombReloading = new Dictionary<ulong, bool>();
        private readonly Dictionary<ulong, Timer> bombReloadTimers = new Dictionary<ulong, Timer>();
        private readonly Dictionary<ulong, float> lastBombDrop = new Dictionary<ulong, float>();
        private readonly Dictionary<ulong, float> lastManualReload = new Dictionary<ulong, float>();
        private readonly HashSet<BasePlayer> activePilots = new HashSet<BasePlayer>();
        private float nextLockOnProcessTime = 0f;
        private const float LOCK_ON_PROCESS_INTERVAL = 0.1f;
        private class LockOnState
        {
            public PlayerHelicopter TargetHelicopter;
            public float LockProgress;
            public bool IsLocked;
            public float NextBeepTime;
        }
        private class IncomingLockState
        {
            public float LockProgress;
            public bool IsLocked;
            public float NextWarningTime;
        }
        private readonly Dictionary<ulong, IncomingLockState> incomingLockByMini = new Dictionary<ulong, IncomingLockState>();
        private class BoostState
        {
            public PlayerHelicopter Helicopter;
            public bool IsActive;
            public float FuelRemaining;
            public float RechargeTimer;
            public float LastEffectTime;
        }
        private class ActiveHomingMissile
        {
            public ServerProjectile Missile;
            public BaseEntity Target;
            public float LaunchTime;
        }
        private readonly List<ActiveHomingMissile> activeMissiles = new List<ActiveHomingMissile>();
        private readonly List<PlayerHelicopter> activeMinicopters = new List<PlayerHelicopter>();
        private readonly List<PendingBullet> pendingBullets = new List<PendingBullet>();
        private readonly HashSet<BaseEntity> activeBombs = new HashSet<BaseEntity>();
        private class PendingBullet
        {
            public BasePlayer Owner;
            public PlayerHelicopter Helicopter;
            public Vector3 Origin;
            public Vector3 Velocity;
            public Vector3 CurrentPosition;
            public float SpawnTime;
            public float BaseDamage;
            public float MaxLifetime;
            public Vector3 LastPosition;
            public bool ShowTracer;
            public float TracerUpdateTime;
        }
        #endregion
        #region Oxide Hooks
        private void Init()
        {
            permission.RegisterPermission(PermissionUse, this);
        }
        protected override void LoadDefaultConfig()
        {
            config = new PluginConfig();
            SaveConfig();
        }
        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<PluginConfig>();
                if (config == null)
                    throw new Exception("Configuration file is null");
            }
            catch
            {
                PrintWarning("Configuration file is corrupt or missing, creating new one...");
                LoadDefaultConfig();
            }
        }
        protected override void SaveConfig()
        {
            Config.WriteObject(config, true);
        }
        private void OnServerInitialized()
        {
            try
            {
                config = Config.ReadObject<PluginConfig>();
                if (config.LockOnMaxAngle > 20.0f)
                {
                    config.LockOnMaxAngle = 10.0f;
                }
                SaveConfig();
                activeMinicopters.Clear();
                foreach (var entity in BaseNetworkable.serverEntities)
                {
                    if (entity is PlayerHelicopter mini)
                        activeMinicopters.Add(mini);
                }
                Puts($"MinicopterCombat v2.5.0 loaded - Bullet velocity: {config.MinigunBulletVelocity} m/s, Magazine: {config.MinigunMagazineSize}");
                Puts($"Tracers: {(config.MinigunEnableTracers ? "Enabled" : "Disabled")}, Explosive: {(config.ExplosiveBulletsEnabled ? "Enabled" : "Disabled")}, Boost: {(config.EnableRocketBoost ? "Enabled" : "Disabled")}");
            }
            catch (Exception e)
            {
                PrintError($"OnServerInitialized error: {e}");
            }
        }
        private void OnTick()
        {
            ProcessPendingBullets();
            if (activePilots.Count > 0 && config.EnableLockOn)
            {
                float currentTime = Time.realtimeSinceStartup;
                if (currentTime >= nextLockOnProcessTime)
                {
                    nextLockOnProcessTime = currentTime + LOCK_ON_PROCESS_INTERVAL;
                    ProcessLockOnLogic();
                }
            }
            if (activeMissiles.Count > 0)
            {
                ProcessHomingMissiles();
            }
            if (boostByMini.Count > 0)
            {
                ProcessBoostPhysics();
            }
        }
        private void ProcessLockOnLogic()
        {
            activePilots.RemoveWhere(p => p == null || !p.IsConnected || p.IsDead());
            foreach (var player in activePilots)
            {
                if (!IsInMiniCopter(player, out PlayerHelicopter mini, out BaseMountable seat))
                {
                    if (lockOnByPlayer.ContainsKey(player.userID))
                    {
                        lockOnByPlayer.Remove(player.userID);
                    }
                    continue;
                }
                if (!lockOnByPlayer.TryGetValue(player.userID, out LockOnState state))
                {
                    state = new LockOnState();
                    lockOnByPlayer[player.userID] = state;
                }
                PlayerHelicopter target = null;
                PlayerHelicopter bestTarget = null;
                float bestAngle = config.LockOnMaxAngle;
                Vector3 eyePos = player.eyes.position;
                Vector3 lookDir = player.eyes.HeadForward();
                for (int i = activeMinicopters.Count - 1; i >= 0; i--)
                {
                    var targetMini = activeMinicopters[i];
                    if (targetMini == null || targetMini.IsDestroyed)
                    {
                        activeMinicopters.RemoveAt(i);
                        continue;
                    }
                    if (targetMini == mini) continue;
                    Vector3 toTarget = targetMini.transform.position - eyePos;
                    float distSqr = toTarget.sqrMagnitude;
                    if (distSqr > config.LockOnMaxRange * config.LockOnMaxRange) continue;
                    float angle = Vector3.Angle(lookDir, toTarget);
                    if (angle > config.LockOnMaxAngle) continue;
                    if (Physics.Linecast(eyePos, targetMini.transform.position + Vector3.up, out RaycastHit hit, BulletLayerMask))
                    {
                         var hitEntity = hit.GetEntity();
                         if (hitEntity != targetMini && hitEntity?.GetParentEntity() != targetMini)
                         {
                             continue;
                         }
                    }
                    if (angle < bestAngle)
                    {
                        bestAngle = angle;
                        bestTarget = targetMini;
                    }
                }
                if (bestTarget != null)
                {
                    target = bestTarget;
                    if (config.DebugMode && Time.realtimeSinceStartup % 1.0f < 0.1f)
                    {
                    }
                }
                if (target != null)
                {
                    ulong targetMiniId = target.net.ID.Value;
                    if (state.TargetHelicopter != target)
                    {
                        state.TargetHelicopter = target;
                        state.LockProgress = 0f;
                        state.IsLocked = false;
                    }
                    if (!incomingLockByMini.TryGetValue(targetMiniId, out IncomingLockState incomingState))
                    {
                        incomingState = new IncomingLockState { LockProgress = 0f, IsLocked = false, NextWarningTime = 0f };
                        incomingLockByMini[targetMiniId] = incomingState;
                    }
                    if (!state.IsLocked)
                    {
                        state.LockProgress += Time.deltaTime / config.LockOnTime;
                        incomingState.LockProgress = state.LockProgress;
                        if (state.LockProgress >= 1.0f)
                        {
                            state.LockProgress = 1.0f;
                            state.IsLocked = true;
                            incomingState.LockProgress = 1.0f;
                            incomingState.IsLocked = true;
                            Effect.server.Run("assets/prefabs/npc/auto_turret/effects/target_acquired.prefab", player.transform.position);
                            var targetDriver = target.GetDriver();
                            if (targetDriver != null)
                            {
                                Effect.server.Run("assets/prefabs/locks/keypad/effects/lock.denied.prefab", targetDriver.transform.position);
                            }
                        }
                        else if (Time.realtimeSinceStartup >= state.NextBeepTime)
                        {
                            float beepDelay = Mathf.Lerp(0.8f, 0.1f, state.LockProgress);
                            state.NextBeepTime = Time.realtimeSinceStartup + beepDelay;
                            Effect.server.Run("assets/prefabs/locks/keypad/effects/lock.code.updated.prefab", player.transform.position);
                            if (Time.realtimeSinceStartup >= incomingState.NextWarningTime)
                            {
                                incomingState.NextWarningTime = Time.realtimeSinceStartup + beepDelay;
                                var targetDriver = target.GetDriver();
                                if (targetDriver != null)
                                {
                                    Effect.server.Run("assets/prefabs/locks/keypad/effects/lock.code.lock.prefab", targetDriver.transform.position);
                                }
                            }
                        }
                    }
                    else
                    {
                        incomingState.IsLocked = true;
                        incomingState.LockProgress = 1.0f;
                    }
                }
                else
                {
                    if (state.TargetHelicopter != null)
                    {
                        ulong oldTargetId = state.TargetHelicopter.net.ID.Value;
                        if (incomingLockByMini.TryGetValue(oldTargetId, out IncomingLockState oldIncoming))
                        {
                            oldIncoming.LockProgress = Mathf.Max(0f, oldIncoming.LockProgress - Time.deltaTime / config.LockOnTime);
                            if (oldIncoming.LockProgress <= 0f)
                            {
                                incomingLockByMini.Remove(oldTargetId);
                            }
                            oldIncoming.IsLocked = false;
                        }
                    }
                    if (state.LockProgress > 0)
                    {
                        state.LockProgress -= Time.deltaTime / config.LockOnTime;
                        if (state.LockProgress <= 0f)
                        {
                            state.LockProgress = 0f;
                            state.TargetHelicopter = null;
                            state.IsLocked = false;
                        }
                    }
                    else
                    {
                        if (state.TargetHelicopter != null)
                        {
                            state.TargetHelicopter = null;
                            state.IsLocked = false;
                        }
                    }
                }
            }
        }
        private void Unload()
        {
            foreach (var player in BasePlayer.activePlayerList)
            {
                DestroyHUD(player);
            }
            foreach (var t in minigunTimersByPlayer.Values)
            {
                try { t?.Destroy(); } catch { }
            }
            minigunTimersByPlayer.Clear();
            foreach (var t in missileReloadTimers.Values)
            {
                try { t?.Destroy(); } catch { }
            }
            missileReloadTimers.Clear();
            foreach (var t in minigunReloadTimers.Values)
            {
                try { t?.Destroy(); } catch { }
            }
            minigunReloadTimers.Clear();
            foreach (var t in hudUpdateTimers.Values)
            {
                try { t?.Destroy(); } catch { }
            }
            hudUpdateTimers.Clear();
            foreach (var t in bombReloadTimers.Values)
            {
                try { t?.Destroy(); } catch { }
            }
            bombReloadTimers.Clear();
            pendingBullets.Clear();
        }
        private void OnEntitySpawned(BaseNetworkable entity)
        {
             if (entity is PlayerHelicopter mini)
             {
                 if (!activeMinicopters.Contains(mini))
                    activeMinicopters.Add(mini);
             }
        }
        private void OnEntityKill(BaseNetworkable entity)
        {
            if (entity is PlayerHelicopter mini)
            {
                if (activeMinicopters.Contains(mini))
                    activeMinicopters.Remove(mini);
                ulong miniId = mini.net.ID.Value;
                CleanupMinicopter(miniId);
                boostByMini.Remove(miniId);
                boostedMinisVulnerable.Remove(miniId);
                incomingLockByMini.Remove(miniId);
            }
            if (entity is BaseEntity bombEntity && activeBombs.Contains(bombEntity))
            {
                activeBombs.Remove(bombEntity);
                Vector3 pos = bombEntity.transform.position;
                Effect.server.Run(config.BombExplosionEffect, pos, Vector3.up);
                Effect.server.Run(config.BombExplosionEffect, pos + Vector3.up * 0.5f, Vector3.up);
            }
        }
        private void OnEntityMounted(BaseMountable mountable, BasePlayer player)
        {
            try
            {
                if (player == null || mountable == null) return;
                if (!IsInMiniCopter(player, out PlayerHelicopter mini, out BaseMountable seat)) return;
                var driver = mini.GetDriver();
                if (driver == null || driver != player) return;
                if (HasPermission(player))
                {
                    activePilots.Add(player);
                }
            }
            catch (Exception e)
            {
                PrintError($"OnEntityMounted error: {e}");
            }
        }
        private void OnPlayerInput(BasePlayer player, InputState input)
        {
            try
            {
                if (player == null || input == null || !player.IsConnected) return;
                if (!IsInMiniCopter(player, out PlayerHelicopter mini, out BaseMountable seat)) return;
                var driver = mini.GetDriver();
                if (driver == null || driver != player) return;
                if (mini.engineController == null || !mini.engineController.IsOn) return;
                if (!HasPermission(player)) return;
                ulong miniId = mini.net.ID.Value;
                if (config.EnableHUD)
                {
                    StartHUDUpdates(player, mini);
                }
                if (input.IsDown(BUTTON.FIRE_PRIMARY))
                {
                    if (IsMinigunReloading(miniId))
                    {
                        return;
                    }
                    StartMinigunTimer(player, mini);
                }
                else
                {
                    StopMinigunTimer(player.userID);
                }
                if (input.WasJustPressed(BUTTON.FIRE_SECONDARY))
                {
                    TryFireMissiles(player, mini);
                }
                if (config.EnableRocketBoost)
                {
                    HandleBoostInput(player, mini, input.IsDown(BUTTON.SPRINT));
                }
                if (config.EnableBombs && input.WasJustPressed(BUTTON.USE))
                {
                    TryDropBomb(player, mini);
                }
                if (input.WasJustPressed(BUTTON.RELOAD))
                {
                    TryManualReloadAll(player, mini);
                }
            }
            catch (Exception e)
            {
                PrintError($"OnPlayerInput error: {e}");
            }
        }
        private void HandleBoostInput(BasePlayer player, PlayerHelicopter mini, bool isPressed)
        {
            ulong miniId = mini.net.ID.Value;
            if (!boostByMini.TryGetValue(miniId, out BoostState state))
            {
                state = new BoostState
                {
                    Helicopter = mini,
                    IsActive = false,
                    FuelRemaining = config.BoostFuelDuration,
                    RechargeTimer = 0f,
                    LastEffectTime = 0f
                };
                boostByMini[miniId] = state;
            }
            if (isPressed && state.FuelRemaining > 0f && state.RechargeTimer <= 0f)
            {
                if (!state.IsActive)
                {
                    state.IsActive = true;
                    boostedMinisVulnerable.Add(miniId);
                    Effect.server.Run(config.BoostActivateSound, mini.transform.position);
                }
            }
            else
            {
                if (state.IsActive)
                {
                    state.IsActive = false;
                    if (state.FuelRemaining <= 0f)
                    {
                        state.RechargeTimer = config.BoostRechargeTime;
                        player.ChatMessage($"<color=#ffaa00>Boost depleted! Recharging... {config.BoostRechargeTime}s</color>");
                    }
                }
            }
        }
        private void ProcessBoostPhysics()
        {
            float dt = Time.deltaTime;
            float currentTime = Time.realtimeSinceStartup;
            List<ulong> toRemove = null;
            foreach (var kvp in boostByMini)
            {
                ulong miniId = kvp.Key;
                BoostState state = kvp.Value;
                var mini = state.Helicopter;
                if (mini == null || mini.IsDestroyed)
                {
                    if (toRemove == null) toRemove = new List<ulong>();
                    toRemove.Add(miniId);
                    continue;
                }
                if (state.RechargeTimer > 0f)
                {
                    state.RechargeTimer -= dt;
                    if (state.RechargeTimer <= 0f)
                    {
                        state.FuelRemaining = config.BoostFuelDuration;
                        state.RechargeTimer = 0f;
                        var driver = mini.GetDriver();
                        if (driver != null)
                        {
                            driver.ChatMessage($"<color=#00ff00>Boost recharged!</color>");
                            Effect.server.Run(config.MissileReloadCompleteSound, mini.transform.position);
                        }
                    }
                }
                if (state.IsActive && state.FuelRemaining > 0f)
                {
                    state.FuelRemaining -= dt;
                    if (mini.rigidBody != null)
                    {
                        Vector3 boostForce = mini.transform.forward * config.BoostThrustMultiplier * 50f;
                        mini.rigidBody.AddForce(boostForce, ForceMode.Acceleration);
                    }
                    if (currentTime - state.LastEffectTime > 0.15f)
                    {
                        Vector3 exhaustPos = mini.transform.position - mini.transform.forward * 1.5f;
                        Effect.server.Run(config.BoostLoopEffect, exhaustPos, -mini.transform.forward);
                        state.LastEffectTime = currentTime;
                    }
                    if (state.FuelRemaining <= 0f)
                    {
                        state.IsActive = false;
                        state.FuelRemaining = 0f;
                        state.RechargeTimer = config.BoostRechargeTime;
                        var driver = mini.GetDriver();
                        if (driver != null)
                        {
                            driver.ChatMessage($"<color=#ffaa00>Boost depleted! Recharging... {config.BoostRechargeTime}s</color>");
                        }
                    }
                }
                else if (!state.IsActive && state.RechargeTimer <= 0f && state.FuelRemaining < config.BoostFuelDuration)
                {
                    boostedMinisVulnerable.Remove(miniId);
                }
            }
            if (toRemove != null)
            {
                foreach (var id in toRemove)
                {
                    boostByMini.Remove(id);
                    boostedMinisVulnerable.Remove(id);
                }
            }
        }
        private void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
        {
            if (entity == null || info == null) return;
            var mini = entity as PlayerHelicopter;
            if (mini == null) return;
            ulong miniId = mini.net.ID.Value;
            if (!boostedMinisVulnerable.Contains(miniId)) return;
            if (!boostByMini.TryGetValue(miniId, out BoostState state)) return;
            if (!state.IsActive) return;
            float speed = mini.rigidBody != null ? mini.rigidBody.velocity.magnitude : 0f;
            if (speed < config.BoostCrashSpeedThreshold) return;
            bool isCollision = info.damageTypes.Has(Rust.DamageType.Collision) ||
                              info.damageTypes.Has(Rust.DamageType.Blunt) ||
                              (info.Initiator == null && info.damageTypes.Total() > 0);
            if (!isCollision) return;
            TriggerBoostCrashExplosion(mini, info.HitPositionWorld);
        }
        private void TriggerBoostCrashExplosion(PlayerHelicopter mini, Vector3 crashPoint)
        {
            if (mini == null || mini.IsDestroyed) return;
            ulong miniId = mini.net.ID.Value;
            boostByMini.Remove(miniId);
            boostedMinisVulnerable.Remove(miniId);
            Vector3 pos = crashPoint != Vector3.zero ? crashPoint : mini.transform.position;
            for (int i = 0; i < 3; i++)
            {
                Vector3 offset = UnityEngine.Random.insideUnitSphere * 2f;
                Effect.server.Run(config.BoostCrashExplosionEffect, pos + offset, Vector3.up);
            }
            Effect.server.Run(config.BoostCrashFireEffect, pos, Vector3.up);
            Effect.server.Run(config.BoostCrashFireEffect, pos + Vector3.up * 1f, Vector3.up);
            Effect.server.Run("assets/bundled/prefabs/fx/explosions/explosion_03.prefab", pos, Vector3.up);
            Effect.server.Run("assets/bundled/prefabs/fx/fire/fire_v3_1x1.prefab", pos + UnityEngine.Random.insideUnitSphere * 1.5f, Vector3.up);
            var driver = mini.GetDriver();
            if (driver != null)
            {
                activePilots.Remove(driver);
                lockOnByPlayer.Remove(driver.userID);
            }
            timer.Once(0.1f, () =>
            {
                if (mini != null && !mini.IsDestroyed)
                {
                    mini.Kill(BaseNetworkable.DestroyMode.Gib);
                }
            });
        }
        private void TryDropBomb(BasePlayer player, PlayerHelicopter mini)
        {
            try
            {
                float now = Time.realtimeSinceStartup;
                ulong miniId = mini.net.ID.Value;
                if (IsBombReloading(miniId))
                {
                    return;
                }
                if (lastBombDrop.TryGetValue(miniId, out float lastDrop))
                {
                    if (now - lastDrop < config.BombCooldown)
                    {
                        return;
                    }
                }
                int bombsUsed = 0;
                if (bombCountByMini.TryGetValue(miniId, out int used))
                {
                    bombsUsed = used;
                }
                if (bombsUsed >= config.MaxBombsPerReload)
                {
                    StartBombReload(player, mini, miniId);
                    return;
                }
                lastBombDrop[miniId] = now;
                bombCountByMini[miniId] = bombsUsed + 1;
                Vector3 dropPos = mini.transform.position +
                                  mini.transform.forward * config.BombDropForwardOffset +
                                  Vector3.down * config.BombDropDownOffset;
                BaseEntity bomb = GameManager.server.CreateEntity(config.BombPrefab, dropPos, Quaternion.identity);
                if (bomb == null) return;
                ServerProjectile proj = bomb.GetComponent<ServerProjectile>();
                if (proj != null)
                {
                    Vector3 heliVelocity = mini.rigidBody != null ? mini.rigidBody.velocity : Vector3.zero;
                    Vector3 velocity = heliVelocity * 0.3f;
                    velocity += Vector3.down * 20f;
                    proj.InitializeVelocity(velocity);
                }
                bomb.Spawn();
                activeBombs.Add(bomb);
                Effect.server.Run(config.BombDropSound, mini.transform.position);
                int remaining = config.MaxBombsPerReload - (bombsUsed + 1);
                if (remaining <= 0)
                {
                    StartBombReload(player, mini, miniId);
                }
            }
            catch (Exception e)
            {
                PrintError($"TryDropBomb error: {e}");
            }
        }
        private void StartBombReload(BasePlayer player, PlayerHelicopter mini, ulong miniId)
        {
            if (IsBombReloading(miniId)) return;
            isBombReloading[miniId] = true;
            Effect.server.Run(config.MissileReloadStartSound, mini.transform.position, Vector3.up);
            if (bombReloadTimers.TryGetValue(miniId, out var existingTimer))
            {
                existingTimer?.Destroy();
            }
            bombReloadTimers[miniId] = timer.Once(config.BombReloadTime, () =>
            {
                CompleteBombReload(miniId);
            });
        }
        private void CompleteBombReload(ulong miniId)
        {
            if (!isBombReloading.ContainsKey(miniId) || !isBombReloading[miniId])
                return;
            var mini = BaseNetworkable.serverEntities.Find(new NetworkableId(miniId)) as PlayerHelicopter;
            bombCountByMini[miniId] = 0;
            isBombReloading[miniId] = false;
            if (mini != null)
            {
                Effect.server.Run(config.MissileReloadCompleteSound, mini.transform.position, Vector3.up);
                var driver = mini.GetDriver();
                if (driver != null)
                {
                    driver.ChatMessage($"<color=#00ff00>Bombs reloaded! ({config.MaxBombsPerReload} ready)</color>");
                }
            }
            bombReloadTimers.Remove(miniId);
        }
        private int GetBombsRemaining(ulong miniId)
        {
            if (!bombCountByMini.TryGetValue(miniId, out int used))
                used = 0;
            return config.MaxBombsPerReload - used;
        }
        private bool IsBombReloading(ulong miniId)
        {
            return isBombReloading.TryGetValue(miniId, out bool reloading) && reloading;
        }
        private void TryManualReloadAll(BasePlayer player, PlayerHelicopter mini)
        {
            try
            {
                float now = Time.realtimeSinceStartup;
                ulong miniId = mini.net.ID.Value;
                if (lastManualReload.TryGetValue(miniId, out float lastReload))
                {
                    float remaining = config.ManualReloadCooldown - (now - lastReload);
                    if (remaining > 0)
                    {
                        player.ChatMessage($"<color=#ffaa00>Manual reload on cooldown ({remaining:F1}s)</color>");
                        return;
                    }
                }
                bool anyReloading = IsMinigunReloading(miniId) || IsReloadingMissiles(miniId) || IsBombReloading(miniId);
                if (anyReloading)
                {
                    player.ChatMessage("<color=#ffaa00>Systems are already reloading!</color>");
                    return;
                }
                int currentAmmo = GetMinigunAmmo(miniId);
                int currentMissiles = GetMissilesRemaining(miniId);
                int currentBombs = GetBombsRemaining(miniId);
                float currentBoost = config.BoostFuelDuration;
                if (boostByMini.TryGetValue(miniId, out BoostState bState))
                {
                    currentBoost = bState.FuelRemaining;
                }
                bool needsMinigun = currentAmmo < config.MinigunMagazineSize;
                bool needsMissiles = currentMissiles < config.MaxMissilesPerReload;
                bool needsBombs = currentBombs < config.MaxBombsPerReload;
                bool needsBoost = currentBoost < config.BoostFuelDuration;
                if (!needsMinigun && !needsMissiles && !needsBombs && !needsBoost)
                {
                    player.ChatMessage("<color=#88ff88>All systems already at full capacity!</color>");
                    return;
                }
                lastManualReload[miniId] = now;
                if (minigunReloadTimers.TryGetValue(miniId, out var t1)) { t1?.Destroy(); minigunReloadTimers.Remove(miniId); }
                if (missileReloadTimers.TryGetValue(miniId, out var t2)) { t2?.Destroy(); missileReloadTimers.Remove(miniId); }
                if (bombReloadTimers.TryGetValue(miniId, out var t3)) { t3?.Destroy(); bombReloadTimers.Remove(miniId); }
                if (needsMinigun) isMinigunReloading[miniId] = true;
                if (needsMissiles) isMissileReloading[miniId] = true;
                if (needsBombs) isBombReloading[miniId] = true;
                if (needsBoost && boostByMini.TryGetValue(miniId, out BoostState boostState))
                {
                    boostState.IsActive = false;
                    boostState.RechargeTimer = config.ManualReloadCooldown;
                    boostedMinisVulnerable.Remove(miniId);
                }
                Effect.server.Run(config.MissileReloadStartSound, mini.transform.position, Vector3.up);
                player.ChatMessage($"<color=#ffaa00>Reloading depleted systems... {config.ManualReloadCooldown}s</color>");
                timer.Once(config.ManualReloadCooldown, () =>
                {
                    if (mini == null || mini.IsDestroyed) return;
                    if (needsMinigun)
                    {
                        minigunAmmoByMini[miniId] = config.MinigunMagazineSize;
                        isMinigunReloading[miniId] = false;
                    }
                    if (needsMissiles)
                    {
                        missileCountByMini[miniId] = 0;
                        isMissileReloading[miniId] = false;
                    }
                    if (needsBombs)
                    {
                        bombCountByMini[miniId] = 0;
                        isBombReloading[miniId] = false;
                    }
                    if (needsBoost && boostByMini.TryGetValue(miniId, out BoostState bs))
                    {
                        bs.FuelRemaining = config.BoostFuelDuration;
                        bs.RechargeTimer = 0f;
                    }
                    Effect.server.Run(config.MissileReloadCompleteSound, mini.transform.position, Vector3.up);
                    var driver = mini.GetDriver();
                    if (driver != null)
                    {
                        driver.ChatMessage("<color=#00ff00>Systems reloaded!</color>");
                    }
                });
            }
            catch (Exception e)
            {
                PrintError($"TryManualReloadAll error: {e}");
            }
        }
        private void OnEntityDismounted(BaseMountable mountable, BasePlayer player)
        {
            try
            {
                if (player == null) return;
                activePilots.Remove(player);
                lockOnByPlayer.Remove(player.userID);
                StopMinigunTimer(player.userID);
                StopHUDUpdates(player);
                DestroyHUD(player);
            }
            catch { }
        }
        private void OnPlayerDisconnected(BasePlayer player, string reason)
        {
            try
            {
                if (player == null) return;
                activePilots.Remove(player);
                lockOnByPlayer.Remove(player.userID);
                StopMinigunTimer(player.userID);
                StopHUDUpdates(player);
                DestroyHUD(player);
                lastMissileFire.Remove(player.userID);
            }
            catch { }
        }
        private void OnPlayerDeath(BasePlayer player, HitInfo info)
        {
            try
            {
                if (player == null) return;
                activePilots.Remove(player);
                lockOnByPlayer.Remove(player.userID);
                StopMinigunTimer(player.userID);
                StopHUDUpdates(player);
                DestroyHUD(player);
            }
            catch { }
        }
        #endregion
        #region HUD System
        private void StartHUDUpdates(BasePlayer player, PlayerHelicopter mini)
        {
            if (hudUpdateTimers.ContainsKey(player.userID)) return;
            UpdateHUD(player, mini);
            hudUpdateTimers[player.userID] = timer.Every(0.5f, () =>
            {
                if (player == null || !player.IsConnected)
                {
                    StopHUDUpdates(player);
                    return;
                }
                if (!IsInMiniCopter(player, out PlayerHelicopter m, out BaseMountable s))
                {
                    StopHUDUpdates(player);
                    DestroyHUD(player);
                    return;
                }
                UpdateHUD(player, m);
            });
        }
        private void StopHUDUpdates(BasePlayer player)
        {
            if (player == null) return;
            if (hudUpdateTimers.TryGetValue(player.userID, out var t))
            {
                try { t?.Destroy(); } catch { }
                hudUpdateTimers.Remove(player.userID);
            }
        }
        private void UpdateHUD(BasePlayer player, PlayerHelicopter mini)
        {
            if (player == null || mini == null) return;
            ulong miniId = mini.net.ID.Value;
            int bulletsRemaining = GetMinigunAmmo(miniId);
            int missilesRemaining = GetMissilesRemaining(miniId);
            bool minigunReloading = IsMinigunReloading(miniId);
            bool missileReloading = IsReloadingMissiles(miniId);
            float boostFuel = config.BoostFuelDuration;
            bool boostRecharging = false;
            if (config.EnableRocketBoost && boostByMini.TryGetValue(miniId, out BoostState boostState))
            {
                boostFuel = boostState.FuelRemaining;
                boostRecharging = boostState.RechargeTimer > 0f;
            }
            int bombsRemaining = GetBombsRemaining(miniId);
            bool bombReloading = IsBombReloading(miniId);
            CuiHelper.DestroyUi(player, HUD_PANEL);
            var elements = new CuiElementContainer();
            string panelAnchorMin = "0.35 0.095";
            string panelAnchorMax = "0.65 0.125";
            elements.Add(new CuiPanel
            {
                Image = { Color = "0 0 0 0" },
                RectTransform = { AnchorMin = panelAnchorMin, AnchorMax = panelAnchorMax },
                CursorEnabled = false
            }, "Hud", HUD_PANEL);
            elements.Add(new CuiPanel
            {
                Image = { Color = "0.1 0.1 0.1 0.7" },
                RectTransform = { AnchorMin = "0.02 0.1", AnchorMax = "0.26 0.9" }
            }, HUD_PANEL, HUD_PANEL + "_BulletBG");
            string bulletValue;
            if (minigunReloading)
            {
                bulletValue = "<color=#ffaa00>RELOADING</color>";
            }
            else
            {
                string bulletColor = bulletsRemaining > 30 ? "#88ff88" : (bulletsRemaining > 10 ? "#ffcc00" : "#ff4444");
                bulletValue = $"<color={bulletColor}>{bulletsRemaining}</color>";
            }
            elements.Add(new CuiLabel
            {
                Text = { 
                    Text = $"<color=#cccccc>Bullets:</color> {bulletValue}", 
                    FontSize = 11, 
                    Align = TextAnchor.MiddleCenter, 
                    Color = "1 1 1 1",
                    Font = "robotocondensed-bold.ttf"
                },
                RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" }
            }, HUD_PANEL + "_BulletBG");
            elements.Add(new CuiPanel
            {
                Image = { Color = "0.1 0.1 0.1 0.7" },
                RectTransform = { AnchorMin = "0.27 0.1", AnchorMax = "0.51 0.9" }
            }, HUD_PANEL, HUD_PANEL + "_RocketBG");
            string rocketValue;
            if (missileReloading)
            {
                rocketValue = "<color=#ffaa00>RELOADING</color>";
            }
            else
            {
                string rocketColor = missilesRemaining > 2 ? "#ff8844" : (missilesRemaining > 0 ? "#ffcc00" : "#ff4444");
                rocketValue = $"<color={rocketColor}>{missilesRemaining}</color>";
            }
            elements.Add(new CuiLabel
            {
                Text = { 
                    Text = $"<color=#cccccc>Rockets:</color> {rocketValue}", 
                    FontSize = 11, 
                    Align = TextAnchor.MiddleCenter, 
                    Color = "1 1 1 1",
                    Font = "robotocondensed-bold.ttf"
                },
                RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" }
            }, HUD_PANEL + "_RocketBG");
            if (config.EnableBombs)
            {
                elements.Add(new CuiPanel
                {
                    Image = { Color = "0.1 0.1 0.1 0.7" },
                    RectTransform = { AnchorMin = "0.52 0.1", AnchorMax = "0.76 0.9" }
                }, HUD_PANEL, HUD_PANEL + "_BombBG");
                string bombValue;
                if (bombReloading)
                {
                    bombValue = "<color=#ffaa00>RELOADING</color>";
                }
                else
                {
                    string bombColor = bombsRemaining > 2 ? "#aa44ff" : (bombsRemaining > 0 ? "#ffcc00" : "#ff4444");
                    bombValue = $"<color={bombColor}>{bombsRemaining}</color>";
                }
                elements.Add(new CuiLabel
                {
                    Text = {
                        Text = $"<color=#cccccc>Bombs:</color> {bombValue}",
                        FontSize = 11,
                        Align = TextAnchor.MiddleCenter,
                        Color = "1 1 1 1",
                        Font = "robotocondensed-bold.ttf"
                    },
                    RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" }
                }, HUD_PANEL + "_BombBG");
            }
            if (config.EnableRocketBoost)
            {
                elements.Add(new CuiPanel
                {
                    Image = { Color = "0.1 0.1 0.1 0.7" },
                    RectTransform = { AnchorMin = "0.77 0.1", AnchorMax = "0.98 0.9" }
                }, HUD_PANEL, HUD_PANEL + "_BoostBG");
                string boostValue;
                if (boostRecharging)
                {
                    boostValue = "<color=#ffaa00>RECHARGING</color>";
                }
                else
                {
                    float boostPercent = (boostFuel / config.BoostFuelDuration) * 100f
// ... [Code preview truncated due to length. Full source in attached file] ...

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