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

FREE CS2Plugin : 2.0.0 2.0.0

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

namespace Oxide.Plugins
{
    [Info("CS2 Style Plugin", "ModernCS2", "2.0.0")]
    [Description("Modern CS2-style competitive gameplay plugin for Rust")]
    public class CS2Plugin : RustPlugin
    {
        #region Fields and Configuration
        
        private Configuration config;
        private Dictionary<ulong, PlayerData> playerData = new Dictionary<ulong, PlayerData>();
        private Dictionary<string, QueueZone> queueZones = new Dictionary<string, QueueZone>();
        private Dictionary<string, BombSite> bombSites = new Dictionary<string, BombSite>();
        private Dictionary<string, MapConfiguration> mapConfigurations = new Dictionary<string, MapConfiguration>();
        private Dictionary<ulong, Match> activeMatches = new Dictionary<ulong, Match>();
        private Dictionary<ulong, ClipboardData> adminClipboard = new Dictionary<ulong, ClipboardData>();
        
        private const string PERMISSION_ADMIN = "cs2plugin.admin";
        private const string PERMISSION_USE = "cs2plugin.use";
        
        private Timer uiUpdateTimer;
        private Timer matchUpdateTimer;
        
        #endregion
        
        #region Configuration Classes
        
        public class Configuration
        {
            [JsonProperty("Default Team Size")]
            public int DefaultTeamSize { get; set; } = 5;
            
            [JsonProperty("Round Win Requirement")]
            public int RoundWinRequirement { get; set; } = 13;
            
            [JsonProperty("Side Swap Round")]
            public int SideSwapRound { get; set; } = 12;
            
            [JsonProperty("Buy Phase Duration")]
            public float BuyPhaseDuration { get; set; } = 20f;
            
            [JsonProperty("Buy Menu Duration")]
            public float BuyMenuDuration { get; set; } = 40f;
            
            [JsonProperty("Bomb Timer")]
            public float BombTimer { get; set; } = 40f;
            
            [JsonProperty("Max Team Damage")]
            public float MaxTeamDamage { get; set; } = 300f;
            
            [JsonProperty("Max Grenade Damage")]
            public float MaxGrenadeDamage { get; set; } = 60f;
            
            [JsonProperty("Lobby Spawn Point")]
            public Vector3 LobbySpawnPoint { get; set; } = new Vector3(0, 0, 0);
            
            [JsonProperty("Auto Generate Default Maps")]
            public bool AutoGenerateDefaultMaps { get; set; } = true;
            
            [JsonProperty("Default Map Template")]
            public string DefaultMapTemplate { get; set; } = "custom";
            
            [JsonProperty("Map Generation On Server Start")]
            public bool GenerateMapOnStart { get; set; } = true;
            
            [JsonProperty("Server Map Name")]
            public string ServerMapName { get; set; } = "cs2_server_map";
            
            [JsonProperty("Map Generation Center")]
            public Vector3 MapGenerationCenter { get; set; } = new Vector3(0, 0, 0);
            
            [JsonProperty("Auto Detect Map Center")]
            public bool AutoDetectMapCenter { get; set; } = true;
            
            [JsonProperty("Large Map Mode")]
            public bool LargeMapMode { get; set; } = true;
            
            [JsonProperty("Map Search Radius")]
            public float MapSearchRadius { get; set; } = 1450f;
            
            [JsonProperty("Multiple Map Zones")]
            public bool MultipleMapZones { get; set; } = true;
        }
        
        #endregion
        
        #region Data Classes
        
        public class PlayerData
        {
            public ulong PlayerId { get; set; }
            public string PlayerName { get; set; }
            public Team CurrentTeam { get; set; } = Team.None;
            public ulong CurrentMatchId { get; set; } = 0;
            public bool IsInQueue { get; set; } = false;
            public string QueueZoneName { get; set; } = "";
            public float TeamDamageDealt { get; set; } = 0f;
            public int Kills { get; set; } = 0;
            public int Deaths { get; set; } = 0;
            public int Assists { get; set; } = 0;
            public bool IsSpectating { get; set; } = false;
            public bool HasBuyMenuOpen { get; set; } = false;
            public Vector3 LastPosition { get; set; }
            public bool IsDefusing { get; set; } = false;
            public Timer DefuseTimer { get; set; }
            public bool IsInFreeCam { get; set; } = false;
            public ulong SpectatingPlayerId { get; set; } = 0;
            public int SpectatorIndex { get; set; } = 0;
            public bool IsTalking { get; set; } = false;
            public float LastTalkTime { get; set; } = 0f;
            public int RoundKills { get; set; } = 0;
            public int RoundDeaths { get; set; } = 0;
        }
        
        public class QueueZone
        {
            public string Name { get; set; }
            public Vector3 MinBounds { get; set; }
            public Vector3 MaxBounds { get; set; }
            public List<ulong> QueuedPlayers { get; set; } = new List<ulong>();
            public int TeamSize { get; set; } = 5;
            public bool IsActive { get; set; } = true;
            public string SelectedMap { get; set; } = "dust2";
        }
        
        public class BombSite
        {
            public string Name { get; set; }
            public Vector3 MinBounds { get; set; }
            public Vector3 MaxBounds { get; set; }
            public string MapName { get; set; }
            public bool IsActive { get; set; } = true;
        }
        
        public class MapConfiguration
        {
            public string MapName { get; set; }
            public List<Vector3> TerroristSpawns { get; set; } = new List<Vector3>();
            public List<Vector3> CounterTerroristSpawns { get; set; } = new List<Vector3>();
            public List<string> BombSiteNames { get; set; } = new List<string>();
            public Vector3 SpectatorArea { get; set; }
        }
        
        public class Match
        {
            public ulong MatchId { get; set; }
            public List<ulong> TerroristPlayers { get; set; } = new List<ulong>();
            public List<ulong> CounterTerroristPlayers { get; set; } = new List<ulong>();
            public int TerroristScore { get; set; } = 0;
            public int CounterTerroristScore { get; set; } = 0;
            public int CurrentRound { get; set; } = 1;
            public MatchState State { get; set; } = MatchState.WaitingForPlayers;
            public string MapName { get; set; } = "dust2";
            public float RoundStartTime { get; set; }
            public bool IsBuyPhase { get; set; } = false;
            public PlantedBomb PlantedBomb { get; set; }
            public bool HasSwappedSides { get; set; } = false;
            public Timer RoundTimer { get; set; }
            public Timer BuyPhaseTimer { get; set; }
            public string QueueZoneName { get; set; }
        }
        
        public class PlantedBomb
        {
            public Vector3 Position { get; set; }
            public float PlantTime { get; set; }
            public ulong PlanterId { get; set; }
            public string BombSiteName { get; set; }
            public Timer ExplosionTimer { get; set; }
            public bool IsBeingDefused { get; set; } = false;
            public ulong DefuserId { get; set; } = 0;
        }
        
        public class ClipboardData
        {
            public Vector3? PointA { get; set; }
            public Vector3? PointB { get; set; }
            public string PendingCommand { get; set; } = "";
        }
        
        public enum Team
        {
            None,
            Terrorist,
            CounterTerrorist
        }
        
        public enum MatchState
        {
            WaitingForPlayers,
            InProgress,
            Finished
        }
        
        public enum RoundEndReason
        {
            TerroristsEliminated,
            CounterTerroristsEliminated,
            BombExploded,
            BombDefused,
            TimeExpired
        }
        
        #endregion
        
        #region Plugin Initialization
        
        protected override void LoadDefaultConfig()
        {
            config = new Configuration();
            SaveConfig();
        }
        
        private void LoadConfig()
        {
            try
            {
                config = Config.ReadObject<Configuration>();
                if (config == null) LoadDefaultConfig();
            }
            catch
            {
                LoadDefaultConfig();
            }
        }
        
        private void SaveConfig() => Config.WriteObject(config);
        
        void Init()
        {
            LoadConfig();
            
            permission.RegisterPermission(PERMISSION_ADMIN, this);
            permission.RegisterPermission(PERMISSION_USE, this);
            
            // Register commands
            cmd.AddChatCommand("setqzone", this, "CmdSetQueueZone");
            cmd.AddChatCommand("removeqzone", this, "CmdRemoveQueueZone");
            cmd.AddChatCommand("setbombsite", this, "CmdSetBombSite");
            cmd.AddChatCommand("removebombsite", this, "CmdRemoveBombSite");
            cmd.AddChatCommand("setspawn", this, "CmdSetSpawn");
            cmd.AddChatCommand("removespawn", this, "CmdRemoveSpawn");
            cmd.AddChatCommand("buymenu", this, "CmdBuyMenu");
            cmd.AddChatCommand("joinqueue", this, "CmdJoinQueue");
            cmd.AddChatCommand("leavequeue", this, "CmdLeaveQueue");
            cmd.AddChatCommand("forcestart", this, "CmdForceStartMatch");
            cmd.AddChatCommand("endmatch", this, "CmdEndMatch");
            cmd.AddChatCommand("cs2stats", this, "CmdShowStats");
            cmd.AddChatCommand("spec", this, "CmdSpectate");
            cmd.AddChatCommand("specnext", this, "CmdSpectateNext");
            cmd.AddChatCommand("specprev", this, "CmdSpectatePrevious");
            cmd.AddChatCommand("freecam", this, "CmdToggleFreeCam");
            cmd.AddChatCommand("generatemap", this, "CmdGenerateMap");
            cmd.AddChatCommand("clearmap", this, "CmdClearMap");
            cmd.AddChatCommand("listmaps", this, "CmdListMaps");
            cmd.AddChatCommand("teleportmap", this, "CmdTeleportToMap");
            
            // Initialize timers
            uiUpdateTimer = timer.Every(1f, UpdateAllUI);
            matchUpdateTimer = timer.Every(0.1f, UpdateMatches);
        }
        
        void OnServerInitialized()
        {
            LoadData();
            
            // Initialize default map configurations
            InitializeDefaultMaps();
            
            // Auto-generate maps if enabled
            if (config.GenerateMapOnStart)
            {
                timer.Once(10f, () => AutoGenerateServerMaps());
            }
            
            // Auto-detect map center if enabled
            if (config.AutoDetectMapCenter)
            {
                DetectMapCenter();
            }
            
            PrintWarning("CS2 Style Plugin loaded successfully!");
            PrintWarning($"Map generation center: {config.MapGenerationCenter}");
            PrintWarning($"Available maps: {mapConfigurations.Count}");
            PrintWarning($"Large map mode: {config.LargeMapMode}");
        }
        
        void Unload()
        {
            SaveData();
            
            // Clean up timers
            uiUpdateTimer?.Destroy();
            matchUpdateTimer?.Destroy();
            
            // Clean up all active matches
            foreach (var match in activeMatches.Values)
            {
                EndMatch(match, "Plugin unloaded");
            }
            
            // Destroy all UI elements
            foreach (var player in BasePlayer.activePlayerList)
            {
                DestroyUI(player);
            }
        }
        
        #endregion
        
        #region Data Management
        
        private void LoadData()
        {
            try
            {
                playerData = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<ulong, PlayerData>>("CS2Plugin_PlayerData") ?? new Dictionary<ulong, PlayerData>();
                queueZones = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<string, QueueZone>>("CS2Plugin_QueueZones") ?? new Dictionary<string, QueueZone>();
                bombSites = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<string, BombSite>>("CS2Plugin_BombSites") ?? new Dictionary<string, BombSite>();
                mapConfigurations = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<string, MapConfiguration>>("CS2Plugin_MapConfigurations") ?? new Dictionary<string, MapConfiguration>();
            }
            catch (Exception ex)
            {
                PrintError($"Error loading data: {ex.Message}");
            }
        }
        
        private void SaveData()
        {
            try
            {
                Interface.Oxide.DataFileSystem.WriteObject("CS2Plugin_PlayerData", playerData);
                Interface.Oxide.DataFileSystem.WriteObject("CS2Plugin_QueueZones", queueZones);
                Interface.Oxide.DataFileSystem.WriteObject("CS2Plugin_BombSites", bombSites);
                Interface.Oxide.DataFileSystem.WriteObject("CS2Plugin_MapConfigurations", mapConfigurations);
            }
            catch (Exception ex)
            {
                PrintError($"Error saving data: {ex.Message}");
            }
        }
        
        private PlayerData GetPlayerData(ulong playerId)
        {
            if (!playerData.ContainsKey(playerId))
            {
                var player = BasePlayer.FindByID(playerId);
                playerData[playerId] = new PlayerData
                {
                    PlayerId = playerId,
                    PlayerName = player?.displayName ?? "Unknown"
                };
            }
            return playerData[playerId];
        }
        
        private void InitializeDefaultMaps()
        {
            // Initialize Dust2 configuration
            if (!mapConfigurations.ContainsKey("dust2"))
            {
                mapConfigurations["dust2"] = new MapConfiguration
                {
                    MapName = "dust2",
                    TerroristSpawns = new List<Vector3>
                    {
                        new Vector3(100, 0, 100),
                        new Vector3(105, 0, 100),
                        new Vector3(110, 0, 100),
                        new Vector3(115, 0, 100),
                        new Vector3(120, 0, 100)
                    },
                    CounterTerroristSpawns = new List<Vector3>
                    {
                        new Vector3(-100, 0, -100),
                        new Vector3(-105, 0, -100),
                        new Vector3(-110, 0, -100),
                        new Vector3(-115, 0, -100),
                        new Vector3(-120, 0, -100)
                    },
                    SpectatorArea = new Vector3(0, 50, 0)
                };
            }
            
            // Initialize Train configuration
            if (!mapConfigurations.ContainsKey("train"))
            {
                mapConfigurations["train"] = new MapConfiguration
                {
                    MapName = "train",
                    TerroristSpawns = new List<Vector3>
                    {
                        new Vector3(200, 0, 200),
                        new Vector3(205, 0, 200),
                        new Vector3(210, 0, 200),
                        new Vector3(215, 0, 200),
                        new Vector3(220, 0, 200)
                    },
                    CounterTerroristSpawns = new List<Vector3>
                    {
                        new Vector3(-200, 0, -200),
                        new Vector3(-205, 0, -200),
                        new Vector3(-210, 0, -200),
                        new Vector3(-215, 0, -200),
                        new Vector3(-220, 0, -200)
                    },
                    SpectatorArea = new Vector3(0, 75, 0)
                };
            }
        }
        
        #endregion
        
        #region Modern Map Detection System
        
        private void AutoGenerateServerMaps()
        {
            PrintWarning("Starting automatic map generation for large Rust map...");
            
            // Check if we already have maps configured
            if (mapConfigurations.Count > 2)
            {
                PrintWarning("Maps already exist, skipping auto-generation.");
                return;
            }
            
            // Detect map size and adjust accordingly
            float mapSize = GetMapSize();
            PrintWarning($"Detected map size: {mapSize}");
            
            if (mapSize >= 2000f) // Large map
            {
                GenerateMultipleCompetitiveZones();
            }
            else
            {
                GenerateSingleCompetitiveZone();
            }
            
            PrintWarning("Automatic map generation completed!");
        }
        
        private float GetMapSize()
        {
            try
            {
                if (TerrainMeta.Size != null)
                {
                    return TerrainMeta.Size.x;
                }
            }
            catch
            {
                // Fallback for older versions
            }
            
            // Default assumption for large maps
            return 2900f;
        }
        
        private void GenerateMultipleCompetitiveZones()
        {
            PrintWarning("Generating multiple competitive zones for large map...");
            
            float mapSize = GetMapSize();
            float radius = mapSize * 0.3f; // 30% of map size
            
            // Generate 4 competitive zones in different quadrants
            var zones = new List<Vector3>
            {
                new Vector3(radius * 0.5f, 0, radius * 0.5f),      // NE
                new Vector3(-radius * 0.5f, 0, radius * 0.5f),     // NW
                new Vector3(radius * 0.5f, 0, -radius * 0.5f),     // SE
                new Vector3(-radius * 0.5f, 0, -radius * 0.5f)     // SW
            };
            
            for (int i = 0; i < zones.Count; i++)
            {
                Vector3 center = FindSafeLocation(zones[i]);
                GenerateCompetitiveZone($"zone_{i + 1}", center, i);
            }
        }
        
        private void GenerateSingleCompetitiveZone()
        {
            PrintWarning("Generating single competitive zone...");
            
            Vector3 center = config.MapGenerationCenter;
            if (center == Vector3.zero)
            {
                center = FindSafeLocation(Vector3.zero);
            }
            
            GenerateCompetitiveZone("main_zone", center, 0);
        }
        
        private Vector3 FindSafeLocation(Vector3 preferredLocation)
        {
            Vector3 bestLocation = preferredLocation;
            
            try
            {
                // Try to find a location that's not in water and has reasonable terrain
                for (int attempts = 0; attempts < 10; attempts++)
                {
                    Vector3 testLocation = preferredLocation + new Vector3(
                        UnityEngine.Random.Range(-200f, 200f),
                        0,
                        UnityEngine.Random.Range(-200f, 200f)
                    );
                    
                    // Get terrain height
                    float height = GetTerrainHeight(testLocation);
                    testLocation.y = height;
                    
                    // Check if location is suitable
                    if (IsLocationSuitable(testLocation))
                    {
                        bestLocation = testLocation;
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                PrintWarning($"Error finding safe location: {ex.Message}");
            }
            
            return bestLocation;
        }
        
        private float GetTerrainHeight(Vector3 position)
        {
            try
            {
                if (TerrainMeta.HeightMap != null)
                {
                    return TerrainMeta.HeightMap.GetHeight(position);
                }
            }
            catch
            {
                // Fallback for older versions
            }
            
            // Use raycast as fallback
            RaycastHit hit;
            if (Physics.Raycast(position + Vector3.up * 200f, Vector3.down, out hit, 400f, LayerMask.GetMask("Terrain")))
            {
                return hit.point.y;
            }
            
            return 0f;
        }
        
        private bool IsLocationSuitable(Vector3 location)
        {
            try
            {
                // Check if in water
                if (WaterLevel.Test(location))
                {
                    return false;
                }
                
                // Check for existing entities
                var entities = new List<BaseEntity>();
                Vis.Entities(location, 50f, entities);
                
                // Too many existing structures
                if (entities.Count > 20)
                {
                    return false;
                }
                
                return true;
            }
            catch
            {
                return true; // Assume suitable if checks fail
            }
        }
        
        private void GenerateCompetitiveZone(string zoneName, Vector3 center, int zoneIndex)
        {
            PrintWarning($"Generating competitive zone: {zoneName} at {center}");
            
            // Create map configuration
            var mapConfig = new MapConfiguration
            {
                MapName = zoneName,
                SpectatorArea = center + Vector3.up * 20f
            };
            
            // Generate spawn points
            GenerateSpawnPoints(mapConfig, center);
            
            // Generate bomb sites
            GenerateBombSites(mapConfig, center);
            
            // Generate structures
            GenerateMapStructures(center, zoneName);
            
            // Save configuration
            mapConfigurations[zoneName] = mapConfig;
            
            // Create queue zone
            CreateQueueZone(zoneName, center);
            
            PrintWarning($"Zone {zoneName} generated successfully!");
        }
        
        private void GenerateSpawnPoints(MapConfiguration mapConfig, Vector3 center)
        {
            // Terrorist spawns (one side)
            for (int i = 0; i < 8; i++)
            {
                Vector3 spawnPos = center + new Vector3(-60f + i * 3f, 0, -80f);
                spawnPos.y = GetTerrainHeight(spawnPos) + 1f;
                mapConfig.TerroristSpawns.Add(spawnPos);
            }
            
            // Counter-Terrorist spawns (opposite side)
            for (int i = 0; i < 8; i++)
            {
                Vector3 spawnPos = center + new Vector3(-60f + i * 3f, 0, 80f);
                spawnPos.y = GetTerrainHeight(spawnPos) + 1f;
                mapConfig.CounterTerroristSpawns.Add(spawnPos);
            }
        }
        
        private void GenerateBombSites(MapConfiguration mapConfig, Vector3 center)
        {
            // Bomb site A
            Vector3 siteA = center + new Vector3(-40f, 0, 40f);
            siteA.y = GetTerrainHeight(siteA);
            
            string siteAName = $"{mapConfig.MapName}_A";
            bombSites[siteAName] = new BombSite
            {
                Name = siteAName,
                MapName = mapConfig.MapName,
                MinBounds = siteA - Vector3.one * 8f,
                MaxBounds = siteA + Vector3.one * 8f,
                IsActive = true
            };
            mapConfig.BombSiteNames.Add(siteAName);
            
            // Bomb site B
            Vector3 siteB = center + new Vector3(40f, 0, -40f);
            siteB.y = GetTerrainHeight(siteB);
            
            string siteBName = $"{mapConfig.MapName}_B";
            bombSites[siteBName] = new BombSite
            {
                Name = siteBName,
                MapName = mapConfig.MapName,
                MinBounds = siteB - Vector3.one * 8f,
                MaxBounds = siteB + Vector3.one * 8f,
                IsActive = true
            };
            mapConfig.BombSiteNames.Add(siteBName);
        }
        
        private void GenerateMapStructures(Vector3 center, string mapName)
        {
            try
            {
                // Generate basic structures using modern Rust entities
                CreateWalls(center);
                CreateCover(center);
                CreateLighting(center);
            }
            catch (Exception ex)
            {
                PrintWarning($"Error generating structures for {mapName}: {ex.Message}");
            }
        }
        
        private void CreateWalls(Vector3 center)
        {
            // Create walls using modern building blocks
            var wallPositions = new List<Vector3>
            {
                center + new Vector3(-50f, 0, 0),
                center + new Vector3(50f, 0, 0),
                center + new Vector3(0, 0, -50f),
                center + new Vector3(0, 0, 50f)
            };
            
            foreach (var pos in wallPositions)
            {
                Vector3 groundPos = pos;
                groundPos.y = GetTerrainHeight(pos);
                
                // Create wall entity
                var wall = GameManager.server.CreateEntity("assets/prefabs/building core/wall/wall.prefab", groundPos);
                if (wall != null)
                {
                    wall.Spawn();
                }
            }
        }
        
        private void CreateCover(Vector3 center)
        {
            // Create cover objects
            var coverPositions = new List<Vector3>
            {
                center + new Vector3(-20f, 0, 20f),
                center + new Vector3(20f, 0, -20f),
                center + new Vector3(-20f, 0, -20f),
                center + new Vector3(20f, 0, 20f)
            };
            
            foreach (var pos in coverPositions)
            {
                Vector3 groundPos = pos;
                groundPos.y = GetTerrainHeight(pos);
                
                // Create cover entity
                var cover = GameManager.server.CreateEntity("assets/prefabs/deployable/large wood storage/box.large.wood.prefab", groundPos);
                if (cover != null)
                {
                    cover.Spawn();
                }
            }
        }
        
        private void CreateLighting(Vector3 center)
        {
            // Create lighting for the area
            var lightPositions = new List<Vector3>
            {
                center + new Vector3(0, 6f, 0),
                center + new Vector3(-30f, 6f, 30f),
                center + new Vector3(30f, 6f, -30f)
            };
            
            foreach (var pos in lightPositions)
            {
                Vector3 lightPos = pos;
                lightPos.y = GetTerrainHeight(new Vector3(pos.x, 0, pos.z)) + 6f;
                
                // Create light entity
                var light = GameManager.server.CreateEntity("assets/prefabs/deployable/lantern/lantern.deployed.prefab", lightPos);
                if (light != null)
                {
                    light.Spawn();
                }
            }
        }
        
        private void CreateQueueZone(string mapName, Vector3 center)
        {
            Vector3 queueCenter = center + new Vector3(0, 0, -120f);
            queueCenter.y = GetTerrainHeight(queueCenter);
            
            queueZones[mapName + "_queue"] = new QueueZone
            {
                Name = mapName + "_queue",
                MinBounds = queueCenter - new Vector3(10f, 5f, 10f),
                MaxBounds = queueCenter + new Vector3(10f, 5f, 10f),
                TeamSize = config.DefaultTeamSize,
                IsActive = true,
                SelectedMap = mapName
            };
            
            PrintWarning($"Queue zone created for {mapName} at {queueCenter}");
        }
        
        private void DetectMapCenter()
        {
            if (config.MapGenerationCenter != Vector3.zero) return;
            
            try
            {
                // Try to get map bounds
                if (TerrainMeta.Bounds.size != Vector3.zero)
                {
                    config.MapGenerationCenter = TerrainMeta.Bounds.center;
                }
                else
                {
                    config.MapGenerationCenter = Vector3.zero;
                }
            }
            catch
            {
                config.MapGenerationCenter = Vector3.zero;
            }
            
            SaveConfig();
            PrintWarning($"Map center detected: {config.MapGenerationCenter}");
        }
        
        #endregion
        
        #region Player Events (Modern)
        
        void OnPlayerConnected(BasePlayer player)
        {
            if (player == null) return;
            
            var data = GetPlayerData(player.userID);
            data.PlayerName = player.displayName;
            
            // If player was in a match, handle reconnection
            if (data.CurrentMatchId != 0 && activeMatches.ContainsKey(data.CurrentMatchId))
            {
                var match = activeMatches[data.CurrentMatchId];
                if (match.State == MatchState.InProgress)
                {
                    timer.Once(2f, () => HandlePlayerReconnection(player, match));
                }
            }
            
            timer.Once(1f, () => CreateUI(player));
        }
        
        void OnPlayerDisconnected(BasePlayer player, string reason)
        {
            if (player == null) return;
            
            var data = GetPlayerData(player.userID);
            
            // Remove from queue if in one
            if (data.IsInQueue)
            {
                RemovePlayerFromQueue(player.userID);
            }
            
            // Handle match disconnection
            if (data.CurrentMatchId != 0 && activeMatches.ContainsKey(data.CurrentMatchId))
            {
                var match = activeMatches[data.CurrentMatchId];
                PrintToChat($"{player.displayName} disconnected from match {match.MatchId}");
            }
            
            DestroyUI(player);
        }
        
        void OnPlayerDeath(BasePlayer victim, HitInfo info)
        {
            if (victim == null) return;
            
            var victimData = GetPlayerData(victim.userID);
            if (victimData.CurrentMatchId == 0) return;
            
            if (!activeMatches.ContainsKey(victimData.CurrentMatchId)) return;
            var match = activeMatches[victimData.CurrentMatchId];
            
            if (match.State != MatchState.InProgress) return;
            
            victimData.Deaths++;
            victimData.RoundDeaths++;
            
            BasePlayer attacker = info?.InitiatorPlayer;
            if (attacker != null && attacker != victim)
            {
                var attackerData = GetPlayerData(attacker.userID);
                
                // Check for team kill
                if (victimData.CurrentTeam == attackerData.CurrentTeam)
                {
                    HandleTeamKill(attacker, victim, info);
                }
                else
                {
                    attackerData.Kills++;
                    attackerData.RoundKills++;
                    PrintToChat($"{attacker.displayName} eliminated {victim.displayName}");
                }
            }
            
            // Move player to spectator area
            timer.Once(2f, () => MoveToSpectatorArea(victim, match));
            
            // Check if round should end
            CheckRoundEnd(match);
        }
        
        void OnPlayerRespawned(BasePlayer player)
        {
            if (player == null) return;
            
            var data = GetPlayerData(player.userID);
            if (data.CurrentMatchId != 0 && activeMatches.ContainsKey(data.CurrentMatchId))
            {
                var match = activeMatches[data.CurrentMatchId];
                if (match.State == MatchState.InProgress && !data.IsSpectating)
                {
                    timer.Once(0.5f, () => RestorePlayerForMatch(player, match));
                }
            }
        }
        
        #endregion
        
        #region Queue System (Modern)
        
        void OnPlayerInput(BasePlayer player, InputState input)
        {
            if (player == null || input == null) return;
            
            var data = GetPlayerData(player.userID);
            
            // Check if player is in a queue zone
            CheckPlayerInQueueZone(player);
            
            // Handle spectator controls
            if (data.IsSpectating && !data.IsInFreeCam)
            {
                if (input.WasJustPressed(BUTTON.FIRE_PRIMARY))
                {
                    SpectateNextPlayer(player);
                }
                else if (input.WasJustPressed(BUTTON.FIRE_SECONDARY))
                {
                    SpectatePreviousPlayer(player);
                }
                else if (input.WasJustPressed(BUTTON.USE))
                {
                    ToggleFreeCam(player);
                }
            }
            
            // Handle defuse mechanics
            if (data.CurrentMatchId != 0 && activeMatches.ContainsKey(data.CurrentMatchId))
            {
                var match = activeMatches[data.CurrentMatchId];
                
                if (match.State == MatchState.InProgress && match.PlantedBomb != null)
                {
                    if (input.IsDown(BUTTON.USE) && data.CurrentTeam == Team.CounterTerrorist && !data.IsSpectating)
                    {
                        float distance = Vector3.Distance(player.transform.position, match.PlantedBomb.Position);
                        if (distance <= 2f)
                        {
                            StartDefuse(player, match);
                        }
                    }
                    else if (!input.IsDown(BUTTON.USE) && data.IsDefusing)
                    {
                        StopDefuse(player, match);
                    }
                }
            }
        }
        
        private void CheckPlayerInQueueZone(BasePlayer player)
        {
            var data = GetPlayerData(player.userID);
            var playerPos = player.transform.position;
            
            foreach (var zone in queueZones.Values)
            {
                if (!zone.IsActive) continue;
                
                bool inZone = IsPlayerInZone(playerPos, zone.MinBounds, zone.MaxBounds);
                
                if (inZone && !data.IsInQueue)
                {
                    AddPlayerToQueue(player, zone);
                }
                else if (!inZone && data.IsInQueue && data.QueueZoneName == zone.Name)
                {
                    RemovePlayerFromQueue(player.userID);
                }
            }
        }
        
        private bool IsPlayerInZone(Vector3 position, Vector3 minBounds, Vector3 maxBounds)
        {
            return position.x >= minBounds.x && position.x <= maxBounds.x &&
                   position.y >= minBounds.y && position.y <= maxBounds.y &&
                   position.z >= minBounds.z && position.z <= maxBounds.z;
        }
        
        private void AddPlayerToQueue(BasePlayer player, QueueZone zone)
        {
            var data = GetPlayerData(player.userID);
            
            if (data.CurrentMatchId != 0)
            {
                SendReply(player, "You are already in a match!");
                return;
            }
            
            data.IsInQueue = true;
            data.QueueZoneName = zone.Name;
            zone.QueuedPlayers.Add(player.userID);
            
            SendReply(player, $"Added to queue ({zone.QueuedPlayers.Count}/{zone.TeamSize * 2})");
            
            if (zone.QueuedPlayers.Count >= zone.TeamSize * 2)
            {
                StartMatch(zone);
            }
        }
        
        private void RemovePlayerFromQueue(ulong playerId)
        {
            var data = GetPlayerData(playerId);
            if (!data.IsInQueue) return;
            
            if (queueZones.ContainsKey(data.QueueZoneName))
            {
                var zone = queueZones[data.QueueZoneName];
                zone.QueuedPlayers.Remove(playerId);
            }
            
            data.IsInQueue = false;
            data.QueueZoneName = "";
            
            var player = BasePlayer.FindByID(playerId);
            if (player != null)
            {
                SendReply(player, "Removed from queue");
            }
        }
        
        private void StartMatch(QueueZone zone)
        {
            var queuedPlayers = zone.QueuedPlayers.ToList();
            zone.QueuedPlayers.Clear();
            
            var matchId = (ulong)UnityEngine.Random.Range(100000, 999999);
            var match = new Match
            {
                MatchId = matchId,
                MapName = zone.SelectedMap,
                QueueZoneName = zone.Name,
                State = MatchState.InProgress
            };
            
            // Assign teams randomly
            var shuffledPlayers = queuedPlayers.OrderBy(x => UnityEngine.Random.value).ToList();
            for (int i = 0; i < shuffledPlayers.Count; i++)
            {
                var playerId = shuffledPlayers[i];
                var data = GetPlayerData(playerId);
                data.CurrentMatchId = matchId;
                data.IsInQueue = false;
                data.QueueZoneName = "";
                
                if (i < zone.TeamSize)
                {
                    match.TerroristPlayers.Add(playerId);
                    data.CurrentTeam = Team.Terrorist;
                }
                else
                {
                    match.CounterTerroristPlayers.Add(playerId);
                    data.CurrentTeam = Team.CounterTerrorist;
                }
            }
            
            activeMatches[matchId] = match;
            
            NotifyMatchStart(match);
            StartRound(match);
        }
        
        #endregion
        
        #region Match Management (Modern)
        
        private void NotifyMatchStart(Match match)
        {
            var allPlayers = match.TerroristPlayers.Concat(match.CounterTerroristPlayers);
            
            foreach (var playerId in allPlayers)
            {
                var player = BasePlayer.FindByID(playerId);
                if (player != null)
                {
                    SendReply(player, $"Match {match.MatchId} started! Map: {match.MapName}");
                    SendReply(player, $"First to {config.RoundWinRequirement} rounds wins!");
                }
            }
        }
        
        private void StartRound(Match match)
        {
            match.CurrentRound++;
            match.RoundStartTime = Time.time;
            match.IsBuyPhase = true;
            match.PlantedBomb = null;
            
            // Reset round stats for all players
            var allPlayers = match.TerroristPlayers.Concat(match.CounterTerroristPlayers);
            foreach (var playerId in allPlayers)
            {
                var data = GetPlayerData(playerId);
                data.RoundKills = 0;
                data.RoundDeaths = 0;
            }
            
            // Check for side swap
            if (match.CurrentRound == config.SideSwapRound + 1 && !match.HasSwappedSides)
            {
                SwapTeams(match);
            }
            
            // Teleport players to spawns
            TeleportPlayersToSpawns(match);
            
            // Give satchel to terrorists
            GiveSatchelToTerrorists(match);
            
            // Start buy phase timer
            match.BuyPhaseTimer = timer.Once(config.BuyPhaseDuration, () => EndBuyPhase(match));
            
            // Start round timer
            float roundDuration = 120f;
            match.RoundTimer = timer.Once(roundDuration, () => EndRound(match, RoundEndReason.TimeExpired));
            
            NotifyRoundStart(match);
        }
        
        private void SwapTeams(Match match)
        {
            match.HasSwappedSides = true;
            
            foreach (var playerId in match.TerroristPlayers.ToList())
            {
                var data = GetPlayerData(playerId);
                data.CurrentTeam = Team.CounterTerrorist;
            }
            
            foreach (var playerId in match.CounterTerroristPlayers.ToList())
            {
                var data = GetPlayerData(playerId);
                data.CurrentTeam = Team.Terrorist;
            }
            
            var tempList = match.TerroristPlayers.ToList();
            match.TerroristPlayers = match.CounterTerroristPlayers.ToList();
            match.CounterTerroristPlayers = tempList;
            
            NotifyTeamSwap(match);
        }
        
        private void TeleportPlayersToSpawns(Match match)
        {
            if (!mapConfigurations.ContainsKey(match.MapName)) return;
            
            var mapConfig = mapConfigurations[match.MapName];
            
            for (int i = 0; i < match.TerroristPlayers.Count; i++)
            {
                var player = BasePlayer.FindByID(match.TerroristPlayers[i]);
                if (player != null && i < mapConfig.TerroristSpawns.Count)
                {
                    TeleportPlayer(player, mapConfig.TerroristSpawns[i]);
                    GetPlayerData(player.userID).IsSpectating = false;
                }
            }
            
            for (int i = 0; i < match.CounterTerroristPlayers.Count; i++)
            {
                var player = BasePlayer.FindByID(match.CounterTerroristPlayers[i]);
                if (player != null && i < mapConfig.CounterTerroristSpawns.Count)
                {
                    TeleportPlayer(player, mapConfig.CounterTerroristSpawns[i]);
                    GetPlayerData(player.userID).IsSpectating = false;
                }
            }
        }
        
        private void TeleportPlayer(BasePlayer player, Vector3 position)
        {
            if (player == null) return;
            
            player.Teleport(position);
            player.SendNetworkUpdateImmediate();
        }
        
        private void GiveSatchelToTerrorists(Match match)
        {
            foreach (var playerId in match.TerroristPlayers)
            {
                var player = BasePlayer.FindByID(playerId);
                if (player != null && !player.IsDead())
                {
                    GiveItem(player, "explosive.satchel", 1);
                    SendReply(player, "You have the bomb! Plant it at a bomb site.");
                    break;
                }
            }
        }
        
        private void EndBuyPhase(Match match)
        {
            match.IsBuyPhase = false;
            
            var allPlayers = match.TerroristPlayers.Concat(match.CounterTerroristPlayers);
            foreach (var playerId in allPlayers)
            {
                var player = BasePlayer.FindByID(playerId);
                if (player != null)
                {
                    var data = GetPlayerData(playerId);
                    if (data.HasBuyMenuOpen)
                    {
                        CloseBuyMenu(player);
                    }
                    SendReply(player, "Buy phase ended!");
                }
            }
        }
        
        #endregion
        
        #region Essential Commands (Simplified)
        
        private void CmdSetQueueZone(BasePlayer player, string command, string[] args)
        {
            if (!permission.UserHasPermission(player.UserIDString, PERMISSION_ADMIN))
            {
                SendReply(player, "You don't have permission to use this command!");
                return;
            }
            
            if (args.Length == 0)
            {
                SendReply(player, "Usage: /setqzone <name> [teamsize]");
                return;
            }
            
            string zoneName = args[0];
            int teamSize = args.Length > 1 ? int.Parse(args[1]) : config.DefaultTeamSize;
            
            Vector3 playerPos = player.transform.position;
            
            queueZones[zoneName] = new QueueZone
            {
                Name = zoneName,
                MinBounds = playerPos - new Vector3(10f, 5f, 10f),
                MaxBounds = playerPos + new Vector3(10f, 5f, 10f),
                TeamSize = teamSize,
                IsActive = true,
                SelectedMap = "dust2"
            };
            
            SendReply(player, $"Queue zone '{zoneName}' created with team size {teamSize}!");
            SaveData();
        }
        
        private void CmdGenerateMap(BasePlayer player, string command, string[] args)
        {
            if (!permission.UserHasPermission(player.UserIDString, PERMISSION_ADMIN))
            {
                SendReply(player, "You don't have permission to use this command!");
                return;
            }
            
            if (args.Length < 2)
            {
                SendReply(player, "Usage: /generatemap <template> <mapname>");
                SendReply(player, "Templates: dust2, mirage, inferno, cache, custom");
                return;
            }
            
            string template = args[0];
            string mapName = args[1];
            Vector3 playerPos = player.transform.position;
            
            SendReply(player, $"Generating map '{mapName}' using template '{template}'...");
            
            GenerateCompetitiveZone(mapName, playerPos, 0);
            
            SendReply(player, $"Map '{mapName}' generated successfully!");
        }
        
        private void CmdListMaps(BasePlayer player, string command, string[] args)
        {
            SendReply(player, "=== AVAILABLE MAPS ===");
            
            if (mapConfigurations.Count == 0)
            {
                SendReply(player, "No maps configured yet!");
                return;
            }
            
            foreach (var map in mapConfigurations.Values)
            {
                SendReply(player, $"• {map.MapName} - T:{map.TerroristSpawns.Count} CT:{map.CounterTerroristSpawns.Count}");
            }
        }
        
        private void CmdJoinQueue(BasePlayer player, string command, string[] args)
        {
            var data = GetPlayerData(player.userID);
            
            if (data.IsInQueue)
            {
                SendReply(player, "You are already in a queue!");
                return;
            }
            
            if (data.CurrentMatchId != 0)
            {
                SendReply(player, "You are already in a match!");
                return;
            }
            
        
// ... [Code preview truncated due to length. Full source in attached file] ...

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