refatore este plugin:
using System.Collections.Generic;
using Facepunch;
using Newtonsoft.Json;
using Rust;
using UnityEngine;
using Random = UnityEngine.Random;

namespace Oxide.Plugins
{
    [Info("ChristmasDungeon", "Marte6", "1.1.0")]
    public class ChristmasDungeon : RustPlugin
    {
        private static PluginConfig _config;
        private Timer _autoSpawnTimer;
        private const string DungeonPrefabPath = "assets/prefabs/missions/portal/xmasportalentry.prefab";

        private XmasDungeon _currentDungeon;

        private void Init()
        {
            LoadConfig();
            if (_config.EnableAutoSpawn)
            {
                StartAutoSpawnTimer();
            }
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            _config = Config.ReadObject<PluginConfig>();

            if (string.Compare(_config.Version, Version.ToString()) < 0)
            {
                UpdateConfig();
            }

            SaveConfig();
        }

        protected override void LoadDefaultConfig()
        {
            _config = GetDefaultConfig();
        }

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

        private void UpdateConfig()
        {
            PrintWarning("Config changes detected! Updating...");
            var defaultConfig = GetDefaultConfig();

            if (string.Compare(_config.Version, "1.1.0") < 0)
            {
                _config = defaultConfig;
            }

            PrintWarning($"Config update complete! Updated from version {_config.Version} to {Version.ToString()}");
            _config.Version = Version.ToString();
        }

        private PluginConfig GetDefaultConfig()
        {
            return new PluginConfig
            {
                Version = Version.ToString(),
                EnableAutoSpawn = true,
                AutoSpawnIntervalMinutes = 30f,
                MinSpawnDistance = 6f,
                MaxSpawnDistance = 20f,
                MaxSpawnAttempts = 5,
                NPCSpawnConfigs = new List<NPCSpawnConfig>
                {
                    new NPCSpawnConfig { PrefabName = "assets/rust.ai/agents/npcplayer/humannpc/tunneldweller/npc_tunneldweller.prefab", SpawnCount = 5 },
                    new NPCSpawnConfig { PrefabName = "assets/rust.ai/agents/wolf/wolf.prefab", SpawnCount = 5 },
                },
            };
        }

        [ChatCommand("dun")]
        private void CreateDungeonCommand(BasePlayer player, string command, string[] args)
        {
            RemoveExistingDungeon();
            if (FindSuitableDungeonSpawnPoint(player.transform.position, _config.MinSpawnDistance, _config.MaxSpawnDistance, out var position, out var rotation))
            {
                _currentDungeon = CreateDungeon(position, rotation);
                player.ChatMessage("Christmas Dungeon created!");
            }
            else
            {
                player.ChatMessage("Could not find a suitable location for the Christmas Dungeon.");
            }
        }

        private void StartAutoSpawnTimer() => _autoSpawnTimer = timer.Every(_config.AutoSpawnIntervalMinutes * 60f, AutoSpawnDungeon);

        private void AutoSpawnDungeon()
        {
            RemoveExistingDungeon();
            for (var attempt = 0; attempt < _config.MaxSpawnAttempts; attempt++)
            {
                foreach (var player in BasePlayer.activePlayerList)
                {
                    if (!FindSuitableDungeonSpawnPoint(player.transform.position, 20f, 50f, out var position, out var rotation))
                        continue;
                    _currentDungeon = CreateDungeon(position, rotation);
                    NotifyPlayersOfDungeonLocation(position);
                    return;
                }
            }
            Puts("Failed to auto-spawn dungeon after max attempts.");
        }

        private void NotifyPlayersOfDungeonLocation(Vector3 position)
        {
            foreach (var player in BasePlayer.activePlayerList)
            {
                player.ChatMessage($"A Christmas Dungeon has appeared at coordinates: ({position.x:F0}, {position.z:F0})!");
            }
        }

        private XmasDungeon CreateDungeon(Vector3 position, Quaternion rotation)
        {
            var entity = GameManager.server.CreateEntity(DungeonPrefabPath, position, rotation) as XmasDungeon;

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

            entity.transform.rotation = rotation;
            entity.Spawn();
            IntegrateNPCsInDungeon(entity);
            return entity;
        }

        private void IntegrateNPCsInDungeon(XmasDungeon dungeon)
        {
            Puts("Integrating NPCs in the Dungeon");
            ProceduralDynamicDungeon proceduralDungeon = dungeon.dungeonInstance.Get(serverside: true);
            int totalCells = proceduralDungeon.spawnedCells.Count;

            if (totalCells == 0)
            {
                Puts("No cells found in dungeon for NPC spawns.");
                return;
            }

            foreach (var config in _config.NPCSpawnConfigs)
            {
                int totalNPCsAssigned = 0;
                int baseNPCsPerCell = config.SpawnCount / totalCells;
                int remainingNPCs = config.SpawnCount % totalCells;

                foreach (ProceduralDungeonCell spawnedCell in proceduralDungeon.spawnedCells)
                {
                    if (totalNPCsAssigned >= config.SpawnCount)
                        break;

                    SpawnGroup spawnGroup = spawnedCell.gameObject.AddComponent<SpawnGroup>();

                    if (!GameManifest.pathToGuid.TryGetValue(config.PrefabName, out var guid))
                    {
                        Puts($"GUID not found for Prefab Name: {config.PrefabName}");
                        continue;
                    }

                    spawnGroup.prefabs = new List<SpawnGroup.SpawnEntry>
                    {
                        new SpawnGroup.SpawnEntry
                        {
                            prefab = new GameObjectRef { guid = guid },
                            weight = 1,
                            mobile = true,
                        },
                    };

                    int assignedNPCs = baseNPCsPerCell + (remainingNPCs > 0 ? 1 : 0);
                    if (remainingNPCs > 0)
                    {
                        remainingNPCs--;
                    }

                    assignedNPCs = Mathf.Min(assignedNPCs, config.SpawnCount - totalNPCsAssigned);
                    totalNPCsAssigned += assignedNPCs;

                    // Set the population constraints
                    spawnGroup.maxPopulation = assignedNPCs;
                    spawnGroup.numToSpawnPerTickMin = 0; // Avoid re-spawning per tick
                    spawnGroup.numToSpawnPerTickMax = 0; // Avoid re-spawning per tick
                    spawnGroup.fillOnSpawn = true; // Fill initially and avoid further spawns

                    Puts($"Configured spawn group in cell for {config.PrefabName} with {assignedNPCs} NPCs.");
                    spawnGroup.Fill();
                }

                Puts($"Finished configuring spawn groups. Total NPCs assigned: {totalNPCsAssigned}");
            }
        }

        private bool FindSuitableDungeonSpawnPoint(Vector3 center, float minRadius, float maxRadius, out Vector3 suitablePosition, out Quaternion suitableRotation)
        {
            for (var attempt = 0; attempt < _config.MaxSpawnAttempts; attempt++)
            {
                var position = TerrainUtilities.GetRandomPosition(center, minRadius, maxRadius);

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

        private bool IsPositionSuitable(Vector3 position)
        {
            if (TerrainUtilities.ContainsRock(position, 2f))
                return false;
            if (TerrainUtilities.IsInRadTown(position))
                return false;
            if (TerrainUtilities.ContainsEntitiesNearby(position, 4f, TerrainUtilities.EntityLayerMask))
                return false;
            if (TerrainUtilities.IsInWater(position))
                return false;
            if (TerrainUtilities.IsOnRoadOrRail(position))
                return false;
            if (TerrainUtilities.IsInNoBuildZone(position, 2f))
                return false;

            return true;
        }

        private object OnPortalUse(BasePlayer player, XmasDungeon dungeon)
        {
            Puts("XmasDungeon triggered.");
            return null;
        }

        private void RemoveExistingDungeon()
        {
            _currentDungeon?.Kill();
            _currentDungeon = null;
        }

        private void Unload()
        {
            RemoveExistingDungeon();
            _autoSpawnTimer?.Destroy();
        }

        private class PluginConfig
        {
            [JsonProperty("Version")]
            public string Version { get; set; }

            [JsonProperty("Auto Spawn Enabled")]
            public bool EnableAutoSpawn { get; set; }

            [JsonProperty("Auto Spawn Interval Minutes")]
            public float AutoSpawnIntervalMinutes { get; set; }

            [JsonProperty("Min Spawn Distance")]
            public float MinSpawnDistance { get; set; }

            [JsonProperty("Max Spawn Distance")]
            public float MaxSpawnDistance { get; set; }

            [JsonProperty("Max Spawn Attempts")]
            public int MaxSpawnAttempts { get; set; }

            [JsonProperty("NPC Spawn Configs")]
            public List<NPCSpawnConfig> NPCSpawnConfigs { get; set; }
        }

        private class NPCSpawnConfig
        {
            [JsonProperty("Prefab Name")]
            public string PrefabName { get; set; }

            [JsonProperty("Spawn Count")]
            public int SpawnCount { get; set; }
        }
    }

    public static class TerrainUtilities
    {
        public const int EntityLayerMask = Layers.Mask.Deployed | Layers.Mask.Construction;
        public const int GroundLayerMask = Layers.Mask.Terrain | Layers.Mask.World | Layers.Mask.Default;

        public static bool OnTopology(Vector3 position, TerrainTopology.Enum topology)
        {
            return (TerrainMeta.TopologyMap.GetTopology(position) & (int)topology) != 0;
        }

        public static bool IsOnRoadOrRail(Vector3 position)
        {
            var combinedTopology = TerrainTopology.Enum.Road | TerrainTopology.Enum.Roadside | TerrainTopology.Enum.Rail | TerrainTopology.Enum.Railside;
            return OnTopology(position, combinedTopology);
        }

        public static bool IsInNoBuildZone(Vector3 position, float radius)
        {
            return Physics.CheckSphere(position, radius, Layers.Mask.Prevent_Building);
        }

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

        public static bool ContainsRock(Vector3 position, float radius)
        {
            List<Collider> colliders = Pool.Get<List<Collider>>();
            Vis.Colliders(position, radius, colliders, GroundLayerMask, QueryTriggerInteraction.Ignore);
            return colliders.Exists(collider =>
                collider.name.Contains("rock", System.StringComparison.OrdinalIgnoreCase)
                || collider.name.Contains("cliff", System.StringComparison.OrdinalIgnoreCase)
                || collider.name.Contains("formation", System.StringComparison.OrdinalIgnoreCase)
            );
        }

        public static bool IsInRadTown(Vector3 position)
        {
            foreach (var monumentInfo in TerrainMeta.Path.Monuments)
            {
                if (monumentInfo.IsInBounds(position) && monumentInfo.shouldDisplayOnMap)
                    return true;
            }
            return OnTopology(position, TerrainTopology.Enum.Monument);
        }

        public static bool ContainsEntitiesNearby(Vector3 position, float radius, int mask)
        {
            List<Collider> hitColliders = Pool.Get<List<Collider>>();
            GamePhysics.OverlapSphere(position, radius, hitColliders, mask, QueryTriggerInteraction.Ignore);
            return hitColliders.Exists(collider => collider.gameObject.ToBaseEntity() != null);
        }

        public static bool GetTerrainInfo(Vector3 startPosition, float range, out RaycastHit hitInfo)
        {
            return Physics.Linecast(startPosition + new Vector3(0f, range, 0f), startPosition - new Vector3(0f, range, 0f), out hitInfo, GroundLayerMask);
        }

        public static Vector3 GetRandomPosition(Vector3 center, float minRadius, float maxRadius)
        {
            Vector3 randomDirection = Random.onUnitSphere;
            randomDirection.y = 0;
            float randomDistance = Random.Range(minRadius, maxRadius);
            Vector3 randomPosition = center + randomDirection * randomDistance;
            randomPosition.y = TerrainMeta.HeightMap.GetHeight(randomPosition);
            return randomPosition;
        }
    }
}


use this conventions:
C# Coding Standards and Naming Conventions
Object Name	Notation	Length	Plural	Prefix	Suffix	Abbreviation	Char Mask	Underscores
Namespace name	PascalCase	128	Yes	Yes	No	No	[A-z][0-9]	No
Class name	PascalCase	128	No	No	Yes	No	[A-z][0-9]	No
Constructor name	PascalCase	128	No	No	Yes	No	[A-z][0-9]	No
Method name	PascalCase	128	Yes	No	No	No	[A-z][0-9]	No
Method arguments	camelCase	128	Yes	No	No	Yes	[A-z][0-9]	No
Local variables	camelCase	50	Yes	No	No	Yes	[A-z][0-9]	No
Constants name	PascalCase	50	No	No	No	No	[A-z][0-9]	No
Field name Public	PascalCase	50	Yes	No	No	Yes	[A-z][0-9]	No
Field name Private	_camelCase	50	Yes	No	No	Yes	_[A-z][0-9]	Yes
Properties name	PascalCase	50	Yes	No	No	Yes	[A-z][0-9]	No
Delegate name	PascalCase	128	No	No	Yes	Yes	[A-z]	No
Enum type name	PascalCase	128	Yes	No	No	No	[A-z]	No
1. Do use PascalCasing for class names and method names:
public class ClientActivity
{
  public void ClearStatistics()
  {
    //...
  }
  public void CalculateStatistics()
  {
    //...
  }
}
Why: consistent with the Microsoft's .NET Framework and easy to read.

2. Do use camelCasing for method arguments and local variables:
public class UserLog
{
  public void Add(LogEvent logEvent)
  {
    int itemCount = logEvent.Items.Count;
    // ...
  }
}
Why: consistent with the Microsoft's .NET Framework and easy to read.

3. Do not use Hungarian notation or any other type identification in identifiers
// Correct
int counter;
string name;    
// Avoid
int iCounter;
string strName;
Why: consistent with the Microsoft's .NET Framework and Visual Studio IDE makes determining types very easy (via tooltips). In general, you want to avoid type indicators in any identifier.

4. Do not use Screaming Caps for constants or readonly variables:
// Correct
public const string ShippingType = "DropShip";
// Avoid
public const string SHIPPINGTYPE = "DropShip";
Why: consistent with the Microsoft's .NET Framework. Caps grab too much attention.

5. Use meaningful names for variables. The following example uses seattleCustomers for customers who are located in Seattle:
var seattleCustomers = from customer in customers
  where customer.City == "Seattle" 
  select customer.Name;
Why: consistent with the Microsoft's .NET Framework and easy to read.

6. Avoid using Abbreviations. Exceptions: abbreviations commonly used as names, such as Id, Xml, Ftp, Uri.
// Correct
UserGroup userGroup;
Assignment employeeAssignment;     
// Avoid
UserGroup usrGrp;
Assignment empAssignment; 
// Exceptions
CustomerId customerId;
XmlDocument xmlDocument;
FtpHelper ftpHelper;
UriPart uriPart;
Why: consistent with the Microsoft's .NET Framework and prevents inconsistent abbreviations.

7. Do use PascalCasing or camelCasing (Depending on the identifier type) for abbreviations 3 characters or more (2 chars are both uppercase when PascalCasing is appropriate or inside the identifier).:
HtmlHelper htmlHelper;
FtpTransfer ftpTransfer, fastFtpTransfer;
UIControl uiControl, nextUIControl;
Why: consistent with the Microsoft's .NET Framework. Caps would grab visually too much attention.

8. Do not use Underscores in identifiers. Exception: you can prefix private fields with an underscore:
// Correct
public DateTime clientAppointment;
public TimeSpan timeLeft;    
// Avoid
public DateTime client_Appointment;
public TimeSpan time_Left; 
// Exception (Class field)
private DateTime _registrationDate;
Why: consistent with the Microsoft's .NET Framework and makes code more natural to read (without 'slur'). Also avoids underline stress (inability to see underline).

9. Do use predefined type names (C# aliases) like int, float, string for local, parameter and member declarations. Do use .NET Framework names like Int32, Single, String when accessing the type's static members like Int32.TryParse or String.Join.
// Correct
string firstName;
int lastIndex;
bool isSaved;
string commaSeparatedNames = String.Join(", ", names);
int index = Int32.Parse(input);
// Avoid
String firstName;
Int32 lastIndex;
Boolean isSaved;
string commaSeparatedNames = string.Join(", ", names);
int index = int.Parse(input);
Why: consistent with the Microsoft's .NET Framework and makes code more natural to read.

10. Do use implicit type var for local variable declarations. Exception: primitive types (int, string, double, etc) use predefined names.
var stream = File.Create(path);
var customers = new Dictionary();
// Exceptions
int index = 100;
string timeSheet;
bool isCompleted;
Why: removes clutter, particularly with complex generic types. Type is easily detected with Visual Studio tooltips.

11. Do use noun or noun phrases to name a class.
public class Employee
{
}
public class BusinessLocation
{
}
public class DocumentCollection
{
}
Why: consistent with the Microsoft's .NET Framework and easy to remember.

12. Do prefix interfaces with the letter I. Interface names are noun (phrases) or adjectives.
public interface IShape
{
}
public interface IShapeCollection
{
}
public interface IGroupable
{
}
Why: consistent with the Microsoft's .NET Framework.

13. Do name source files according to their main classes. Exception: file names with partial classes reflect their source or purpose, e.g. designer, generated, etc.
// Located in Task.cs
public partial class Task
{
}
// Located in Task.generated.cs
public partial class Task
{
}
Why: consistent with the Microsoft practices. Files are alphabetically sorted and partial classes remain adjacent.

14. Do organize namespaces with a clearly defined structure:
// Examples
namespace Company.Technology.Feature.Subnamespace
{
}
namespace Company.Product.Module.SubModule
{
}
namespace Product.Module.Component
{
}
namespace Product.Layer.Module.Group
{
}
Why: consistent with the Microsoft's .NET Framework. Maintains good organization of your code base.

15. Do vertically align curly brackets:
// Correct
class Program
{
  static void Main(string[] args)
  {
    //...
  }
}
Why: Microsoft has a different standard, but developers have overwhelmingly preferred vertically aligned brackets.

16. Do declare all member variables at the top of a class, with static variables at the very top.
// Correct
public class Account
{
  public static string BankName;
  public static decimal Reserves;      
  public string Number { get; set; }
  public DateTime DateOpened { get; set; }
  public DateTime DateClosed { get; set; }
  public decimal Balance { get; set; }     
  // Constructor
  public Account()
  {
    // ...
  }
}
Why: generally accepted practice that prevents the need to hunt for variable declarations.

17. Do use singular names for enums. Exception: bit field enums.
// Correct
public enum Color
{
  Red,
  Green,
  Blue,
  Yellow,
  Magenta,
  Cyan
} 
// Exception
[Flags]
public enum Dockings
{
  None = 0,
  Top = 1,
  Right = 2, 
  Bottom = 4,
  Left = 8
}
Why: consistent with the Microsoft's .NET Framework and makes the code more natural to read. Plural flags because enum can hold multiple values (using bitwise 'OR').

18. Do not explicitly specify a type of an enum or values of enums (except bit fields):
// Don't
public enum Direction : long
{
  North = 1,
  East = 2,
  South = 3,
  West = 4
} 
// Correct
public enum Direction
{
  North,
  East,
  South,
  West
}
Why: can create confusion when relying on actual types and values.

19. Do not use an "Enum" suffix in enum type names:
// Don't
public enum CoinEnum
{
  Penny,
  Nickel,
  Dime,
  Quarter,
  Dollar
} 
// Correct
public enum Coin
{
  Penny,
  Nickel,
  Dime,
  Quarter,
  Dollar
}
Why: consistent with the Microsoft's .NET Framework and consistent with prior rule of no type indicators in identifiers.

20. Do not use "Flag" or "Flags" suffixes in enum type names:
// Don't
[Flags]
public enum DockingsFlags
{
  None = 0,
  Top = 1,
  Right = 2, 
  Bottom = 4,
  Left = 8
}
// Correct
[Flags]
public enum Dockings
{
  None = 0,
  Top = 1,
  Right = 2, 
  Bottom = 4,
  Left = 8
}
Why: consistent with the Microsoft's .NET Framework and consistent with prior rule of no type indicators in identifiers.

21. Do use suffix EventArgs at creation of the new classes comprising the information on event:
// Correct
public class BarcodeReadEventArgs : System.EventArgs
{
}
Why: consistent with the Microsoft's .NET Framework and easy to read.

22. Do name event handlers (delegates used as types of events) with the "EventHandler" suffix, as shown in the following example:
public delegate void ReadBarcodeEventHandler(object sender, ReadBarcodeEventArgs e);
Why: consistent with the Microsoft's .NET Framework and easy to read.

23. Do not create names of parameters in methods (or constructors) which differ only by the register:
// Avoid
private void MyFunction(string name, string Name)
{
  //...
}
Why: consistent with the Microsoft's .NET Framework and easy to read, and also excludes possibility of occurrence of conflict situations.

24. Do use two parameters named sender and e in event handlers. The sender parameter represents the object that raised the event. The sender parameter is typically of type object, even if it is possible to employ a more specific type.
public void ReadBarcodeEventHandler(object sender, ReadBarcodeEventArgs e)
{
  //...
}
Why: consistent with the Microsoft's .NET Framework and consistent with prior rule of no type indicators in identifiers.

25. Do use suffix Exception at creation of the new classes comprising the information on exception:
// Correct
public class BarcodeReadException : System.Exception
{
}
Why: consistent with the Microsoft's .NET Framework and easy to read.

26. Do use prefix Any, Is, Have or similar keywords for boolean identifier:
// Correct
public static bool IsNullOrEmpty(string value) {
    return (value == null || value.Length == 0);
}
Why: consistent with the Microsoft's .NET Framework and easy to read.

27. Use Named Arguments in method calls:
When calling a method, arguments are passed with the parameter name followed by a colon and a value.

// Method
public void DoSomething(string foo, int bar) 
{
...
}

// Avoid
DoSomething("someString", 1);
// Correct
DoSomething(foo: "someString", bar: 1);