Instructions to Refactor and Improve the Plugin
Review and consolidate similar functions: Identify functions with overlapping functionality and merge them to reduce redundancy.
Improve code readability: Refactor variable and function names to make them more descriptive and consistent.
Optimize performance:
Replace any inefficient algorithms or data structures with more optimal ones.
Minimize database queries by using caching where applicable.
Reduce the use of nested loops or heavy computations inside frequently executed code.
Separate concerns: Break down large functions into smaller, reusable functions. Adhere to the Single Responsibility Principle.
Organize the code structure: Group related functions into classes or modules for better maintainability.
Handle errors properly: Implement clear error handling and logging for easier debugging.
Eliminate dead code: Remove unused functions, variables, and files to declutter the codebase.
Follow coding standards: Ensure the code adheres to the best practices and style guides for the language/framework used.
Implement asynchronous processing: Where applicable, use asynchronous functions or multi-threading to avoid blocking the main execution flow.
Request feedback: Share the updated plugin with stakeholders or testers to gather suggestions for further improvement.
Implement a centralized configuration option (e.g., a global variable or a settings file) to toggle debug mode.
I want you to send me the complete code, without removing any parts. It needs to be entire and complete.



using System;
using System.Collections.Generic;
using System.Linq;
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Configuration;
using Rust;
using UnityEngine;
using Random = UnityEngine.Random;
using Time = UnityEngine.Time;

namespace Oxide.Plugins
{
    [Info("Dungeons", "Marte6", "1.0.0")]
    public class Dungeons : RustPlugin
    {
        private ConfigData _configData;
        private DynamicConfigFile _dataFile;
        private DungeonData _dungeonData;
        private Timer _autoSpawnTimer;
        private Timer _dungeonCheckTimer;
        private Timer _updateMarkersTimer;
        private Dictionary<DungeonTierConfig, string> _tierNameMap;
        private const ulong OwnerID = 1020304050;

        #region Chat Commands

        [ChatCommand("createdungeon")]
        private void AutoSpawnDungeonCommand(BasePlayer player, string command, string[] args)
        {
            if (player == null || !player.IsAdmin)
                return;

            player.ChatMessage(Msg(player.UserIDString, "AttemptSpawnDungeons"));

            var result = AutoSpawnDungeon();

            switch (result)
            {
                case DungeonSpawnResult.Success:
                    player.ChatMessage(Msg(player.UserIDString, "DungeonSpawnedSuccess"));
                    break;
                case DungeonSpawnResult.MaxActiveDungeonsReached:
                    player.ChatMessage(Msg(player.UserIDString, "MaxActiveDungeonsReached"));
                    break;
                case DungeonSpawnResult.NoValidSpawnLocation:
                    player.ChatMessage(Msg(player.UserIDString, "DungeonSpawnFailed"));
                    break;
            }
        }

        [ChatCommand("removeinactivedungeons")]
        private void RemoveInactiveDungeonCommand(BasePlayer player, string command, string[] args)
        {
            if (player != null && player.IsAdmin)
            {
                var removedDungeons = CheckDungeonsExistence();
                if (removedDungeons.Count == 0)
                {
                    player.ChatMessage(Msg(player.UserIDString, "NoInactiveDungeonsToRemove"));
                }
                else
                {
                    foreach (var dungeon in removedDungeons)
                    {
                        string gridPosition = PhoneController.PositionToGridCoord(dungeon.Position);
                        player.ChatMessage(Msg(player.UserIDString, "DungeonRemovedDetail", dungeon.TierName, gridPosition));
                    }
                }
            }
        }

        [ChatCommand("removedungeons")]
        private void RemoveDungeonCommand(BasePlayer player, string command, string[] args)
        {
            if (player != null && player.IsAdmin)
            {
                var removedDungeons = RemoveAllExistingDungeons();

                if (removedDungeons.Count == 0)
                {
                    player.ChatMessage(Msg(player.UserIDString, "NoDungeonsToRemove"));
                }
                else
                {
                    foreach (var dungeon in removedDungeons)
                    {
                        string gridPosition = PhoneController.PositionToGridCoord(dungeon.Position);
                        player.ChatMessage(Msg(player.UserIDString, "DungeonRemovedDetail", dungeon.TierName, gridPosition));
                    }
                }
            }
        }

        #endregion

        #region Initialization

        private void Init()
        {
            if (false)
            {
                foreach (var obj in UnityEngine.Object.FindObjectsOfType<VendingMachineMapMarker>())
                {
                    if (obj.OwnerID == OwnerID)
                        obj.Kill(BaseNetworkable.DestroyMode.None);
                }

                foreach (var obj in UnityEngine.Object.FindObjectsOfType<MapMarkerGenericRadius>())
                {
                    if (obj.OwnerID == OwnerID)
                        obj.Kill(BaseNetworkable.DestroyMode.None);
                }

                foreach (var obj in UnityEngine.Object.FindObjectsOfType<BasePortal>())
                {
                    obj.Kill(BaseNetworkable.DestroyMode.None);
                }

                foreach (var obj in UnityEngine.Object.FindObjectsOfType<ProceduralDynamicDungeon>())
                {
                    obj.Kill(BaseNetworkable.DestroyMode.None);
                }

                foreach (var obj in UnityEngine.Object.FindObjectsOfType<AutoTurret>())
                {
                    if (obj.OwnerID == OwnerID)
                        obj.Kill(BaseNetworkable.DestroyMode.None);
                }

                foreach (var obj in UnityEngine.Object.FindObjectsOfType<NPCPlayer>())
                {
                    if (obj.OwnerID == OwnerID)
                        obj.Kill(BaseNetworkable.DestroyMode.None);
                }

                foreach (var obj in UnityEngine.Object.FindObjectsOfType<StorageContainer>())
                {
                    if (obj.OwnerID == OwnerID)
                        obj.Kill(BaseNetworkable.DestroyMode.None);
                }
            }

            _tierNameMap = new Dictionary<DungeonTierConfig, string>
            {
                { _configData.Tiers.Easy, "Easy" },
                { _configData.Tiers.Normal, "Normal" },
                { _configData.Tiers.Medium, "Medium" },
                { _configData.Tiers.Hard, "Hard" },
                { _configData.Tiers.Nightmare, "Nightmare" },
            };

            LoadData();
            RemoveAllExistingDungeons();

            if (_configData.DungeonSpawn.EnableAutoSpawn)
            {
                StartAutoSpawnTimer();
            }

            StartDungeonCheckTimer();
            StartUpdateMarkersTimer();
        }

        private void LoadData()
        {
            _dataFile = Interface.Oxide.DataFileSystem.GetFile("Dungeons_Data");

            try
            {
                _dungeonData = _dataFile.ReadObject<DungeonData>() ?? new DungeonData();
            }
            catch
            {
                _dungeonData = new DungeonData();
            }
        }

        private void SaveData()
        {
            _dataFile.WriteObject(_dungeonData);
        }

        private List<ActiveDungeon> RemoveAllExistingDungeons()
        {
            var removedDungeons = new List<ActiveDungeon>();

            foreach (var dungeon in _dungeonData.ActiveDungeons.ToList())
            {
                var basePortal = BaseNetworkable.serverEntities.Find(new NetworkableId(dungeon.PortalId)) as BasePortal;
                if (basePortal != null)
                {
                    RemoveDungeonEntities(dungeon);
                    basePortal.Kill(BaseNetworkable.DestroyMode.None);
                }
                RemoveDungeonMarkers(dungeon);

                removedDungeons.Add(dungeon);
            }

            _dungeonData.ActiveDungeons.Clear();
            SaveData();

            return removedDungeons;
        }

        #endregion

        #region Hooks

        private object CanEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo)
        {
            return IsDungeonCreatedEntity(entity) ? (object)true : null;
        }

        private object OnEntityTakeDamage(BaseCombatEntity victim, HitInfo info)
        {
            if (victim == null || info == null || info.Initiator == null || !IsDungeonCreatedEntity(victim))
                return null;

            if (IsDungeonCreatedEntity(info.Initiator))
            {
                return false;
            }

            return null;
        }

        private bool IsDungeonCreatedEntity(BaseEntity entity)
        {
            return entity != null && _dungeonData.ActiveDungeons.Any(dungeon => dungeon.EntityIds.Contains(entity.net.ID.Value));
        }

        private void OnServerSave() => SaveData();

        #endregion

        #region Dungeon Management

        private void StartUpdateMarkersTimer()
        {
            _updateMarkersTimer = timer.Every(30f, UpdateMarkers);
        }

        private void UpdateMarkers()
        {
            IEnumerable<MapMarkerGenericRadius> genericMarkers = UnityEngine.Object.FindObjectsOfType<MapMarkerGenericRadius>();
            foreach (MapMarkerGenericRadius genericMarker in genericMarkers)
            {
                if (!(genericMarker == null || genericMarker.IsDestroyed || !genericMarker.IsFullySpawned()))
                {
                    genericMarker.SendUpdate();
                }
            }
        }

        private void StartAutoSpawnTimer()
        {
            _autoSpawnTimer = timer.Every(
                _configData.DungeonSpawn.AutoSpawnCycleInterval,
                () =>
                {
                    var result = AutoSpawnDungeon();

                    switch (result)
                    {
                        case DungeonSpawnResult.Success:
                            Puts("Dungeon spawned successfully.");
                            break;
                        case DungeonSpawnResult.MaxActiveDungeonsReached:
                            Puts("Cannot spawn: Maximum number of active dungeons reached.");
                            break;
                        case DungeonSpawnResult.NoValidSpawnLocation:
                            Puts("Could not find a valid location to spawn a new dungeon.");
                            break;
                    }
                }
            );
        }

        private void StartDungeonCheckTimer()
        {
            _dungeonCheckTimer = timer.Every(
                60f,
                () =>
                {
                    var removedDungeons = CheckDungeonsExistence();
                    if (removedDungeons.Count > 0)
                    {
                        foreach (var dungeon in removedDungeons)
                        {
                            string gridPosition = PhoneController.PositionToGridCoord(dungeon.Position);
                            Puts($"Removed inactive dungeon of tier {dungeon.TierName} at grid {gridPosition} during automated check.");
                        }
                    }
                    else
                    {
                        Puts("No inactive dungeons were removed during the automated check.");
                    }
                }
            );
        }

        private enum DungeonSpawnResult
        {
            Success,
            MaxActiveDungeonsReached,
            NoValidSpawnLocation,
        }

        private DungeonSpawnResult AutoSpawnDungeon()
        {
            int activeDungeons = _dungeonData.ActiveDungeons.Count;

            if (activeDungeons >= _configData.DungeonSpawn.MaxTotalActiveDungeons)
            {
                return DungeonSpawnResult.MaxActiveDungeonsReached;
            }

            if (TryFindDungeonSpawnPoint(out var position, out var rotation))
            {
                var selectedTier = CreateDungeon(position, rotation);
                if (selectedTier != null)
                {
                    _tierNameMap.TryGetValue(selectedTier, out string tierName);

                    CreateDungeonMarkers(position, tierName);
                    NotifyPlayersOfDungeonLocation(position, tierName);

                    return DungeonSpawnResult.Success;
                }
            }

            return DungeonSpawnResult.NoValidSpawnLocation;
        }

        private void NotifyPlayersOfDungeonLocation(Vector3 position, string tierName)
        {
            string gridPosition = PhoneController.PositionToGridCoord(position);
            List<BasePlayer> players = new(BasePlayer.activePlayerList);

            foreach (var player in players)
            {
                string message = Msg(player.UserIDString, "DungeonAppeared", tierName, gridPosition);
                player.ChatMessage(message);
                player.ShowToast(GameTip.Styles.Blue_Normal, message);
            }
        }

        private void CreateDungeonMarkers(Vector3 position, string tierName)
        {
            var dungeonVendingMarker = GameManager.server.CreateEntity("assets/prefabs/deployable/vendingmachine/vending_mapmarker.prefab", position) as VendingMachineMapMarker;
            dungeonVendingMarker.Spawn();
            dungeonVendingMarker.OwnerID = OwnerID;
            dungeonVendingMarker.markerShopName = $"Dungeon: {tierName}";
            dungeonVendingMarker.SendNetworkUpdate();

            var dungeonRadiusMarker = GameManager.server.CreateEntity("assets/prefabs/tools/map/genericradiusmarker.prefab", position) as MapMarkerGenericRadius;
            dungeonRadiusMarker.Spawn();
            dungeonRadiusMarker.OwnerID = OwnerID;
            dungeonRadiusMarker.alpha = 0.75f;
            dungeonRadiusMarker.radius = 0.5f;
            dungeonRadiusMarker.color2 = GetMarkerColorByTier(tierName);
            dungeonRadiusMarker.SendUpdate();
            dungeonRadiusMarker.SendNetworkUpdate();

            var marker = new DungeonMarker
            {
                VendingMarker = dungeonVendingMarker.net.ID.Value,
                RadiusMarker = dungeonRadiusMarker.net.ID.Value,
                Position = position,
                Tier = tierName,
            };

            _dungeonData.ActiveDungeonMarkers.Add(marker);
        }

        private Color GetMarkerColorByTier(string tierName)
        {
            return tierName switch
            {
                "Easy" => Color.green,
                "Normal" => Color.yellow,
                "Medium" => Color.yellow,
                "Hard" => Color.red,
                "Nightmare" => Color.black,
                _ => Color.white,
            };
        }

        private DungeonTierConfig CreateDungeon(Vector3 position, Quaternion rotation)
        {
            string prefabPath = RandomlySelectDungeonPrefab();
            if (string.IsNullOrEmpty(prefabPath))
            {
                return null;
            }

            var dungeon = GameManager.server.CreateEntity(prefabPath, position, rotation) as BasePortal;

            if (dungeon == null)
            {
                return null;
            }

            dungeon.OwnerID = OwnerID;
            dungeon.Spawn();

            var proceduralDungeon = (dungeon as XmasDungeon)?.dungeonInstance.Get(true) ?? (dungeon as HalloweenDungeon)?.dungeonInstance.Get(true);
            proceduralDungeon.OwnerID = OwnerID;

            if (proceduralDungeon == null || proceduralDungeon.spawnedCells.Count < 1)
            {
                dungeon.Kill(BaseNetworkable.DestroyMode.None);
                return null;
            }

            var selectedTier = DetermineDungeonTier(proceduralDungeon.spawnedCells.Count);

            var activeDungeon = new ActiveDungeon
            {
                PortalId = dungeon.net.ID.Value,
                DynamicDungeonId = proceduralDungeon.net.ID.Value,
                Position = position,
                TierName = _tierNameMap[selectedTier],
                EntityIds = new List<ulong>(),
            };

            NextTick(() =>
            {
                IntegrateEntitiesIntoDungeon(dungeon, selectedTier, activeDungeon);
            });

            _dungeonData.ActiveDungeons.Add(activeDungeon);

            return selectedTier;
        }

        private object OnPortalUses(BasePlayer player, BasePortal portal)
        {
            if (_dungeonData.ActiveDungeons.All(d => d.PortalId != portal.net.ID.Value))
            {
                return null;
            }

            var proceduralDungeon = (portal as XmasDungeon)?.dungeonInstance.Get(true) ?? (portal as HalloweenDungeon)?.dungeonInstance.Get(true);
            if (proceduralDungeon == null)
            {
                return null;
            }

            foreach (var cell in proceduralDungeon.spawnedCells)
            {
                foreach (var group in cell.spawnGroups)
                {
                    for (int num = group.spawnInstances.Count - 1; num >= 0; num--)
                    {
                        SpawnPointInstance spawnPointInstance = group.spawnInstances[num];
                        BaseEntity entity = spawnPointInstance?.gameObject?.ToBaseEntity();

                        if (entity != null && !IsDungeonCreatedEntity(entity) && (entity is NPCDwelling || entity is ScarecrowNPC || entity is LootContainer))
                        {
                            entity.Kill();
                        }
                    }
                }
            }
            return null;
        }

        private DungeonTierConfig DetermineDungeonTier(int cellCount)
        {
            return cellCount switch
            {
                < 5 => _configData.Tiers.Easy,
                < 8 => _configData.Tiers.Normal,
                < 12 => _configData.Tiers.Medium,
                < 16 => _configData.Tiers.Hard,
                _ => _configData.Tiers.Nightmare,
            };
        }

        private void RemoveDungeonEntities(ActiveDungeon activeDungeon)
        {
            foreach (var entityId in activeDungeon.EntityIds)
            {
                var entity = BaseNetworkable.serverEntities.Find(new NetworkableId(entityId)) as BaseEntity;
                if (entity != null && !entity.IsDestroyed)
                {
                    entity.Kill(BaseNetworkable.DestroyMode.None);
                }
            }

            activeDungeon.EntityIds.Clear();
        }

        private void RemoveDungeonMarkers(ActiveDungeon dungeon)
        {
            foreach (var marker in _dungeonData.ActiveDungeonMarkers.Where(m => m.Position == dungeon.Position).ToList())
            {
                var vendingMarker = BaseNetworkable.serverEntities.Find(new NetworkableId(marker.VendingMarker)) as BaseEntity;
                var radiusMarker = BaseNetworkable.serverEntities.Find(new NetworkableId(marker.RadiusMarker)) as BaseEntity;

                vendingMarker?.Kill(BaseNetworkable.DestroyMode.None);
                radiusMarker?.Kill(BaseNetworkable.DestroyMode.None);

                _dungeonData.ActiveDungeonMarkers.Remove(marker);
            }
        }

        private List<ActiveDungeon> CheckDungeonsExistence()
        {
            List<ActiveDungeon> removedDungeons = new List<ActiveDungeon>();

            foreach (var dungeon in _dungeonData.ActiveDungeons.ToList())
            {
                if (DungeonCanBeDeleted(dungeon))
                {
                    removedDungeons.Add(dungeon);
                    RemoveDungeon(dungeon);
                }
            }

            return removedDungeons;
        }

        private bool DungeonCanBeDeleted(ActiveDungeon dungeon)
        {
            bool allNpcsEliminated = CheckAllNpcEliminated(dungeon);
            bool noPlayersInside = CheckNoPlayersInside(dungeon);

            return allNpcsEliminated && noPlayersInside;
        }

        private bool CheckAllNpcEliminated(ActiveDungeon dungeon)
        {
            foreach (var npcId in dungeon.NpcIds)
            {
                var npc = BaseNetworkable.serverEntities.Find(new NetworkableId(npcId)) as BaseEntity;
                if (npc != null && !npc.IsDestroyed)
                {
                    return false;
                }
            }
            return true;
        }

        private bool CheckNoPlayersInside(ActiveDungeon dungeon)
        {
            var proceduralDynamicDungeon = BaseNetworkable.serverEntities.Find(new NetworkableId(dungeon.DynamicDungeonId)) as ProceduralDynamicDungeon;

            return proceduralDynamicDungeon != null && !proceduralDynamicDungeon.ContainsAnyPlayers();
        }

        private void RemoveDungeon(ActiveDungeon dungeon)
        {
            RemoveDungeonEntities(dungeon);
            RemoveDungeonMarkers(dungeon);

            _dungeonData.ActiveDungeons.Remove(dungeon);

            var basePortal = BaseNetworkable.serverEntities.Find(new NetworkableId(dungeon.PortalId)) as BasePortal;

            basePortal?.Kill(BaseNetworkable.DestroyMode.None);
        }

        private string RandomlySelectDungeonPrefab()
        {
            var availablePrefabs = new List<string>();

            if (_configData.DungeonSpawn.EnableXmasDungeon)
                availablePrefabs.Add("assets/prefabs/missions/portal/xmasportalentry.prefab");

            if (_configData.DungeonSpawn.EnableHalloweenDungeon)
                availablePrefabs.Add("assets/prefabs/missions/portal/halloweenportalentry.prefab");

            return availablePrefabs.Count == 0 ? null : availablePrefabs[Random.Range(0, availablePrefabs.Count)];
        }

        private void IntegrateEntitiesIntoDungeon(BasePortal dungeon, DungeonTierConfig tierConfig, ActiveDungeon activeDungeon)
        {
            var proceduralDungeon = (dungeon as XmasDungeon)?.dungeonInstance.Get(true) ?? (dungeon as HalloweenDungeon)?.dungeonInstance.Get(true);

            if (proceduralDungeon == null)
            {
                return;
            }

            var allSpawnEntries = GatherAllSpawnEntries(tierConfig);
            int totalEntities = allSpawnEntries.Count;
            int cellCount = proceduralDungeon.spawnedCells.Count;

            if (totalEntities == 0 || cellCount == 0)
            {
                return;
            }

            float exactRatio = (float)totalEntities / cellCount;
            int[] entitiesPerCell = new int[cellCount];
            int assignedEntities = 0;

            for (int i = 0; i < cellCount; i++)
            {
                entitiesPerCell[i] = (int)exactRatio;
                assignedEntities += entitiesPerCell[i];
            }

            int remainingEntities = totalEntities - assignedEntities;
            var random = new System.Random();
            while (remainingEntities > 0)
            {
                int cellIndex = random.Next(cellCount);
                entitiesPerCell[cellIndex]++;
                remainingEntities--;
            }

            int entryIndex = 0;
            for (int i = 0; i < cellCount; i++)
            {
                int entitiesInThisCell = entitiesPerCell[i];
                if (entitiesInThisCell > 0 && entryIndex + entitiesInThisCell <= totalEntities)
                {
                    var entriesForCell = allSpawnEntries.GetRange(entryIndex, entitiesInThisCell);
                    SetupSpawnGroup(proceduralDungeon.spawnedCells[i], entriesForCell, tierConfig, entitiesInThisCell, activeDungeon);
                    entryIndex += entitiesInThisCell;
                }
            }
        }

        private List<SpawnGroup.SpawnEntry> GatherAllSpawnEntries(DungeonTierConfig tierConfig)
        {
            var allSpawnEntries = new List<SpawnGroup.SpawnEntry>();

            foreach (var config in tierConfig.NpcSpawnConfigs)
            {
                if (GameManifest.pathToGuid.TryGetValue(config.PrefabName, out var guid))
                {
                    for (int i = 0; i < config.Total; i++)
                    {
                        allSpawnEntries.Add(
                            new SpawnGroup.SpawnEntry
                            {
                                prefab = new GameObjectRef { guid = guid },
                                weight = 1,
                                mobile = true,
                            }
                        );
                    }
                }
            }

            for (int i = 0; i < tierConfig.AutoTurretConfig.Total; i++)
            {
                if (GameManifest.pathToGuid.TryGetValue("assets/prefabs/npc/autoturret/autoturret_deployed.prefab", out var turretGuid))
                {
                    allSpawnEntries.Add(
                        new SpawnGroup.SpawnEntry
                        {
                            prefab = new GameObjectRef { guid = turretGuid },
                            weight = 1,
                            mobile = false,
                        }
                    );
                }
            }

            for (int i = 0; i < tierConfig.TotalLootBoxes; i++)
            {
                if (GameManifest.pathToGuid.TryGetValue("assets/prefabs/deployable/woodenbox/woodbox_deployed.prefab", out var boxGuid))
                {
                    allSpawnEntries.Add(
                        new SpawnGroup.SpawnEntry
                        {
                            prefab = new GameObjectRef { guid = boxGuid },
                            weight = 1,
                            mobile = false,
                        }
                    );
                }
            }

            return allSpawnEntries.OrderBy(x => Random.value).ToList();
        }

        private void SetupSpawnGroup(ProceduralDungeonCell cell, List<SpawnGroup.SpawnEntry> entries, DungeonTierConfig tierConfig, int population, ActiveDungeon activeDungeon)
        {
            var spawnGroup = cell.gameObject.AddComponent<SpawnGroup>();
            spawnGroup.prefabs = entries;
            spawnGroup.maxPopulation = population;
            spawnGroup.numToSpawnPerTickMin = 0;
            spawnGroup.numToSpawnPerTickMax = 0;
            spawnGroup.fillOnSpawn = true;
            spawnGroup.wantsInitialSpawn = true;
            spawnGroup.SpawnInitial();

            foreach (var instance in spawnGroup.spawnInstances)
            {
                var entity = instance.GetComponent<BaseEntity>();
                entity.OwnerID = OwnerID;
                if (entity != null && !activeDungeon.EntityIds.Contains(entity.net.ID.Value))
                {
                    activeDungeon.EntityIds.Add(entity.net.ID.Value);

                    if (entity is NPCPlayer)
                    {
                        activeDungeon.NpcIds.Add(entity.net.ID.Value);
                    }

                    if (entity is AutoTurret autoTurret)
                    {
                        ConfigureAutoTurret(autoTurret, tierConfig.AutoTurretConfig);
                        autoTurret.gameObject.AddComponent<TurretBehaviour>();
                    }

                    if (entity is StorageContainer box)
                    {
                        ConfigureLootBox(box);
                    }
                }
            }
        }

        private void ConfigureLootBox(StorageContainer box)
        {
            box.skinID = _configData.LootBoxConfig.SmallWoodBoxSkinID;
            AddLockToBox(box);
            FillLootBox(box.inventory, _configData.LootBoxConfig.LootItems);
        }

        private void AddLockToBox(StorageContainer box)
        {
            var codeLock = GameManager.server.CreateEntity("assets/prefabs/locks/keypad/lock.code.prefab") as CodeLock;
            if (codeLock != null)
            {
                codeLock.SetParent(box, box.GetSlotAnchorName(BaseEntity.Slot.Lock));
                codeLock.Spawn();
                codeLock.code = Random.Range(1000, 9999).ToString();
                codeLock.hasCode = true;
                codeLock.guestCode = string.Empty;
                codeLock.hasGuestCode = false;
                codeLock.guestPlayers.Clear();
                codeLock.whitelistPlayers.Clear();
                codeLock.SetFlag(BaseEntity.Flags.Locked, true);
            }
        }

        private void FillLootBox(ItemContainer container, List<ItemConfig> lootItems)
        {
            var shuffledItems = lootItems.OrderBy(_ => Random.value).ToList();
            int maxDifferentItems = _configData.LootBoxConfig.MaxDifferentItemsPerBox;
            int differentItemsCount = 0;

            foreach (var itemInfo in shuffledItems)
            {
                if (differentItemsCount >= maxDifferentItems)
                    break;

                if (Random.Range(0f, 100f) <= itemInfo.InclusionChancePercentage)
                {
                    var itemDefinition = ItemManager.FindItemDefinition(itemInfo.ShortName);
                    if (itemDefinition != null)
                    {
                        int amount = Random.Range(itemInfo.MinimumAmount, itemInfo.MaximumAmount + 1);

                        if (container.itemList.Count < container.capacity)
                        {
                            var item = ItemManager.Create(itemDefinition, amount);
                            item.MoveToContainer(container);
                            differentItemsCount++;
                        }
                    }
                }
            }
        }

        private void ConfigureAutoTurret(AutoTurret autoTurret, TurretConfig turretConfig)
        {
            autoTurret.health = turretConfig.Health;
            autoTurret.SetMaxHealth(autoTurret.health);

            var weapon = ItemManager.CreateByName(turretConfig.WeaponShortName);
            if (weapon != null)
            {
                weapon.MoveToContainer(autoTurret.inventory, 0);

                var laserSight = ItemManager.CreateByName("weapon.mod.lasersight");
                laserSight?.MoveToContainer(weapon.contents);
                autoTurret.UpdateAttachedWeapon();
            }
        }

        #endregion

        #region Utility and Cleanup

        private bool TryFindDungeonSpawnPoint(out Vector3 suitablePosition, out Quaternion suitableRotation)
        {
            float mapSize = World.Size;

            for (int attempt = 0; attempt < 1000; attempt++)
            {
                Vector3 candidatePosition = GenerateRandomPosition(mapSize);

                if (IsValidSpawnPosition(candidatePosition, out suitablePosition, out suitableRotation))
                {
                    if (IsSafeFromOtherDungeons(suitablePosition) && !IsNearMonuments(suitablePosition))
                        return true;
                }
            }

            suitablePosition = Vector3.zero;
            suitableRotation = Quaternion.identity;
            return false;
        }

        private Vector3 GenerateRandomPosition(float mapSize)
        {
            return new Vector3(Random.Range(-mapSize / 2, mapSize / 2), 0, Random.Range(-mapSize / 2, mapSize / 2));
        }

        private bool IsSafeFromOtherDungeons(Vector3 position)
        {
            foreach (var dungeon in _dungeonData.ActiveDungeons)
            {
                if (Vector3.Distance(position, dungeon.Position) < _configData.DungeonSpawn.MinDistanceBetweenDungeons)
                {
                    return false;
                }
            }
            return true;
        }

        private bool IsNearMonuments(Vector3 position)
        {
            foreach (var monument in TerrainMeta.Path.Monuments)
            {
                if (Vector3.Distance(position, monument.transform.position) < _configData.DungeonSpawn.MinDistanceFromMonuments)
                {
                    return true;
                }
            }
            return false;
        }

        private bool IsValidSpawnPosition(Vector3 position, out Vector3 suitablePosition, out Quaternion suitableRotation)
        {
            suitablePosition = Vector3.zero;
            suitableRotation = Quaternion.identity;

            return TerrainUtilities.GetTerrainInfo(position, out var hitInfo)
                && !TerrainUtilities.InsideRock(position, _configData.DungeonSpawn.RocksAvoidanceRadius)
                && !TerrainUtilities.InWater(position)
                && !TerrainUtilities.OnRoadOrRail(position)
                && !TerrainUtilities.InNoBuildZone(position, _configData.DungeonSpawn.DistanceFromNoBuildZones)
                && SetPositionAndRotation(position, hitInfo, out suitablePosition, out suitableRotation);
        }

        private bool SetPositionAndRotation(Vector3 position, RaycastHit hitInfo, out Vector3 suitablePosition, out Quaternion suitableRotation)
        {
            suitablePosition = hitInfo.point;
            suitableRotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
            return true;
        }

        private void Unload()
        {
            RemoveAllExistingDungeons();
            _autoSpawnTimer?.Destroy();
            _dungeonCheckTimer?.Destroy();
            _updateMarkersTimer?.Destroy();
        }

        #endregion

        #region Terrain Utilities

        public static class TerrainUtilities
        {
            private const int WorldLayer = Layers.Mask.World;
            private const int TerrainLayer = Layers.Mask.Terrain;
            private const int PreventBuildingLayer = Layers.Mask.Prevent_Building;
            private const int ConstructionLayer = Layers.Mask.Construction;

            public static Vector3 GetRandomPosition(float minX, float maxX, float minZ, float maxZ)
            {
                float randomX = Random.Range(minX, maxX);
                float randomZ = Random.Range(minZ, maxZ);
                float y = TerrainMeta.HeightMap.GetHeight(new Vector3(randomX, 0, randomZ));

                return new Vector3(randomX, y, randomZ);
            }

            public static bool InsideRock(Vector3 position, float radius)
            {
                List<Collider> colliders = Pool.GetList<Collider>();
                Vis.Colliders(position, radius, colliders, WorldLayer, QueryTriggerInteraction.Ignore);

                bool isInsideRock = colliders.Any(collider =>
                    collider.name.Contains("rock", StringComparison.OrdinalIgnoreCase)
                    || collider.name.Contains("cliff", StringComparison.OrdinalIgnoreCase)
                    || collider.name.Contains("formation", StringComparison.OrdinalIgnoreCase)
                );

                Pool.FreeList(ref colliders);
                return isInsideRock;
            }

            public static bool InRadTown(Vector3 position)
            {
                return TerrainMeta.Path.Monuments.Any(monument => monument.IsInBounds(position) && monument.shouldDisplayOnMap)
                    || (TerrainMeta.TopologyMap.GetTopology(position) & (int)TerrainTopology.Enum.Monument) != 0;
            }

            public static bool HasEntityNearby(Vector3 position, float radius, int mask, string prefabName = null)
            {
                List<Collider> hitColliders = Pool.GetList<Collider>();
                GamePhysics.OverlapSphere(position, radius, hitColliders, mask, QueryTriggerInteraction.Ignore);

                bool result = hitColliders.Any(collider =>
                {
                    BaseEntity entity = collider.gameObject.ToBaseEntity();
                    return entity != null && (prefabName == null || entity.PrefabName == prefabName);
                });

                Pool.FreeList(ref hitColliders);
                return result;
            }

            public static bool InWater(Vector3 position)
            {
                return WaterLevel.Test(position, false, false);
            }

            public static bool OnRoadOrRail(Vector3 position)
            {
                int topology = TerrainMeta.TopologyMap.GetTopology(position);
                return (topology & (int)(TerrainTopology.Enum.Road | TerrainTopology.Enum.Roadside | TerrainTopology.Enum.Rail | TerrainTopology.Enum.Railside)) != 0;
            }

            public static bool GetTerrainInfo(Vector3 startPosition, out RaycastHit hitInfo, float range = 1f, LayerMask mask = default)
            {
                mask = mask == default ? TerrainLayer : mask;
                return Physics.Linecast(startPosition + Vector3.up * range, startPosition - Vector3.up * range, out hitInfo, mask);
            }

            public static bool InNoBuildZone(Vector3 position, float radius)
            {
                return Physics.CheckSphere(position, radius, PreventBuildingLayer, QueryTriggerInteraction.Ignore);
            }
        }
        #endregion

        #region Configuration

        private class ConfigData
        {
            [JsonProperty(PropertyName = "Dungeon Spawn Settings")]
            public DungeonSpawnSettings DungeonSpawn { get; set; } = new DungeonSpawnSettings();

            [JsonProperty(PropertyName = "Tiers")]
            public DungeonTiers Tiers { get; set; } = new DungeonTiers();

            [JsonProperty(PropertyName = "Loot Box Config")]
            public LootBoxConfig LootBoxConfig { get; set; } = new LootBoxConfig();

            [JsonProperty(PropertyName = "Version")]
            public VersionNumber Version { get; set; }
        }

        private class DungeonTiers
        {
            [JsonProperty(PropertyName = "Easy")]
            public DungeonTierConfig Easy { get; set; } =
                new DungeonTierConfig
                {
                    TotalLootBoxes = 1,
                    NpcSpawnConfigs = new List<NpcSpawnConfig>
                    {
                        new NpcSpawnConfig { PrefabName = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_full_pistol.prefab", Total = 1 },
                    },
                    AutoTurretConfig = new TurretConfig
                    {
                        Total = 1,
                        Health = 300,
                        WeaponShortName = "pistol.revolver",
                    },
                };

            [JsonProperty(PropertyName = "Normal")]
            public DungeonTierConfig Normal { get; set; } =
                new DungeonTierConfig
                {
                    TotalLootBoxes = 2,
                    NpcSpawnConfigs = new List<NpcSpawnConfig>
                    {
                        new NpcSpawnConfig { PrefabName = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_full_pistol.prefab", Total = 3 },
                    },
                    AutoTurretConfig = new TurretConfig
                    {
                        Total = 2,
                        Health = 600,
                        WeaponShortName = "smg.2",
                    },
                };

            [JsonProperty(PropertyName = "Medium")]
            public DungeonTierConfig Medium { get; set; } =
                new DungeonTierConfig
                {
                    TotalLootBoxes = 4,
                    NpcSpawnConfigs = new List<NpcSpawnConfig>
                    {
                        new NpcSpawnConfig { PrefabName = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_full_pistol.prefab", Total = 4 },
                    },
                    AutoTurretConfig = new TurretConfig
                    {
                        Total = 3,
                        Health = 1000,
                        WeaponShortName = "smg.mp5",
                    },
                };

            [JsonProperty(PropertyName = "Hard")]
            public DungeonTierConfig Hard { get; set; } =
                new DungeonTierConfig
                {
                    TotalLootBoxes = 5,
                    NpcSpawnConfigs = new List<NpcSpawnConfig>
                    {
                        new NpcSpawnConfig { PrefabName = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_full_pistol.prefab", Total = 4 },
                        new NpcSpawnConfig { PrefabName = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_full_pistol.prefab", Total = 3 },
                    },
                    AutoTurretConfig = new TurretConfig
                    {
                        Total = 4,
                        Health = 1500,
                        WeaponShortName = "rifle.ak",
                    },
                };

            [JsonProperty(PropertyName = "Nightmare")]
            public DungeonTierConfig Nightmare { get; set; } =
                new DungeonTierConfig
                {
                    TotalLootBoxes = 6,
                    NpcSpawnConfigs = new List<NpcSpawnConfig>
                    {
                        new NpcSpawnConfig { PrefabName = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_full_pistol.prefab", Total = 5 },
                        new NpcSpawnConfig { PrefabName = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_full_pistol.prefab", Total = 5 },
                    },
                    AutoTurretConfig = new TurretConfig
                    {
                        Total = 6,
                        Health = 2000,
                        WeaponShortName = "rifle.ak",
                    },
                };
        }

        private class DungeonTierConfig
        {
            [JsonProperty(PropertyName = "Maximum Loot Boxes")]
            public int TotalLootBoxes { get; set; }

            [JsonProperty(PropertyName = "NPC Spawn Configs", ObjectCreationHandling = ObjectCreationHandling.Replace)]
            public List<NpcSpawnConfig> NpcSpawnConfigs { get; set; }

            [JsonProperty(PropertyName = "Auto Turret Config")]
            public TurretConfig AutoTurretConfig { get; set; } = new TurretConfig();
        }

        public class LootBoxConfig
        {
            [JsonProperty(PropertyName = "Small Wood Box Skin ID")]
            public ulong SmallWoodBoxSkinID { get; set; } = 2998755525;

            [JsonProperty(PropertyName = "Max Different Items Per Box")]
            public int MaxDifferentItemsPerBox { get; set; } = 6;

            [JsonProperty(PropertyName = "Loot Items", ObjectCreationHandling = ObjectCreationHandling.Replace)]
            public List<ItemConfig> LootItems { get; set; } =
                new List<ItemConfig>
                {
                    new ItemConfig
                    {
                        ShortName = "ammo.rifle",
                        InclusionChancePercentage = 15,
                        MinimumAmount = 100,
                        MaximumAmount = 300,
                    }
                };
        }

        private class NpcSpawnConfig
        {
            [JsonProperty(PropertyName = "Prefab")]
            public string PrefabName { get; set; }

            [JsonProperty(PropertyName = "Maximum")]
            public int Total { get; set; }
        }

        private class TurretConfig
        {
            [JsonProperty(PropertyName = "Maximum")]
            public int Total { get; set; } = 3;

            [JsonProperty(PropertyName = "Health")]
            public float Health { get; set; } = 1000f;

            [JsonProperty(PropertyName = "Weapon Short Name")]
            public string WeaponShortName { get; set; } = "rifle.ak";
        }

        private class DungeonSpawnSettings
        {
            [JsonProperty(PropertyName = "Enable Xmas Dungeon")]
            public bool EnableXmasDungeon { get; set; } = true;

            [JsonProperty(PropertyName = "Enable Halloween Dungeon")]
            public bool EnableHalloweenDungeon { get; set; } = true;

            [JsonProperty(PropertyName = "Max Total Active Dungeons")]
            public int MaxTotalActiveDungeons { get; set; } = 4;

            [JsonProperty(PropertyName = "Min Distance From Monuments")]
            public float MinDistanceFromMonuments { get; set; } = 100f;

            [JsonProperty(PropertyName = "Min Distance Between Dungeons")]
            public float MinDistanceBetweenDungeons { get; set; } = 50f;

            [JsonProperty(PropertyName = "Dungeon Spawn Interval")]
            public float DungeonSpawnInterval { get; set; } = 3f;

            [JsonProperty(PropertyName = "Auto Spawn Cycle Interval")]
            public float AutoSpawnCycleInterval { get; set; } = 60f;

            [JsonProperty(PropertyName = "Enable Auto Spawn")]
            public bool EnableAutoSpawn { get; set; } = true;

            [JsonProperty(PropertyName = "Nearby Entities Avoidance Radius")]
            public float NearbyEntitiesAvoidanceRadius { get; set; } = 10f;

            [JsonProperty(PropertyName = "Rocks Avoidance Radius")]
            public float RocksAvoidanceRadius { get; set; } = 10f;

            [JsonProperty(PropertyName = "Distance From No Build Zones")]
            public float DistanceFromNoBuildZones { get; set; } = 10f;
        }

        public class ItemConfig
        {
            [JsonProperty(PropertyName = "ShortName")]
            public string ShortName { get; set; }

            [JsonProperty(PropertyName = "Inclusion Chance Percentage")]
            public float InclusionChancePercentage { get; set; }

            [JsonProperty(PropertyName = "Minimum Amount")]
            public int MinimumAmount { get; set; }

            [JsonProperty(PropertyName = "Maximum Amount")]
            public int MaximumAmount { get; set; }
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                _configData = Config.ReadObject<ConfigData>();
                if (_configData == null)
                {
                    LoadDefaultConfig();
                }
            }
            catch (Exception ex)
            {
                PrintError($"The configuration file is corrupted: {ex}");
                LoadDefaultConfig();
            }
            SaveConfig();
        }

        protected override void LoadDefaultConfig()
        {
            PrintWarning("Creating a new configuration file");
            _configData = new ConfigData();
            _configData.Version = Version;
        }

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

        #endregion

        #region Localization

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(
                new Dictionary<string, string>
                {
                    ["NoInactiveDungeonsToRemove"] = "There are no inactive dungeons to remove.",
                    ["NoDungeonsToRemove"] = "There are no dungeons to remove.",
                    ["DungeonRemovedDetail"] = "Removed dungeon of tier {0} at grid {1}.",
                    ["AttemptSpawnDungeons"] = "Attempting to spawn dungeons.",
                    ["DungeonAppeared"] = "A {0} Dungeon has appeared at grid {1}!",
                    ["DungeonSpawnFailed"] = "Could not find a valid location to spawn a new dungeon.",
                    ["DungeonSpawnedSuccess"] = "Dungeon spawned successfully.",
                    ["MaxActiveDungeonsReached"] = "Cannot spawn: Maximum number of active dungeons reached.",
                },
                this
            );
        }

        private string Msg(string userId, string key, params object[] args)
        {
            return string.Format(lang.GetMessage(key, this, userId), args);
        }

        #endregion

        #region Data Classes

        private class DungeonData
        {
            public List<ActiveDungeon> ActiveDungeons { get; set; } = new();
            public List<DungeonMarker> ActiveDungeonMarkers { get; set; } = new();
        }

        private class ActiveDungeon
        {
            public ulong PortalId { get; set; }
            public ulong DynamicDungeonId { get; set; }
            public Vector3 Position { get; set; }
            public string TierName { get; set; }
            public List<ulong> EntityIds { get; set; } = new();
            public List<ulong> NpcIds { get; set; } = new();
        }

        private class DungeonMarker
        {
            public ulong VendingMarker { get; set; }
            public ulong RadiusMarker { get; set; }
            public Vector3 Position { get; set; }
            public string Tier { get; set; }
        }

        #endregion

        #region Turret Behaviour Class

        public class TurretBehaviour : MonoBehaviour
        {
            private AutoTurret _turret;
            private const float SearchDistance = 15f;
            private const int MaxReserveAmmo = 1000;

            private void Awake()
            {
                InitializeTurret();
            }

            private void InitializeTurret()
            {
                _turret = GetComponent<AutoTurret>();
                var triggerCollider = _turret.targetTrigger.GetComponent<SphereCollider>();
                triggerCollider.enabled = false;

                _turret.SetPeacekeepermode(false);
                _turret.InitiateStartup();
                _turret.SetIsOnline(true);
                _turret.CancelInvoke(_turret.ServerTick);
                _turret.SetTarget(null);
                _turret.InvokeRepeating(PerformTurretCycle, 1f, 0.01f);
                _turret.InvokeRepeating(ScanForTargets, 2f, 1f);
                _turret.isLootable = false;
                _turret.dropFloats = false;
                _turret.dropsLoot = false;

                _turret.SendNetworkUpdateImmediate();
                InvokeRepeating(nameof(RefillAmmo), 2f, 30f);
            }

            private void RefillAmmo()
            {
                if (_turret.AttachedWeapon is not BaseProjectile baseProjectile || baseProjectile.primaryMagazine?.ammoType == null)
                    return;

                var ammoType = baseProjectile.primaryMagazine.ammoType;
                int currentAmmoCount = CalculateCurrentAmmoCount(ammoType);

                if (currentAmmoCount < MaxReserveAmmo)
                {
                    int ammoNeeded = MaxReserveAmmo - currentAmmoCount;
                    Item ammoItem = ItemManager.Create(ammoType, ammoNeeded);
                    ammoItem?.MoveToContainer(_turret.inventory);

                    _turret.UpdateTotalAmmo();
                    _turret.EnsureReloaded();
                    _turret.SendNetworkUpdateImmediate();
                }
            }

            private int CalculateCurrentAmmoCount(ItemDefinition ammoType)
            {
                return _turret.inventory.itemList.Where(item => item.info == ammoType).Sum(item => item.amount);
            }

            private void ScanForTargets()
            {
                var entityContents = _turret.targetTrigger.entityContents ??= new HashSet<BaseEntity>();
                entityContents.Clear();

                int foundTargets = BaseEntity.Query.Server.GetPlayersInSphereFast(transform.position, SearchDistance, AIBrainSenses.playerQueryResults, IsTargetValid);

                if (foundTargets == 0)
                    return;

                _turret.authDirty = true;

                for (int i = 0; i < foundTargets; i++)
                {
                    var player = AIBrainSenses.playerQueryResults[i];
                    if (Interface.CallHook("OnEntityEnter", _turret.targetTrigger, player) != null || player.IsSleeping() || (player.InSafeZone() && !player.IsHostile()))
                        continue;

                    entityContents.Add(player);
                }
            }

            private bool IsTargetValid(BasePlayer player) => player != null && !player.IsNpc;

            private void PerformTurretCycle()
            {
                if (_turret.isClient || _turret.IsDestroyed)
                    return;

                float deltaTime = (float)_turret.timeSinceLastServerTick;
                _turret.timeSinceLastServerTick = 0f;

                if (_turret.IsOnline() && !_turret.IsBeingControlled)
                {
                    if (!_turret.HasTarget())
                    {
                        _turret.IdleTick(deltaTime);
                    }
                    else
                    {
                        ExecuteTargetEngagement();
                    }
                }

                _turret.UpdateFacingToTarget(deltaTime);
                UpdateAmmoStatus();
            }

            private void ExecuteTargetEngagement()
            {
                if (Time.realtimeSinceStartup >= _turret.nextVisCheck)
                {
                    _turret.nextVisCheck = Time.realtimeSinceStartup + UnityEngine.Random.Range(0.2f, 0.3f);
                    _turret.targetVisible = _turret.ObjectVisible(_turret.target);

                    if (_turret.targetVisible)
                        _turret.lastTargetSeenTime = Time.realtimeSinceStartup;
                }

                _turret.EnsureReloaded();
                if (ShouldFireAtTarget())
                {
                    var weapon = _turret.GetAttachedWeapon();
                    FireWeapon(weapon);
                }

                ValidateTargetEngagement();
            }

            private bool ShouldFireAtTarget()
            {
                return Time.time >= _turret.nextShotTime
                    && _turret.targetVisible
                    && Mathf.Abs(_turret.AngleToTarget(_turret.target, _turret.currentAmmoGravity != 0f)) < _turret.GetMaxAngleForEngagement();
            }

            private void FireWeapon(BaseProjectile weapon)
            {
                if (weapon == null)
                {
                    _turret.nextShotTime = Time.time + 1f;
                    return;
                }

                if (weapon.primaryMagazine.contents > 0)
                {
                    _turret.FireAttachedGun(_turret.AimOffset(_turret.target), _turret.aimCone, null, _turret.PeacekeeperMode() ? _turret.target : null);
                    float delay = weapon.isSemiAuto ? weapon.repeatDelay * 1.5f : weapon.repeatDelay;
                    delay = weapon.ScaleRepeatDelay(delay);
                    _turret.nextShotTime = Time.time + delay;
                }
                else
                {
                    _turret.nextShotTime = Time.time + 5f;
                }
            }

            private void ValidateTargetEngagement()
            {
                var targetPlayer = _turret.target as BasePlayer;
                if (_turret.target != null && (!IsValidSteamPlayer(targetPlayer) || _turret.target.IsDead() || TimeExceedsLastSeen() || DistanceExceedsSightRange() || ShouldTargetBeReset()))
                {
                    _turret.SetTarget(null);
                }
            }

            private static bool IsValidSteamPlayer(BasePlayer targetPlayer) => targetPlayer != null && targetPlayer.userID.IsSteamId();

            private bool TimeExceedsLastSeen() => Time.realtimeSinceStartup - _turret.lastTargetSeenTime > 3f;

            private bool DistanceExceedsSightRange() => Vector3.Distance(_turret.transform.position, _turret.target.transform.position) > _turret.sightRange;

            private bool ShouldTargetBeReset() => _turret.PeacekeeperMode() && !_turret.IsEntityHostile(_turret.target);

            private void UpdateAmmoStatus()
            {
                if (_turret.totalAmmoDirty && Time.time > _turret.nextAmmoCheckTime)
                {
                    _turret.UpdateTotalAmmo();
                    _turret.totalAmmoDirty = false;
                    _turret.nextAmmoCheckTime = Time.time + 0.5f;
                }
            }

            private void OnDestroy()
            {
                CancelInvoke(nameof(RefillAmmo));
            }
        }

        #endregion
    }
}