Respan Dataset Explorer

Select one behavior. Every returned turn has one binary label: Present or Absent. Source: final dense boolean release.

5,167,182physical rows
86shards
0.00%qualified row coverage
0.00%qualified cell coverage
Random row JSON API

turns-00052.parquet:31308

a54364d746152a1fc9dabbb0
turn 8/15gpt-4o-2024-08-06EnglishBrazil5074 words
degenerate_repetitionAbsentFinal dense release
USER
não faz sentido, tente achar o problema, estou mandando tudo novamente:
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 Dictionary<DungeonTierConfig, string> _tierNameMap;
        const float minDistanceBetweenDungeons = 50f;

        #region Initialization

        private void Init()
        {
            _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.AutoSpawn.EnableAutoSpawn)
            {
                StartAutoSpawnTimer();
            }

            StartDungeonCheckTimer();
        }

        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 void RemoveAllExistingDungeons()
        {
            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);
            }

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

        #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

        [ChatCommand("removedun")]
        private void RemoveDungeonCommand(BasePlayer player, string command, string[] args)
        {
            if (player != null && player.IsAdmin)
            {
                RemoveAllExistingDungeons();
                player.ChatMessage(Msg(player.UserIDString, "AllDungeonsRemoved"));
            }
        }

        [ChatCommand("dun")]
        private void AutoSpawnDungeonCommand(BasePlayer player, string command, string[] args)
        {
            if (player != null && player.IsAdmin)
            {
                player.ChatMessage(Msg(player.UserIDString, "AttemptSpawnDungeons"));
                AutoSpawnDungeon();
            }
        }

        private void StartAutoSpawnTimer()
        {
            _autoSpawnTimer = timer.Every(60f, AutoSpawnDungeon);
        }

        private void StartDungeonCheckTimer()
        {
            _dungeonCheckTimer = timer.Every(60f, CheckDungeonsExistence);
        }

        private void AutoSpawnDungeon()
        {
            int activeDungeons = _dungeonData.ActiveDungeons.Count;
            int dungeonsToSpawn = _configData.MaxTotalActiveDungeons - activeDungeons + 1;

            for (int i = 0; i < dungeonsToSpawn; i++)
            {
                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);
                    }
                }
            }
        }

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

            MapMarkerGenericRadius dungeonRadiusMarker = GameManager.server.CreateEntity("assets/prefabs/tools/map/genericradiusmarker.prefab", position) as MapMarkerGenericRadius;
            dungeonRadiusMarker.Spawn();
            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 void NotifyPlayersOfDungeonLocation(Vector3 position, string tierName)
        {
            string gridPosition = PhoneController.PositionToGridCoord(position);
            foreach (var player in BasePlayer.activePlayerList)
            {
                string message = Msg(player.UserIDString, "DungeonAppeared", tierName, gridPosition);

                Puts($"Formatted Message: {message}");
                //player.ChatMessage(message);
                //player.ShowToast(GameTip.Styles.Blue_Normal, message);
            }
        }

        private DungeonTierConfig CreateDungeon(Vector3 position, Quaternion rotation)
        {
            string prefabPath = RandomlySelectDungeonPrefab();
            if (string.IsNullOrEmpty(prefabPath))
            {
                Puts("Failed to select a valid dungeon prefab.");
                return null;
            }

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

            if (dungeon == null)
            {
                Puts("Failed to create dungeon entity.");
                return null;
            }

            dungeon.Spawn();

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

            if (proceduralDungeon == null || proceduralDungeon.spawnedCells.Count < 1)
            {
                Puts("Insufficient cells in dungeon or failed to retrieve instance, destroying dungeon.");
                dungeon.Kill(BaseNetworkable.DestroyMode.None);
                return null;
            }

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

            var activeDungeon = new ActiveDungeon
            {
                PortalId = dungeon.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 OnPortalUse(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)
            {
                Puts($"No valid dungeon instance associated with the portal.");
                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 void CheckDungeonsExistence()
        {
            foreach (var dungeon in _dungeonData.ActiveDungeons.ToList())
            {
                var basePortal = BaseNetworkable.serverEntities.Find(new NetworkableId(dungeon.PortalId)) as BasePortal;
                if (basePortal == null || basePortal.IsDestroyed)
                {
                    Puts($"Dungeon at {dungeon.Position} no longer exists. Cleaning up.");
                    RemoveDungeonMarkers(dungeon);
                    RemoveDungeonEntities(dungeon);
                    _dungeonData.ActiveDungeons.Remove(dungeon);
                }
            }
        }

        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)
            {
                Puts("Failed to get procedural dungeon instance.");
                return;
            }

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

            if (totalEntities == 0 || cellCount == 0)
            {
                Puts("No entities to spawn or no cells available.");
                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>();
                if (entity != null && !activeDungeon.EntityIds.Contains(entity.net.ID.Value))
                {
                    activeDungeon.EntityIds.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 < 100; attempt++)
            {
                Vector3 candidatePosition = new Vector3(Random.Range(-mapSize / 2, mapSize / 2), 0, Random.Range(-mapSize / 2, mapSize / 2));

                if (IsValidSpawnPosition(candidatePosition, out suitablePosition, out suitableRotation))
                {
                    bool tooClose = false;

                    foreach (var dungeon in _dungeonData.ActiveDungeons)
                    {
                        if (Vector3.Distance(suitablePosition, dungeon.Position) < minDistanceBetweenDungeons)
                        {
                            tooClose = true;
                            break;
                        }
                    }

                    if (!tooClose)
                        return true;
                }
            }

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

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

            if (TerrainUtilities.InsideRock(position, _configData.AutoSpawn.RocksAvoidanceRadius))
                return false;

            if (TerrainUtilities.InRadTown(position) || TerrainUtilities.HasEntityNearby(position, _configData.AutoSpawn.NearbyEntitiesAvoidanceRadius, Layers.Mask.Construction))
                return false;

            if (TerrainUtilities.InWater(position) || TerrainUtilities.OnRoadOrRail(position))
                return false;

            if (TerrainUtilities.InNoBuildZone(position, _configData.AutoSpawn.DistanceFromNoBuildZones))
                return false;

            if (TerrainUtilities.GetTerrainInfo(position, out var hitInfo))
            {
                suitablePosition = hitInfo.point;
                suitableRotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
                return true;
            }

            return false;
        }

        private void Unload()
        {
            SaveData();
            RemoveAllExistingDungeons();
            _autoSpawnTimer?.Destroy();
            _dungeonCheckTimer?.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 = "Auto Spawn Settings")]
            public AutoSpawnSettings AutoSpawn { get; set; } = new AutoSpawnSettings();

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

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

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

            [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_shotgun.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/banditguard/npc_bandit_guard.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_cargo_turret_lr300.prefab", Total = 4 },
                        new NpcSpawnConfig { PrefabName = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_cargo_turret_lr300.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_ch47_gunner.prefab", Total = 5 },
                        new NpcSpawnConfig { PrefabName = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_cargo_turret_lr300.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,
                    },
                    new ItemConfig
                    {
                        ShortName = "sulfur.ore",
                        InclusionChancePercentage = 15,
                        MinimumAmount = 100,
                        MaximumAmount = 300,
                    },
                    new ItemConfig
                    {
                        ShortName = "scrap",
                        InclusionChancePercentage = 70,
                        MinimumAmount = 500,
                        MaximumAmount = 2000,
                    },
                    new ItemConfig
                    {
                        ShortName = "rifle.ak",
                        InclusionChancePercentage = 5,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "rocket.launcher.dragon",
                        InclusionChancePercentage = 5,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "grenade.f1",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "crude.oil",
                        InclusionChancePercentage = 40,
                        MinimumAmount = 10,
                        MaximumAmount = 100,
                    },
                    new ItemConfig
                    {
                        ShortName = "diesel_barrel",
                        InclusionChancePercentage = 30,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "cctv.camera",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 5,
                        MaximumAmount = 20,
                    },
                    new ItemConfig
                    {
                        ShortName = "gingerbreadsuit",
                        InclusionChancePercentage = 5,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "gears",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 1,
                        MaximumAmount = 20,
                    },
                    new ItemConfig
                    {
                        ShortName = "metal.refined",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 10,
                        MaximumAmount = 80,
                    },
                    new ItemConfig
                    {
                        ShortName = "electric.furnace",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "fuse",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "xmas.door.garland",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "gunpowder",
                        InclusionChancePercentage = 50,
                        MinimumAmount = 50,
                        MaximumAmount = 150,
                    },
                    new ItemConfig
                    {
                        ShortName = "handcuffs",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "hazmatsuit",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "ammo.rocket.hv",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 20,
                    },
                    new ItemConfig
                    {
                        ShortName = "ammo.rifle.hv",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 100,
                        MaximumAmount = 300,
                    },
                    new ItemConfig
                    {
                        ShortName = "metal.facemask",
                        InclusionChancePercentage = 25,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "ammo.rocket.fire",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 5,
                        MaximumAmount = 10,
                    },
                    new ItemConfig
                    {
                        ShortName = "jackhammer",
                        InclusionChancePercentage = 30,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "rifle.l96",
                        InclusionChancePercentage = 5,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "furnace.large",
                        InclusionChancePercentage = 50,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "largemedkit",
                        InclusionChancePercentage = 50,
                        MinimumAmount = 1,
                        MaximumAmount = 10,
                    },
                    new ItemConfig
                    {
                        ShortName = "xmas.present.large",
                        InclusionChancePercentage = 40,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "locker",
                        InclusionChancePercentage = 40,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "lowgradefuel",
                        InclusionChancePercentage = 70,
                        MinimumAmount = 50,
                        MaximumAmount = 200,
                    },
                    new ItemConfig
                    {
                        ShortName = "rifle.lr300",
                        InclusionChancePercentage = 5,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "rifle.m39",
                        InclusionChancePercentage = 5,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "pistol.m92",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "lmg.m249",
                        InclusionChancePercentage = 2,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "syringe.medical",
                        InclusionChancePercentage = 70,
                        MinimumAmount = 10,
                        MaximumAmount = 30,
                    },
                    new ItemConfig
                    {
                        ShortName = "metal.ore",
                        InclusionChancePercentage = 80,
                        MinimumAmount = 100,
                        MaximumAmount = 300,
                    },
                    new ItemConfig
                    {
                        ShortName = "metalpipe",
                        InclusionChancePercentage = 60,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "metalspring",
                        InclusionChancePercentage = 60,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "minigun",
                        InclusionChancePercentage = 2,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "grenade.molotov",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "smg.mp5",
                        InclusionChancePercentage = 4,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "multiplegrenadelauncher",
                        InclusionChancePercentage = 15,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "mummymask",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "mushroom",
                        InclusionChancePercentage = 80,
                        MinimumAmount = 20,
                        MaximumAmount = 40,
                    },
                    new ItemConfig
                    {
                        ShortName = "weapon.mod.muzzleboost",
                        InclusionChancePercentage = 35,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "weapon.mod.muzzlebrake",
                        InclusionChancePercentage = 35,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "nightvisiongoggles",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "newyeargong",
                        InclusionChancePercentage = 25,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "hazmatsuit.nomadsuit",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "firework.boomer.pattern",
                        InclusionChancePercentage = 40,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "jar.pickle",
                        InclusionChancePercentage = 60,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "ammo.pistol",
                        InclusionChancePercentage = 70,
                        MinimumAmount = 100,
                        MaximumAmount = 500,
                    },
                    new ItemConfig
                    {
                        ShortName = "pookie.bear",
                        InclusionChancePercentage = 60,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "potato",
                        InclusionChancePercentage = 70,
                        MinimumAmount = 100,
                        MaximumAmount = 100,
                    },
                    new ItemConfig
                    {
                        ShortName = "pistol.revolver",
                        InclusionChancePercentage = 50,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "riflebody",
                        InclusionChancePercentage = 60,
                        MinimumAmount = 5,
                        MaximumAmount = 10,
                    },
                    new ItemConfig
                    {
                        ShortName = "roadsign.gloves",
                        InclusionChancePercentage = 40,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "roadsign.kilt",
                        InclusionChancePercentage = 40,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "ammo.rocket.basic",
                        InclusionChancePercentage = 30,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "rocket.launcher",
                        InclusionChancePercentage = 5,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "rope",
                        InclusionChancePercentage = 60,
                        MinimumAmount = 5,
                        MaximumAmount = 10,
                    },
                    new ItemConfig
                    {
                        ShortName = "icepick.salvaged",
                        InclusionChancePercentage = 30,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "explosive.satchel",
                        InclusionChancePercentage = 25,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "electric.seismicsensor",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "semibody",
                        InclusionChancePercentage = 60,
                        MinimumAmount = 5,
                        MaximumAmount = 10,
                    },
                    new ItemConfig
                    {
                        ShortName = "pistol.semiauto",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "rifle.semiauto",
                        InclusionChancePercentage = 5,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "sheetmetal",
                        InclusionChancePercentage = 70,
                        MinimumAmount = 1,
                        MaximumAmount = 9,
                    },
                    new ItemConfig
                    {
                        ShortName = "sewingkit",
                        InclusionChancePercentage = 65,
                        MinimumAmount = 1,
                        MaximumAmount = 10,
                    },
                    new ItemConfig
                    {
                        ShortName = "weapon.mod.silencer",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "weapon.mod.simplesight",
                        InclusionChancePercentage = 50,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "knife.skinning",
                        InclusionChancePercentage = 50,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "rifle.sks",
                        InclusionChancePercentage = 7,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "xmas.present.small",
                        InclusionChancePercentage = 40,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "smgbody",
                        InclusionChancePercentage = 60,
                        MinimumAmount = 5,
                        MaximumAmount = 9,
                    },
                    new ItemConfig
                    {
                        ShortName = "sofa",
                        InclusionChancePercentage = 30,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "hazmatsuit.spacesuit",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "spookyspeaker",
                        InclusionChancePercentage = 25,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "stones",
                        InclusionChancePercentage = 80,
                        MinimumAmount = 100,
                        MaximumAmount = 600,
                    },
                    new ItemConfig
                    {
                        ShortName = "sulfur",
                        InclusionChancePercentage = 80,
                        MinimumAmount = 100,
                        MaximumAmount = 600,
                    },
                    new ItemConfig
                    {
                        ShortName = "tactical.gloves",
                        InclusionChancePercentage = 35,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "tarp",
                        InclusionChancePercentage = 60,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "vampire.stake",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "vending.machine",
                        InclusionChancePercentage = 15,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "bottle.vodka",
                        InclusionChancePercentage = 9,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "gun.water",
                        InclusionChancePercentage = 9,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "piano",
                        InclusionChancePercentage = 9,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "firework.volcano",
                        InclusionChancePercentage = 30,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "hat.wolf",
                        InclusionChancePercentage = 50,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "worm",
                        InclusionChancePercentage = 70,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                };
        }

        private class AutoSpawnSettings
        {
            [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;
        }

        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; } = false;
        }

        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>
                {
                    ["AllDungeonsRemoved"] = "All dungeons removed.",
                    ["AttemptSpawnDungeons"] = "Attempting to spawn dungeons.",
                    ["DungeonAppeared"] = "A {0} Dungeon has appeared at {1}!",
                    ["NoPermission"] = "You don't have permission to use this command.",
                    ["DungeonSpawnFailed"] = "Failed to select a valid dungeon prefab.",
                    ["DungeonEntityCreationFailed"] = "Failed to create dungeon entity.",
                    ["DungeonRemoved"] = "Dungeon at {0} no longer exists. Cleaning up.",
                    ["DungeonSpawning"] = "Spawning dungeon...",
                    ["LootBoxFilled"] = "Loot box filled with items.",
                    ["AutoSpawnEnabled"] = "Auto-spawning dungeons every {0} seconds.",
                },
                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 Vector3 Position { get; set; }
            public string TierName { get; set; }
            public List<ulong> EntityIds { 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
    }
}
ASSISTANT
The error you are encountering (`FormatException: Index (zero based) must be greater than or equal to zero and less than the size of the argument list.`) indicates that there is a problem with the way placeholders in your string are being used compared to the arguments provided. Here’s how you can identify and fix the issue:

### Key Steps to Resolve the Issue

1. **Review the Message Format String**:
   - Ensure that the `DungeonAppeared` message has exactly two placeholders (`{0}` for `tierName` and `{1}` for `gridPosition`).

2. **Inspect the Call to `Msg` Method**:
   - Verify that **two** arguments are being passed correctly to match the two placeholders ({0} and {1}).

Let's revisit the specific code around message retrieval and usage, and ensure everything is correctly aligned:

### Code Review and Corrections

1. **Message Definition in `LoadDefaultMessages`:**

   Your message definitions seem correct given your `DungeonAppeared` entry. Here is the relevant part:

   ```csharp
   ["DungeonAppeared"] = "A {0} Dungeon has appeared at {1}!",
   ```

   Ensure this matches the number of arguments being passed.

2. **Method Usage:**

   In `NotifyPlayersOfDungeonLocation`, you have:

   ```csharp
   private void NotifyPlayersOfDungeonLocation(Vector3 position, string tierName)
   {
       string gridPosition = PhoneController.PositionToGridCoord(position);
       foreach (var player in BasePlayer.activePlayerList)
       {
           string message = Msg(player.UserIDString, "DungeonAppeared", tierName, gridPosition);

           Puts($"Formatted Message: {message}");
           //player.ChatMessage(message);
           //player.ShowToast(GameTip.Styles.Blue_Normal, message);
       }
   }
   ```

   This snippet shows that you're calling the message with the correct arguments: `tierName` and `gridPosition`, which should match `{0}` and `{1}` in your message string.

3. **Common Issues and Checks**:
   - **Misalignment**: Ensure the `tierName` and `gridPosition` are correctly passed and not altered elsewhere before reaching the `Msg` call.
   - **Ensure Correct Message Key**: Verify you're using the exact key registered in `LoadDefaultMessages` (`DungeonAppeared`).
   - **Resetting Cached Messages**: Sometimes plugins can have old message caches. Ensure your changes to messages are applied by reloading the plugin and possibly restarting the server.
   - **Language Files**: Confirm there are no overriding language JSON files with different placeholders.

4. **Debugging Check**:

   Given the function seems correctly constructed, add a fall-through check in your `Msg` method to catch misalignments:

   ```csharp
   private string Msg(string userId, string key, params object[] args)
   {
       try
       {
           return string.Format(lang.GetMessage(key, this, userId), args);
       }
       catch (FormatException ex)
       {
           PrintError($"Formatting error in message key {key}. Arguments provided: {args.Length}. Expected format: {lang.GetMessage(key, this)}");
           return $"[Error: {ex.Message}]";
       }
   }
   ```

This additional debug information will further guide where the misalignment is occurring by specifying the issue during runtime.

5. **Trigger Plugin Commands**:

   After making sure all the above steps are correct, run commands like `dun` or actions that would trigger this function to see if the debugging reveals more accurate information.

If the error persists and the above checks do not resolve it, verify all your external configurations and consider obtaining a clean server environment to ensure no other scripts or data is influencing behavior.

turns-00052.parquet:31309

e06405594d9f04247ce0c9f7
turn 9/15gpt-4o-2024-08-06EnglishBrazil4912 words
degenerate_repetitionAbsentFinal dense release
USER
agoar funcionou, quero agora fazer spawnar somente uma dungeon por vez:
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 Dictionary<DungeonTierConfig, string> _tierNameMap;
        const float minDistanceBetweenDungeons = 50f;

        #region Initialization

        private void Init()
        {
            _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.AutoSpawn.EnableAutoSpawn)
            {
                StartAutoSpawnTimer();
            }

            StartDungeonCheckTimer();
        }

        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 void RemoveAllExistingDungeons()
        {
            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);
            }

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

        #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

        [ChatCommand("removedun")]
        private void RemoveDungeonCommand(BasePlayer player, string command, string[] args)
        {
            if (player != null && player.IsAdmin)
            {
                RemoveAllExistingDungeons();
                player.ChatMessage(Msg(player.UserIDString, "AllDungeonsRemoved"));
            }
        }

        [ChatCommand("dun")]
        private void AutoSpawnDungeonCommand(BasePlayer player, string command, string[] args)
        {
            if (player != null && player.IsAdmin)
            {
                player.ChatMessage(Msg(player.UserIDString, "AttemptSpawnDungeons"));
                AutoSpawnDungeon();
            }
        }

        private void StartAutoSpawnTimer()
        {
            _autoSpawnTimer = timer.Every(60f, AutoSpawnDungeon);
        }

        private void StartDungeonCheckTimer()
        {
            _dungeonCheckTimer = timer.Every(60f, CheckDungeonsExistence);
        }

        private void AutoSpawnDungeon()
        {
            int activeDungeons = _dungeonData.ActiveDungeons.Count;
            int dungeonsToSpawn = _configData.MaxTotalActiveDungeons - activeDungeons + 1;

            for (int i = 0; i < dungeonsToSpawn; i++)
            {
                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);
                    }
                }
            }
        }

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

            MapMarkerGenericRadius dungeonRadiusMarker = GameManager.server.CreateEntity("assets/prefabs/tools/map/genericradiusmarker.prefab", position) as MapMarkerGenericRadius;
            dungeonRadiusMarker.Spawn();
            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 void NotifyPlayersOfDungeonLocation(Vector3 position, string tierName)
        {
            string gridPosition = PhoneController.PositionToGridCoord(position);

            foreach (var player in BasePlayer.activePlayerList)
            {
                string message = Msg(player.UserIDString, "DungeonAppeared", tierName, gridPosition);

                player.ChatMessage(message);
                player.ShowToast(GameTip.Styles.Blue_Normal, message);
            }
        }

        private DungeonTierConfig CreateDungeon(Vector3 position, Quaternion rotation)
        {
            string prefabPath = RandomlySelectDungeonPrefab();
            if (string.IsNullOrEmpty(prefabPath))
            {
                Puts("Failed to select a valid dungeon prefab.");
                return null;
            }

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

            if (dungeon == null)
            {
                Puts("Failed to create dungeon entity.");
                return null;
            }

            dungeon.Spawn();

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

            if (proceduralDungeon == null || proceduralDungeon.spawnedCells.Count < 1)
            {
                Puts("Insufficient cells in dungeon or failed to retrieve instance, destroying dungeon.");
                dungeon.Kill(BaseNetworkable.DestroyMode.None);
                return null;
            }

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

            var activeDungeon = new ActiveDungeon
            {
                PortalId = dungeon.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 OnPortalUse(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)
            {
                Puts($"No valid dungeon instance associated with the portal.");
                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 void CheckDungeonsExistence()
        {
            foreach (var dungeon in _dungeonData.ActiveDungeons.ToList())
            {
                var basePortal = BaseNetworkable.serverEntities.Find(new NetworkableId(dungeon.PortalId)) as BasePortal;
                if (basePortal == null || basePortal.IsDestroyed)
                {
                    Puts($"Dungeon at {dungeon.Position} no longer exists. Cleaning up.");
                    RemoveDungeonMarkers(dungeon);
                    RemoveDungeonEntities(dungeon);
                    _dungeonData.ActiveDungeons.Remove(dungeon);
                }
            }
        }

        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)
            {
                Puts("Failed to get procedural dungeon instance.");
                return;
            }

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

            if (totalEntities == 0 || cellCount == 0)
            {
                Puts("No entities to spawn or no cells available.");
                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>();
                if (entity != null && !activeDungeon.EntityIds.Contains(entity.net.ID.Value))
                {
                    activeDungeon.EntityIds.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 < 100; attempt++)
            {
                Vector3 candidatePosition = new Vector3(Random.Range(-mapSize / 2, mapSize / 2), 0, Random.Range(-mapSize / 2, mapSize / 2));

                if (IsValidSpawnPosition(candidatePosition, out suitablePosition, out suitableRotation))
                {
                    bool tooClose = false;

                    foreach (var dungeon in _dungeonData.ActiveDungeons)
                    {
                        if (Vector3.Distance(suitablePosition, dungeon.Position) < minDistanceBetweenDungeons)
                        {
                            tooClose = true;
                            break;
                        }
                    }

                    if (!tooClose)
                        return true;
                }
            }

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

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

            if (TerrainUtilities.InsideRock(position, _configData.AutoSpawn.RocksAvoidanceRadius))
                return false;

            if (TerrainUtilities.InRadTown(position) || TerrainUtilities.HasEntityNearby(position, _configData.AutoSpawn.NearbyEntitiesAvoidanceRadius, Layers.Mask.Construction))
                return false;

            if (TerrainUtilities.InWater(position) || TerrainUtilities.OnRoadOrRail(position))
                return false;

            if (TerrainUtilities.InNoBuildZone(position, _configData.AutoSpawn.DistanceFromNoBuildZones))
                return false;

            if (TerrainUtilities.GetTerrainInfo(position, out var hitInfo))
            {
                suitablePosition = hitInfo.point;
                suitableRotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
                return true;
            }

            return false;
        }

        private void Unload()
        {
            SaveData();
            RemoveAllExistingDungeons();
            _autoSpawnTimer?.Destroy();
            _dungeonCheckTimer?.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 = "Auto Spawn Settings")]
            public AutoSpawnSettings AutoSpawn { get; set; } = new AutoSpawnSettings();

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

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

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

            [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_shotgun.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/banditguard/npc_bandit_guard.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_cargo_turret_lr300.prefab", Total = 4 },
                        new NpcSpawnConfig { PrefabName = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_cargo_turret_lr300.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_ch47_gunner.prefab", Total = 5 },
                        new NpcSpawnConfig { PrefabName = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_cargo_turret_lr300.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,
                    },
                    new ItemConfig
                    {
                        ShortName = "sulfur.ore",
                        InclusionChancePercentage = 15,
                        MinimumAmount = 100,
                        MaximumAmount = 300,
                    },
                    new ItemConfig
                    {
                        ShortName = "scrap",
                        InclusionChancePercentage = 70,
                        MinimumAmount = 500,
                        MaximumAmount = 2000,
                    },
                    new ItemConfig
                    {
                        ShortName = "rifle.ak",
                        InclusionChancePercentage = 5,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "rocket.launcher.dragon",
                        InclusionChancePercentage = 5,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "grenade.f1",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "crude.oil",
                        InclusionChancePercentage = 40,
                        MinimumAmount = 10,
                        MaximumAmount = 100,
                    },
                    new ItemConfig
                    {
                        ShortName = "diesel_barrel",
                        InclusionChancePercentage = 30,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "cctv.camera",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 5,
                        MaximumAmount = 20,
                    },
                    new ItemConfig
                    {
                        ShortName = "gingerbreadsuit",
                        InclusionChancePercentage = 5,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "gears",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 1,
                        MaximumAmount = 20,
                    },
                    new ItemConfig
                    {
                        ShortName = "metal.refined",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 10,
                        MaximumAmount = 80,
                    },
                    new ItemConfig
                    {
                        ShortName = "electric.furnace",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "fuse",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "xmas.door.garland",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "gunpowder",
                        InclusionChancePercentage = 50,
                        MinimumAmount = 50,
                        MaximumAmount = 150,
                    },
                    new ItemConfig
                    {
                        ShortName = "handcuffs",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "hazmatsuit",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "ammo.rocket.hv",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 20,
                    },
                    new ItemConfig
                    {
                        ShortName = "ammo.rifle.hv",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 100,
                        MaximumAmount = 300,
                    },
                    new ItemConfig
                    {
                        ShortName = "metal.facemask",
                        InclusionChancePercentage = 25,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "ammo.rocket.fire",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 5,
                        MaximumAmount = 10,
                    },
                    new ItemConfig
                    {
                        ShortName = "jackhammer",
                        InclusionChancePercentage = 30,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "rifle.l96",
                        InclusionChancePercentage = 5,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "furnace.large",
                        InclusionChancePercentage = 50,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "largemedkit",
                        InclusionChancePercentage = 50,
                        MinimumAmount = 1,
                        MaximumAmount = 10,
                    },
                    new ItemConfig
                    {
                        ShortName = "xmas.present.large",
                        InclusionChancePercentage = 40,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "locker",
                        InclusionChancePercentage = 40,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "lowgradefuel",
                        InclusionChancePercentage = 70,
                        MinimumAmount = 50,
                        MaximumAmount = 200,
                    },
                    new ItemConfig
                    {
                        ShortName = "rifle.lr300",
                        InclusionChancePercentage = 5,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "rifle.m39",
                        InclusionChancePercentage = 5,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "pistol.m92",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "lmg.m249",
                        InclusionChancePercentage = 2,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "syringe.medical",
                        InclusionChancePercentage = 70,
                        MinimumAmount = 10,
                        MaximumAmount = 30,
                    },
                    new ItemConfig
                    {
                        ShortName = "metal.ore",
                        InclusionChancePercentage = 80,
                        MinimumAmount = 100,
                        MaximumAmount = 300,
                    },
                    new ItemConfig
                    {
                        ShortName = "metalpipe",
                        InclusionChancePercentage = 60,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "metalspring",
                        InclusionChancePercentage = 60,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "minigun",
                        InclusionChancePercentage = 2,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "grenade.molotov",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "smg.mp5",
                        InclusionChancePercentage = 4,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "multiplegrenadelauncher",
                        InclusionChancePercentage = 15,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "mummymask",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "mushroom",
                        InclusionChancePercentage = 80,
                        MinimumAmount = 20,
                        MaximumAmount = 40,
                    },
                    new ItemConfig
                    {
                        ShortName = "weapon.mod.muzzleboost",
                        InclusionChancePercentage = 35,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "weapon.mod.muzzlebrake",
                        InclusionChancePercentage = 35,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "nightvisiongoggles",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "newyeargong",
                        InclusionChancePercentage = 25,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "hazmatsuit.nomadsuit",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "firework.boomer.pattern",
                        InclusionChancePercentage = 40,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "jar.pickle",
                        InclusionChancePercentage = 60,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "ammo.pistol",
                        InclusionChancePercentage = 70,
                        MinimumAmount = 100,
                        MaximumAmount = 500,
                    },
                    new ItemConfig
                    {
                        ShortName = "pookie.bear",
                        InclusionChancePercentage = 60,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "potato",
                        InclusionChancePercentage = 70,
                        MinimumAmount = 100,
                        MaximumAmount = 100,
                    },
                    new ItemConfig
                    {
                        ShortName = "pistol.revolver",
                        InclusionChancePercentage = 50,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "riflebody",
                        InclusionChancePercentage = 60,
                        MinimumAmount = 5,
                        MaximumAmount = 10,
                    },
                    new ItemConfig
                    {
                        ShortName = "roadsign.gloves",
                        InclusionChancePercentage = 40,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "roadsign.kilt",
                        InclusionChancePercentage = 40,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "ammo.rocket.basic",
                        InclusionChancePercentage = 30,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "rocket.launcher",
                        InclusionChancePercentage = 5,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "rope",
                        InclusionChancePercentage = 60,
                        MinimumAmount = 5,
                        MaximumAmount = 10,
                    },
                    new ItemConfig
                    {
                        ShortName = "icepick.salvaged",
                        InclusionChancePercentage = 30,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "explosive.satchel",
                        InclusionChancePercentage = 25,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "electric.seismicsensor",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "semibody",
                        InclusionChancePercentage = 60,
                        MinimumAmount = 5,
                        MaximumAmount = 10,
                    },
                    new ItemConfig
                    {
                        ShortName = "pistol.semiauto",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "rifle.semiauto",
                        InclusionChancePercentage = 5,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "sheetmetal",
                        InclusionChancePercentage = 70,
                        MinimumAmount = 1,
                        MaximumAmount = 9,
                    },
                    new ItemConfig
                    {
                        ShortName = "sewingkit",
                        InclusionChancePercentage = 65,
                        MinimumAmount = 1,
                        MaximumAmount = 10,
                    },
                    new ItemConfig
                    {
                        ShortName = "weapon.mod.silencer",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "weapon.mod.simplesight",
                        InclusionChancePercentage = 50,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "knife.skinning",
                        InclusionChancePercentage = 50,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "rifle.sks",
                        InclusionChancePercentage = 7,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "xmas.present.small",
                        InclusionChancePercentage = 40,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "smgbody",
                        InclusionChancePercentage = 60,
                        MinimumAmount = 5,
                        MaximumAmount = 9,
                    },
                    new ItemConfig
                    {
                        ShortName = "sofa",
                        InclusionChancePercentage = 30,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "hazmatsuit.spacesuit",
                        InclusionChancePercentage = 10,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "spookyspeaker",
                        InclusionChancePercentage = 25,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "stones",
                        InclusionChancePercentage = 80,
                        MinimumAmount = 100,
                        MaximumAmount = 600,
                    },
                    new ItemConfig
                    {
                        ShortName = "sulfur",
                        InclusionChancePercentage = 80,
                        MinimumAmount = 100,
                        MaximumAmount = 600,
                    },
                    new ItemConfig
                    {
                        ShortName = "tactical.gloves",
                        InclusionChancePercentage = 35,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "tarp",
                        InclusionChancePercentage = 60,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "vampire.stake",
                        InclusionChancePercentage = 20,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "vending.machine",
                        InclusionChancePercentage = 15,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "bottle.vodka",
                        InclusionChancePercentage = 9,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "gun.water",
                        InclusionChancePercentage = 9,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "piano",
                        InclusionChancePercentage = 9,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "firework.volcano",
                        InclusionChancePercentage = 30,
                        MinimumAmount = 1,
                        MaximumAmount = 5,
                    },
                    new ItemConfig
                    {
                        ShortName = "hat.wolf",
                        InclusionChancePercentage = 50,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                    new ItemConfig
                    {
                        ShortName = "worm",
                        InclusionChancePercentage = 70,
                        MinimumAmount = 1,
                        MaximumAmount = 1,
                    },
                };
        }

        private class AutoSpawnSettings
        {
            [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;
        }

        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; } = false;
        }

        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>
                {
                    ["AllDungeonsRemoved"] = "All dungeons removed.",
                    ["AttemptSpawnDungeons"] = "Attempting to spawn dungeons.",
                    ["DungeonAppeared"] = "A {0} Dungeon has appeared at {1}!",
                    ["NoPermission"] = "You don't have permission to use this command.",
                    ["DungeonSpawnFailed"] = "Failed to select a valid dungeon prefab.",
                    ["DungeonEntityCreationFailed"] = "Failed to create dungeon entity.",
                    ["DungeonRemoved"] = "Dungeon at {0} no longer exists. Cleaning up.",
                    ["DungeonSpawning"] = "Spawning dungeon...",
                    ["LootBoxFilled"] = "Loot box filled with items.",
                    ["AutoSpawnEnabled"] = "Auto-spawning dungeons every {0} seconds.",
                },
                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 Vector3 Position { get; set; }
            public string TierName { get; set; }
            public List<ulong> EntityIds { 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
    }
}
ASSISTANT
To ensure that only one dungeon spawns at a time, you can modify the `AutoSpawnDungeon` method to check if there are already active dungeons and return early if so. Here’s how you can modify your script to achieve that:

### Modifications to Limit Dungeon to One Active Dungeon

1. **Change in `AutoSpawnDungeon` Method**: Alter the method to only spawn if there are no active dungeons.

Here's the modified method:

```csharp
private void AutoSpawnDungeon()
{
    if (_dungeonData.ActiveDungeons.Count > 0)
    {
        Puts("A dungeon is already active. Not spawning another.");
        return;
    }

    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);
        }
    }
}
```

### Explanation:

- **Early Exit**: The method checks if `_dungeonData.ActiveDungeons.Count > 0` and exits early if true, meaning a dungeon is already active.
- **Single Dungeon Creation**: If no dungeons are active, it proceeds to find a spawn point and create a dungeon.

### Additional Considerations:

- **Command for Admin**: Ensure the same logic is respected when using the admin command. This way, an admin cannot spawn additional dungeons unless the current one is removed.

```csharp
[ChatCommand("dun")]
private void AutoSpawnDungeonCommand(BasePlayer player, string command, string[] args)
{
    if (player != null && player.IsAdmin)
    {
        if (_dungeonData.ActiveDungeons.Count > 0)
        {
            player.ChatMessage("There is already an active dungeon. Use /removedun first to remove it before spawning a new one.");
            return;
        }

        player.ChatMessage(Msg(player.UserIDString, "AttemptSpawnDungeons"));
        AutoSpawnDungeon();
    }
}
```

- **Feedback Logging**: Use informational messages (like `Puts`) to log actions, which can be useful for monitoring plugin behavior.

With these changes, the plugin ensures that only one dungeon is active at any time and prevents additional dungeons from being spawned until the existing one is cleared. This approach manages server resources effectively and prevents issues related to having multiple active dungeons.

turns-00052.parquet:31310

76945adda753e4136eddc881
turn 10/15gpt-4o-2024-08-06EnglishBrazil385 words
degenerate_repetitionAbsentFinal dense release
USER
não, o que quero na verdade é que a mensagem que vai para o usuário tenha um delay entre as msgs
ASSISTANT
If you want to introduce a delay between messages sent to users notifying them of dungeon locations, you can implement a coroutine to handle the delays in sending messages. Since Unity-based systems like Rust's use a built-in coroutine system, you can utilize Unity’s `MonoBehaviour` functions such as `StartCoroutine` and `WaitForSeconds` to achieve message delays.

Here's how you can integrate such a functionality in your plugin:

### Implementing Delayed Messages

1. **Create a Coroutine for Message Sending**:

   Add a coroutine to handle sending messages with a delay for each player.

```csharp
private IEnumerator SendDelayedMessages(Vector3 position, string tierName, float delay)
{
    string gridPosition = PhoneController.PositionToGridCoord(position);

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

        yield return new WaitForSeconds(delay);  // Waits for the specified delay time before proceeding to the next player
    }
}
```

2. **Modify `NotifyPlayersOfDungeonLocation` Method**:

   Instead of directly sending messages, start the coroutine.

```csharp
private void NotifyPlayersOfDungeonLocation(Vector3 position, string tierName)
{
    StartCoroutine(SendDelayedMessages(position, tierName, 1.0f));  // 1.0f is the delay time in seconds between messages
}
```

### Explanation

- **Coroutine**: The `SendDelayedMessages` coroutine iterates over all active players, sending them the dungeon notification message, and waits for a specified delay (`WaitForSeconds`) before proceeding to the next player.
  
- **Delay Parameter**: The `delay` parameter in `SendDelayedMessages` controls how long to wait between sending messages to each player. You can adjust this to your preference.

- **Running the Coroutine**: Use `StartCoroutine()` to begin the coroutine in `NotifyPlayersOfDungeonLocation`.

### Considerations

- **Adjusting Delay**: Fine-tune the `WaitForSeconds` parameter within `SendDelayedMessages` based on your preference to control how noticeably delayed the messages are.

- **Coroutines with Oxide**: Note that not all situations support using Unity's `Coroutine` feature directly with Oxide without some additional handling, although Rust does utilize the Unity engine. If there are issues with coroutine usage, consider implementing your message queuing manually using timers.

- **Message Order**: Ensure your logic correctly reflects the game’s events sequence; mesh any changes with events that might overlap during the delay.

By using coroutine-based delayed messaging, you can create a smooth, non-intrusive notification flow that sends messages to players in a staggered manner, respecting network conditions and providing a more controlled user experience.

turns-00052.parquet:31311

d8012d10d586ac98b742c735
turn 11/15gpt-4o-2024-08-06EnglishBrazil302 words
degenerate_repetitionAbsentFinal dense release
USER
Using the generic type 'IEnumerator<T>' requires 1 type argumentsCS0305
ASSISTANT
The error `CS0305: Using the generic type 'IEnumerator<T>' requires 1 type arguments` is because `IEnumerator` without a type argument isn't a generic interface, but a non-generic one in C#. When using coroutines in Unity or similar environments, you simply use `IEnumerator` without any type parameters. So, let's correct the example:

### Correct Use of IEnumerator for Coroutines

1. **Correct Coroutine Signature**:

   When defining the coroutine for delay purposes, use the non-generic `IEnumerator` type:

```csharp
private IEnumerator SendDelayedMessages(Vector3 position, string tierName, float delay)
{
    string gridPosition = PhoneController.PositionToGridCoord(position);

    foreach (var player in BasePlayer.activePlayerList)
    {
        string message = Msg(player.UserIDString, "DungeonAppeared", tierName, gridPosition);

        player.ChatMessage(message);
        player.ShowToast(GameTip.Styles.Blue_Normal, message);

        yield return new WaitForSeconds(delay);  // Waits for the specified delay time before proceeding to the next player
    }
}
```

2. **Using Coroutine**:

   Ensure to start the coroutine correctly from within your plugin:

```csharp
private void NotifyPlayersOfDungeonLocation(Vector3 position, string tierName)
{
    StartCoroutine(SendDelayedMessages(position, tierName, 1.0f));  // 1.0f is the delay time in seconds between messages
}
```

### Explanation

- **`IEnumerator`**: In the context of coroutines or yielding in C#, coroutines should use `IEnumerator` without a type parameter. The generic variant (`IEnumerator<T>`) is used for iterating collections in a different context.

- **Usage of `WaitForSeconds`**: This line effectively pauses the coroutine for the given time (`delay`) before sending the message to the next player.

- **Integration with Plugins**: Since Rust plugins using Oxide are often scripts rather than full Unity projects, direct coroutine operations may sometimes require integration with a `MonoBehaviour`. If there are issues running this directly, you might need to use Oxide's timer system or find a workaround related to script context.

Keep in mind that coroutine handling might be unique in Oxide/Rust environments. If `StartCoroutine` is not available, explore using Oxide timers to implement queued or delayed executions.

turns-00052.parquet:31312

ddfc0b134e4d4a1c94e553cb
turn 12/15gpt-4o-2024-08-06EnglishBrazil320 words
degenerate_repetitionAbsentFinal dense release
USER
The name 'StartCoroutine' does not exist in the current contextCS0103
Using the generic type 'IEnumerator<T>' requires 1 type argumentsCS0305
ASSISTANT
In the context of an Oxide plugin for Rust, you don't have access to Unity's `MonoBehaviour` methods like `StartCoroutine` directly, because plugins do not extend `MonoBehaviour`. Instead, you can achieve a similar delay functionality using Oxide's `timer` system.

Here's how you can implement delayed message sending using Oxide's `timer`:

### Implement Delayed Messages Using `timer`

1. **Create a Method to Send Messages with Delays**:

   You'll create a method that uses a timer to introduce delays between message sends.

```csharp
private void NotifyPlayersOfDungeonLocation(Vector3 position, string tierName)
{
    string gridPosition = PhoneController.PositionToGridCoord(position);
    List<BasePlayer> players = new List<BasePlayer>(BasePlayer.activePlayerList);
    
    SendMessagesWithDelay(players, tierName, gridPosition, 1.0f); // Delay of 1.0 seconds between messages
}

private void SendMessagesWithDelay(List<BasePlayer> players, string tierName, string gridPosition, float delay, int index = 0)
{
    if (index < players.Count)
    {
        var player = players[index];
        string message = Msg(player.UserIDString, "DungeonAppeared", tierName, gridPosition);

        player.ChatMessage(message);
        player.ShowToast(GameTip.Styles.Blue_Normal, message);

        // Schedule the next message send
        timer.Once(delay, () => SendMessagesWithDelay(players, tierName, gridPosition, delay, index + 1));
    }
}
```

### Explanation

- **Recursive Timer**: The `SendMessagesWithDelay` function is recursive and uses Oxide's `timer.Once` to create a delay. For each player, it sends a message, waits for the delay, and then calls itself for the next player.

- **No Coroutine Needed**: Instead of using coroutines, use the `timer` API provided by Oxide to schedule delayed tasks.

- **Parameters**: 
  - `players` is a list of players currently active.
  - `tierName` and `gridPosition` are the message parameters.
  - `delay` controls the time between sending messages to each player.
  - `index` tracks which player should receive a message next. 

### Integrate with Plugin

Make sure you initialize and save plugin data where necessary, and that your plugin handles any concurrent or asynchronous processes safely. This method should now work seamlessly within the confines of Oxide's scripting environment, providing the delayed messaging feature you described.

turns-00052.parquet:31313

aa7e10b282b1ebac4ffafc42
turn 13/15gpt-4o-2024-08-06EnglishBrazil424 words
degenerate_repetitionAbsentFinal dense release
USER
parece que a lógica está errada aqui, está mandando as msg e depois mandando denovo com dalay:
        private void NotifyPlayersOfDungeonLocation(Vector3 position, string tierName)
        {
            string gridPosition = PhoneController.PositionToGridCoord(position);
            List<BasePlayer> players = new List<BasePlayer>(BasePlayer.activePlayerList);

            SendMessagesWithDelay(players, tierName, gridPosition, 2.0f);
        }

        private void SendMessagesWithDelay(List<BasePlayer> players, string tierName, string gridPosition, float delay, int index = 0)
        {
            if (index < players.Count)
            {
                var player = players[index];
                string message = Msg(player.UserIDString, "DungeonAppeared", tierName, gridPosition);

                player.ChatMessage(message);
                player.ShowToast(GameTip.Styles.Blue_Normal, message);

                timer.Once(delay, () => SendMessagesWithDelay(players, tierName, gridPosition, delay, index + 1));
            }
        }
ASSISTANT
If you're seeing messages sent immediately and then again with a delay, it suggests that something in your logic might be triggering the notification twice. Let’s correct the approach to ensure that messages are only sent once per player, with the desired delay between sending to each player.

Below is a refined approach using Oxide's timer to send messages to each player with a delay between each message:

### Refined Message Sending Logic with Delay

This method schedules the message for each player in sequence, applying the delay only between subsequent player notifications.

```csharp
private void NotifyPlayersOfDungeonLocation(Vector3 position, string tierName)
{
    string gridPosition = PhoneController.PositionToGridCoord(position);
    List<BasePlayer> players = new List<BasePlayer>(BasePlayer.activePlayerList);
    
    // Start sending messages with a delay between each
    SendMessagesWithDelay(players, tierName, gridPosition, 1.0f); // Adjust delay as needed
}

private void SendMessagesWithDelay(List<BasePlayer> players, string tierName, string gridPosition, float delay)
{
    // Iterate through players and send messages with a delay
    for (int i = 0; i < players.Count; i++)
    {
        var player = players[i];
        
        // Calculate the total delay before sending this player's message
        float totalDelay = i * delay; 

        // Schedule message sending
        timer.Once(totalDelay, () =>
        {
            string message = Msg(player.UserIDString, "DungeonAppeared", tierName, gridPosition);
            player.ChatMessage(message);
            player.ShowToast(GameTip.Styles.Blue_Normal, message);
        });
    }
}
```

### Explanation

- **Iterative Delay Calculation**: Instead of a recursive call, we iterate over all players and calculate when each should receive the message. This uses the loop index `i` to stagger the message dispatch with `i * delay`, ensuring each player only receives one message after a calculated total delay based on their position in the list.

- **Delay Parameter**: Adjust the delay to control how fast or slow the messages should be spaced.

- **Single Timer for Each Player**: Each player is given a unique time offset for message delivery, avoiding the problem of overlapping sends or double messaging.

This logic ensures that each player receives the notification once, sequentially, with the specified delay between each message. This approach is more efficient and less error-prone when handling ordered or timed operations within the Oxide plugin framework.

turns-00052.parquet:31314

df88d687e001c9263ca7aed5
turn 14/15gpt-4o-2024-08-06EnglishBrazil555 words
degenerate_repetitionAbsentFinal dense release
USER
não está adiantando, aqui os  métodos:
  private void AutoSpawnDungeon()
        {
            int activeDungeons = _dungeonData.ActiveDungeons.Count;
            int dungeonsToSpawn = _configData.MaxTotalActiveDungeons - activeDungeons + 1;

            for (int i = 0; i < dungeonsToSpawn; i++)
            {
                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);
                    }
                }
            }
        }

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

            // Start sending messages with a delay between each
            SendMessagesWithDelay(players, tierName, gridPosition, 2.0f); // Adjust delay as needed
        }

        private void SendMessagesWithDelay(List<BasePlayer> players, string tierName, string gridPosition, float delay)
        {
            // Iterate through players and send messages with a delay
            for (int i = 0; i < players.Count; i++)
            {
                var player = players[i];

                // Calculate the total delay before sending this player's message
                float totalDelay = i * delay;

                // Schedule message sending
                timer.Once(
                    totalDelay,
                    () =>
                    {
                        string message = Msg(player.UserIDString, "DungeonAppeared", tierName, gridPosition);
                        player.ChatMessage(message);
                        player.ShowToast(GameTip.Styles.Blue_Normal, message);
                    }
                );
            }
        }
ASSISTANT
To address the issue where messages might be sent multiple times or otherwise incorrectly timed, let's take a closer look at how the `SendMessagesWithDelay` method can be responsible and optimize its usage:

### Troubleshooting and Optimization Steps

1. **Ensure Timers Use Correct Player Reference**:
   - Closure pitfalls can arise when using loops inside timers because all closures might use the same `player` variable (leading to it potentially being the last in the loop).
   
2. **Use of Loop Variables in Closures**:
   - Capture the player object explicitly inside the loop to ensure it retains the specific instance for each loop iteration.

### Adjusted Code with Explicit Player Capture

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

    // Start sending messages with a delay between each
    SendMessagesWithDelay(players, tierName, gridPosition, 2.0f); // Adjust delay as needed
}

private void SendMessagesWithDelay(List<BasePlayer> players, string tierName, string gridPosition, float delay)
{
    // Iterate through players and send messages with a delay
    for (int i = 0; i < players.Count; i++)
    {
        // Capture the player object in the current loop iteration
        var player = players[i];
        
        // Calculate the total delay before sending this player's message
        float totalDelay = i * delay;

        // Schedule message sending for each individual player with a correctly captured player
        timer.Once(totalDelay, () =>
        {
            string message = Msg(player.UserIDString, "DungeonAppeared", tierName, gridPosition);
            player.ChatMessage(message);
            player.ShowToast(GameTip.Styles.Blue_Normal, message);
        });
    }
}
```

### Key Adjustments

- **Variable Capturing**:
  - By copying `var player = players[i];` inside the loop, you ensure each timer has its own reference to each specific player. This eliminates issues where the timers might reference the last loop iteration's player.

- **To Debug**:
  - You could add a `Puts` line inside the timer callback to verify the player and time for when a message is scheduled, confirming expected behavior:
    ```csharp
    timer.Once(totalDelay, () =>
    {
        Puts($"Sending message to {player.displayName} after {totalDelay} seconds.");
        ...
    });
    ```
  
This approach handles timing effectively by ensuring each player's message is scheduled with the appropriate delay, as originally intended by your plugin design, while avoiding common closure issues in C#. 

### Additional Considerations

- Make sure `Msg` function and `PhoneController.PositionToGridCoord(position)` operate as expected.
- Double-check for any other parts of the code that might trigger `NotifyPlayersOfDungeonLocation` unintentionally or simultaneously, leading to potential duplicate scheduling.

turns-00052.parquet:31315

3c1149a33db09622b8317653
turn 15/15gpt-4o-2024-08-06EnglishBrazil683 words
degenerate_repetitionAbsentFinal dense release
USER
ainda não está ok, acho que precisa ajustar AutoSpawnDungeon também:

   private void AutoSpawnDungeon()
        {
            int activeDungeons = _dungeonData.ActiveDungeons.Count;
            int dungeonsToSpawn = _configData.MaxTotalActiveDungeons - activeDungeons + 1;

            for (int i = 0; i < dungeonsToSpawn; i++)
            {
                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);
                    }
                }
            }
        }

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

            // Start sending messages with a delay between each
            SendMessagesWithDelay(players, tierName, gridPosition, 2.0f); // Adjust delay as needed
        }

        private void SendMessagesWithDelay(List<BasePlayer> players, string tierName, string gridPosition, float delay)
        {
            // Iterate through players and send messages with a delay
            for (int i = 0; i < players.Count; i++)
            {
                // Capture the player object in the current loop iteration
                var player = players[i];

                // Calculate the total delay before sending this player's message
                float totalDelay = i * delay;

                // Schedule message sending for each individual player with a correctly captured player
                timer.Once(
                    totalDelay,
                    () =>
                    {
                        string message = Msg(player.UserIDString, "DungeonAppeared", tierName, gridPosition);
                        player.ChatMessage(message);
                        player.ShowToast(GameTip.Styles.Blue_Normal, message);
                    }
                );
            }
        }
ASSISTANT
To fix the logic for spawning only the required number of dungeons and notifying players with a delay, you need to ensure:

1. **Spawning Logic in `AutoSpawnDungeon`:** 
   - Ensure you are handling the correct number of dungeons based on current active dungeons and the configured limit.

2. **Player Notification Logic:**
   - Ensure that messages are sent in a sequence with delays correctly.

### Revised Implementation

1. **Adjust the Dungeon Spawning Logic:**

   First, ensure that the number of dungeons calculated to spawn is correct and logical:

```csharp
private void AutoSpawnDungeon()
{
    // Only spawn new dungeons if the count is less than the configured maximum
    int activeDungeons = _dungeonData.ActiveDungeons.Count;
    int dungeonsToSpawn = _configData.MaxTotalActiveDungeons - activeDungeons;

    if (dungeonsToSpawn > 0)
    {
        for (int i = 0; i < dungeonsToSpawn; i++)
        {
            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);
                }
            }
        }
    }
}
```

2. **Ensure Correct Message Delivery:**

   The current setup for `NotifyPlayersOfDungeonLocation` and `SendMessagesWithDelay` seems generally correct for sending notifications, especially with capturing player references in the loop. However, the double message issue may arise from some concurrency or nested call error.

   Here's a recheck of the message notification section if issues persist:

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

    // Ensure messages are only sent once for each player
    SendMessagesWithDelay(players, tierName, gridPosition, 2.0f); // Adjust delay as needed
}

private void SendMessagesWithDelay(List<BasePlayer> players, string tierName, string gridPosition, float delay)
{
    // Iterate through players and ensure messages are sent with a delay
    for (int i = 0; i < players.Count; i++)
    {
        // Capture the player object in the current loop iteration
        var player = players[i];

        // Calculate the correct delay before sending this player's message
        float totalDelay = i * delay;

        // Schedule message sending via timer for each player individually
        timer.Once(totalDelay, () =>
        {
            string message = Msg(player.UserIDString, "DungeonAppeared", tierName, gridPosition);
            player.ChatMessage(message);
            player.ShowToast(GameTip.Styles.Blue_Normal, message);
        });
    }
}
```

### Explanation & Considerations

- **Correct Spawn Calculation**: Ensure the logic reflects the correct number of dungeons pending creation.
  
- **Sequential Messaging**: The messaging with a delay should work theoretically but if there are concurrency issues or race conditions (e.g., if this method is being called in rapid succession possibly due to external triggers), the logic controlling these calls should be checked.

- **Debugging**: Add `Puts` logs right before `NotifyPlayersOfDungeonLocation` call and inside `SendMessagesWithDelay` to confirm the flow:

    ```csharp
    Puts($"Spawning dungeon at position {position} with tier {tierName}");
    ```

    ```csharp
    Puts($"Scheduled message for player {player.displayName} with delay of {totalDelay} seconds");
    ```

- **External Interactions**: Assess which hooks or external calls might trigger `AutoSpawnDungeon` or `NotifyPlayersOfDungeonLocation` more than expected.

In summary, verify the control structure around dungeon population to prevent erroneous or repeated invocation, and utilize logging to ensure and follow the message dispatch flow per player from start to completion.

turns-00052.parquet:31316

0257ab0be11f287dcd7fdaaa
turn 1/4gpt-4o-2024-08-06ItalianItaly397 words
degenerate_repetitionAbsentFinal dense release
USER
ipotizza come usare dei giochi linguistici alla scuola dell'infanzia per parlare della violenza di genere
ASSISTANT
Introdurre concetti complessi come la violenza di genere alla scuola dell'infanzia richiede un approccio attento, sensibile e adatto all'età dei bambini. I giochi linguistici possono essere strumenti efficaci per promuovere l'uguaglianza, sviluppare la consapevolezza delle emozioni e incoraggiare il rispetto reciproco tra i bambini. Ecco alcune idee su come utilizzare i giochi linguistici per trattare questi argomenti:

1. **Giochi di Ruolo con Storie**: Creare delle piccole storie in cui i personaggi mostrano rispetto, gentilezza e amicizia. I bambini possono interpretare i vari personaggi, imparando attraverso l'azione come interagire in modo positivo. Le storie possono essere seguite da domande guidate per discutere cosa significa aiutarsi a vicenda.

2. **Completa la Frase**: Preparare delle frasi aperte che i bambini devono completare, come "Quando vedo qualcuno triste, io...", in modo da stimolare la riflessione e il dialogo su come supportare gli altri e trattarli con gentilezza.

3. **Giochi di Immaginazione**: Usare immagini o pupazzi per raccontare storie che evidenziano il rispetto e la collaborazione tra personaggi diversi. Dopo la narrazione, i bambini possono inventare una fine diversa, esplorando varie modalità di risolvere conflitti o situazioni di disagio.

4. **Giochi di Parole con Emozioni**: Associare emozioni a parole specifiche e creare un gioco in cui i bambini devono identificare e descrivere queste emozioni. Questo può aiutare ad aumentare la consapevolezza emotiva e la comprensione dei propri sentimenti e di quelli altrui.

5. **Indovina Chi? con Caratteristiche Positive**: Descrivere comportamenti positivi o qualità di una persona e far indovinare ai bambini di chi si sta parlando. Questo può promuovere l'idea di riconoscere e apprezzare gli aspetti positivi negli altri.

6. **Canzoni e Filastrocche**: Utilizzare canzoni o filastrocche che parlano di amicizia, rispetto e cooperazione. Queste possono essere accompagnate da gesti o movimenti per rendere l'apprendimento divertente e coinvolgente, aiutando a fissare i messaggi positivi nella mente dei bambini.

7. **Giochi di Squadra**: Attività in cui i bambini devono collaborare per raggiungere un obiettivo comune, imparando così l'importanza del lavoro di squadra e del rispetto reciproco.

È essenziale che queste attività siano condotte in un ambiente sicuro e supportivo, dove i bambini possano esprimersi liberamente e senza giudizio. Le discussioni dovrebbero essere agevolate dagli educatori, che possono fornire spiegazioni semplici e utilizzare esempi concreti pertinenti all'età. L'obiettivo è instaurare fin da piccoli valori di rispetto, empatia e uguaglianza.

turns-00052.parquet:31317

0cd09a4689aa035f361cfcf6
turn 2/4gpt-4o-2024-08-06ItalianItaly463 words
degenerate_repetitionAbsentFinal dense release
USER
fammi degli esempi concreti di attività
ASSISTANT
Certamente! Ecco alcuni esempi concreti di attività che possono essere svolte alla scuola dell'infanzia per affrontare temi come il rispetto e la cooperazione, in modo adatto all'età dei bambini:

1. **La Scatola delle Emozioni**:
   - **Materiali**: Una scatola colorata, cartoncini colorati, pennarelli.
   - **Attività**: Gli educatori introducono varie emozioni (come felicità, tristezza, rabbia, sorpresa) e chiedono ai bambini di disegnare una faccia su ciascun cartoncino che rappresenti una di queste emozioni. Successivamente, i cartoncini vengono messi nella scatola delle emozioni.
   - **Obiettivo**: Ogni giorno, un bambino può scegliere un cartoncino e condividerlo con il gruppo, spiegando una situazione in cui ha provato quell'emozione. Questo incoraggia i bambini a riconoscere e comprendere le proprie emozioni e quelle degli altri.

2. **La Giornata dell'Amico Speciale**:
   - **Materiali**: Carta, colori, adesivi.
   - **Attività**: Ogni bambino sceglie un "amico speciale" della classe e crea un piccolo biglietto o disegno per esprimergli un apprezzamento. Gli educatori possono aiutare fornendo frasi guida come "Grazie per..." o "Mi piace quando tu...".
   - **Obiettivo**: Promuovere la gentilezza e il riconoscimento delle qualità positive negli altri.

3. **Il Cerchio del Rispetto**:
   - **Materiali**: Nessuno specifico, solo uno spazio fisico per formare un cerchio.
   - **Attività**: Seduti in cerchio, l'educatore inizia dicendo una cosa che gli piace o rispetta di qualcun altro e poi passa la parola al bambino successivo, invitandolo a fare lo stesso con un compagno.
   - **Obiettivo**: Costruire un senso di comunità e riconoscere l'importanza del rispetto reciproco.

4. **Teatro delle Marionette**:
   - **Materiali**: Marionette o pupazzi, un piccolo palco (può essere una scatola aperta su un lato).
   - **Attività**: Gli educatori creano brevi scenette in cui i personaggi affrontano piccoli conflitti o dilemmi, risolvendoli attraverso il dialogo e la collaborazione. Dopo lo spettacolo, i bambini possono proporre continuazioni o alternative alla storia.
   - **Obiettivo**: Insegnare la risoluzione dei conflitti e la comunicazione positiva.

5. **Il Gioco della Ragnatela**:
   - **Materiali**: Un gomitolo di lana.
   - **Attività**: I bambini siedono in cerchio e un bambino inizia tenendo il filo del gomitolo e dicendo qualcosa di positivo su un compagno prima di passargli il gomitolo, mantenendo salda una parte del filo. Alla fine, si forma una "ragnatela" che connette tutti i bambini.
   - **Obiettivo**: Dimostrare visivamente come ogni bambino è connesso agli altri attraverso le sue azioni e parole positive.

6. **Gara di Complimenti**:
   - **Materiali**: Nessuno specifico.
   - **Attività**: In coppie o piccoli gruppi, i bambini devono dire il maggior numero possibile di complimenti o aspetti positivi sui loro partner nel tempo assegnato (ad esempio, 2 minuti).
   - **Obiettivo**: Incoraggiare un'atmosfera di positività e rispetto reciproco. 

Queste attività mirano a promuovere valori come la gentilezza, il rispetto e la cooperazione, fondamentali per la prevenzione dell'aggressività e per la costruzione di relazioni sane e positive.