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

FREE FBuildZoneStages : 1.0.0 1.0.0

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

namespace Oxide.Plugins
{
    [Info("FBuildZoneStages", "fufeldev", "1.0.0")]
    public class FBuildZoneStages : RustPlugin
    {
        private const string SpherePrefab = "assets/bundled/prefabs/modding/events/twitch/br_sphere_green.prefab";
        private const string GenericMapMarkerPrefab = "assets/prefabs/tools/map/genericradiusmarker.prefab";
        private const string VendingMapMarkerPrefab = "assets/prefabs/deployable/vendingmachine/vending_mapmarker.prefab";
        private const float ZoneRadius = 30f;
        private const float SphereStartRadius = 1f;
        private const float SphereExpandSeconds = 8f;
        private const float ZoneCheckRate = 0.5f;
        private const float DenyAttemptCooldown = 2f;
        private const int BuildSeconds = 2700;
        private const int PvpSeconds = 1200;
        private const int RaidSeconds = 5400;
        private const int WarmupSeconds = 300;
        private const float WarmupSpawnRadius = 10f;
        private const int MaxDenyAttempts = 10;

        [PluginReference] private Plugin Stadia, SClan, FClan, Clans, Friends;

        private readonly Dictionary<string, BuildZone> _zones = new Dictionary<string, BuildZone>();
        private readonly Dictionary<NetworkableId, string> _cupboardZones = new Dictionary<NetworkableId, string>();
        private readonly Dictionary<string, NetworkableId> _teamCupboards = new Dictionary<string, NetworkableId>();
        private readonly Dictionary<ulong, Vector3> _lastAllowedPositions = new Dictionary<ulong, Vector3>();
        private readonly Dictionary<ulong, int> _denyAttempts = new Dictionary<ulong, int>();
        private readonly Dictionary<ulong, float> _lastDenyAttemptTime = new Dictionary<ulong, float>();

        private Timer _stageTimer;
        private Timer _zoneTimer;
        private Timer _saveTimer;
        private Stage _stage = Stage.Raid;
        private bool _syncingStadia;
        private StoredData _data = new StoredData();

        private enum Stage
        {
            Warmup,
            Build,
            Pvp,
            Raid
        }

        private class StoredData
        {
            public string WipeId;
            public Vector3 LobbySpawn;
            public Vector3 RespawnSpawn;
            public bool HasLobbySpawn;
            public bool HasRespawnSpawn;
            public string CurrentStage;
            public long StageEndsAtUnix;
            public long SavedAtUnix;
        }

        private class BuildZone
        {
            public string Key;
            public ulong OwnerId;
            public NetworkableId CupboardId;
            public BuildingPrivlidge Cupboard;
            public SphereEntity Sphere;
            public MapMarkerGenericRadius RadiusMarker;
            public VendingMachineMapMarker NameMarker;
        }

        private void OnServerInitialized()
        {
            LoadData();
            RestoreStage();
            StartSaveTimer();
        }

        private void Unload()
        {
            SaveStageData();
            DestroyTimer(ref _stageTimer);
            DestroyTimer(ref _zoneTimer);
            DestroyTimer(ref _saveTimer);
            ClearZones();
            _teamCupboards.Clear();
        }

        private void BeginStage(Stage stage, bool resetTimer)
        {
            BeginStage(stage, resetTimer, GetStageSeconds(stage));
        }

        private void BeginStage(Stage stage, bool resetTimer, int seconds)
        {
            Stage previousStage = _stage;
            _stage = stage;
            if (resetTimer)
            {
                _data.CurrentStage = GetStageName(stage);
                _data.StageEndsAtUnix = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + Math.Max(1, seconds);
            }
            if (resetTimer)
            {
                DestroyTimer(ref _stageTimer);
                _stageTimer = timer.Once(Math.Max(1, seconds), NextStage);
            }

            if (stage == Stage.Build)
            {
                StartBuildZones();
                ClearActiveWeaponsForAll();
            }
            else
            {
                StopBuildZones();
            }

            SyncStadia(stage, resetTimer ? seconds : GetStageSeconds(stage));
            SaveStageData();
            if (stage == Stage.Warmup) NextTick(MovePlayersToWarmupSpawn);
            if (previousStage == Stage.Warmup && stage != Stage.Warmup) NextTick(MovePlayersToRespawnSpawn);
        }

        private void NextStage()
        {
            if (_stage == Stage.Warmup)
            {
                BeginStage(Stage.Build, true);
                return;
            }

            if (_stage == Stage.Build)
            {
                BeginStage(Stage.Pvp, true);
                return;
            }

            if (_stage == Stage.Pvp)
            {
                BeginStage(Stage.Raid, true);
                return;
            }

            BeginStage(Stage.Build, true);
        }

        private int GetStageSeconds(Stage stage)
        {
            if (stage == Stage.Warmup) return WarmupSeconds;
            if (stage == Stage.Build) return BuildSeconds;
            if (stage == Stage.Pvp) return PvpSeconds;
            return RaidSeconds;
        }

        private void SyncStadia(Stage stage, int seconds)
        {
            if (Stadia == null) return;
            string phase = stage == Stage.Warmup ? "warmup" : stage == Stage.Build ? "build" : stage == Stage.Pvp ? "pvp" : "raid";
            string time = (Math.Max(1, seconds) + 1).ToString();
            _syncingStadia = true;
            ConsoleSystem.Run(ConsoleSystem.Option.Server, "stadia.phase", phase, time);
            timer.Once(0.1f, () => _syncingStadia = false);
        }

        private void OnStadiaPhaseChanged(string phase)
        {
            if (_syncingStadia || string.IsNullOrWhiteSpace(phase)) return;
            phase = phase.ToLowerInvariant();
            if (phase == "phase_warmup" || phase == "warmup" || phase == "phase_time" || phase == "time") BeginStage(Stage.Warmup, true);
            else if (phase == "phase_build" || phase == "build") BeginStage(Stage.Build, true);
            else if (phase == "phase_pvp" || phase == "pvp") BeginStage(Stage.Pvp, true);
            else if (phase == "phase_raid" || phase == "raid") BeginStage(Stage.Raid, true);
        }

        [ConsoleCommand("fbuildstage.phase")]
        private void StageConsoleCommand(ConsoleSystem.Arg arg)
        {
            BasePlayer player = arg.Player();
            if (player != null && !player.IsAdmin) return;
            if (arg.Args == null || arg.Args.Length == 0) return;
            SetStageByName(arg.Args[0].ToString());
        }

        [ChatCommand("fbuildstage")]
        private void StageChatCommand(BasePlayer player, string command, string[] args)
        {
            if (player == null || !player.IsAdmin || args == null || args.Length == 0) return;
            SetStageByName(args[0]);
        }

        private void SetStageByName(string value)
        {
            if (string.IsNullOrWhiteSpace(value)) return;
            value = value.ToLowerInvariant();
            if (value == "warmup" || value == "phase_warmup" || value == "time" || value == "phase_time") BeginStage(Stage.Warmup, true);
            else if (value == "build" || value == "phase_build") BeginStage(Stage.Build, true);
            else if (value == "phase_pvp" || value == "pvp") BeginStage(Stage.Pvp, true);
            else if (value == "raid" || value == "phase_raid") BeginStage(Stage.Raid, true);
        }

        [ChatCommand("warmup")]
        private void WarmupChatCommand(BasePlayer player, string command, string[] args)
        {
            if (player == null || !player.IsAdmin) return;
            if (args == null || args.Length == 0 || args[0].ToLowerInvariant() != "set")
            {
                SendReply(player, "/warmup set - поставить точку лобби-спавна");
                return;
            }

            _data.LobbySpawn = player.transform.position;
            _data.HasLobbySpawn = true;
            SaveData();
            SendReply(player, "Точка лобби-спавна сохранена");
        }

        [ChatCommand("respawn")]
        private void RespawnChatCommand(BasePlayer player, string command, string[] args)
        {
            if (player == null || !player.IsAdmin) return;
            if (args == null || args.Length == 0 || args[0].ToLowerInvariant() != "set")
            {
                SendReply(player, "/respawn set - поставить точку респавна после вармапа");
                return;
            }

            _data.RespawnSpawn = player.transform.position;
            _data.HasRespawnSpawn = true;
            SaveData();
            SendReply(player, "Точка респавна после вармапа сохранена");
        }

        [ChatCommand("респавн")]
        private void RespawnRuChatCommand(BasePlayer player, string command, string[] args)
        {
            RespawnChatCommand(player, command, args);
        }

        [ChatCommand("вармап")]
        private void WarmupRuChatCommand(BasePlayer player, string command, string[] args)
        {
            WarmupChatCommand(player, command, args);
        }

        [ChatCommand("вармапы")]
        private void WarmupsRuChatCommand(BasePlayer player, string command, string[] args)
        {
            WarmupChatCommand(player, command, args);
        }

        private void OnPlayerInit(BasePlayer player)
        {
            QueueWarmupMove(player);
        }

        private void OnPlayerConnected(BasePlayer player)
        {
            QueueWarmupMove(player);
        }

        private void QueueWarmupMove(BasePlayer player)
        {
            if (_stage != Stage.Warmup || player == null || player.IsNpc) return;
            timer.Once(1f, () => MovePlayerToWarmupSpawn(player));
        }

        private object OnPlayerRespawn(BasePlayer player, BasePlayer.SpawnPoint spawnPoint)
        {
            if (!CanUseCustomSpawn(player)) return null;
            spawnPoint.pos = GetCustomSpawnPosition();
            spawnPoint.rot = Quaternion.identity;
            return spawnPoint;
        }

        private object OnFindSpawnPoint(BasePlayer player, ulong teamId)
        {
            if (!CanUseCustomSpawn(player)) return null;
            return new BasePlayer.SpawnPoint
            {
                pos = GetCustomSpawnPosition(),
                rot = Quaternion.identity
            };
        }

        private bool CanUseCustomSpawn(BasePlayer player)
        {
            if (player != null && player.IsNpc) return false;
            if (_data == null) return false;
            return _stage == Stage.Warmup ? _data.HasLobbySpawn : _data.HasRespawnSpawn;
        }

        private Vector3 GetCustomSpawnPosition()
        {
            return _stage == Stage.Warmup ? GetSpawnPosition(_data.LobbySpawn) : GetSpawnPosition(_data.RespawnSpawn);
        }

        private void MovePlayersToWarmupSpawn()
        {
            if (_stage != Stage.Warmup) return;
            if (_data == null || !_data.HasLobbySpawn)
            {
                PrintWarning("Точка лобби-спавна не задана. Используй /warmup set");
                return;
            }

            foreach (BasePlayer player in BasePlayer.activePlayerList)
            {
                MovePlayerToWarmupSpawn(player);
            }
        }

        private void MovePlayerToWarmupSpawn(BasePlayer player)
        {
            if (_stage != Stage.Warmup || player == null || !player.IsConnected || player.IsNpc || _data == null || !_data.HasLobbySpawn) return;
            Vector3 position = GetSpawnPosition(_data.LobbySpawn);
            if (player.isMounted) player.EnsureDismounted();
            if (player.IsAlive())
            {
                player.Teleport(position);
                return;
            }

            player.RespawnAt(position, Quaternion.identity);
        }

        private void MovePlayersToRespawnSpawn()
        {
            if (_data == null || !_data.HasRespawnSpawn)
            {
                PrintWarning("Точка респавна после вармапа не задана. Используй /respawn set");
                return;
            }

            foreach (BasePlayer player in BasePlayer.activePlayerList)
            {
                MovePlayerToRespawnSpawn(player);
            }
        }

        private void MovePlayerToRespawnSpawn(BasePlayer player)
        {
            if (player == null || !player.IsConnected || player.IsNpc) return;
            Vector3 position = GetSpawnPosition(_data.RespawnSpawn);
            if (player.isMounted) player.EnsureDismounted();
            if (player.IsAlive())
            {
                player.Teleport(position);
                return;
            }

            player.RespawnAt(position, Quaternion.identity);
        }

        private Vector3 GetSpawnPosition(Vector3 center)
        {
            Vector2 offset = UnityEngine.Random.insideUnitCircle * WarmupSpawnRadius;
            Vector3 position = center + new Vector3(offset.x, 0f, offset.y);
            return position;
        }

        private void LoadData()
        {
            try
            {
                _data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name) ?? new StoredData();
            }
            catch
            {
                _data = new StoredData();
            }
        }

        private void SaveData()
        {
            Interface.Oxide.DataFileSystem.WriteObject(Name, _data);
        }

        private void StartSaveTimer()
        {
            DestroyTimer(ref _saveTimer);
            _saveTimer = timer.Every(120f, SaveStageData);
        }

        private void SaveStageData()
        {
            if (_data == null) _data = new StoredData();
            _data.WipeId = SaveRestore.WipeId;
            _data.CurrentStage = GetStageName(_stage);
            _data.SavedAtUnix = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
            SaveData();
        }

        private void RestoreStage()
        {
            if (_data == null) _data = new StoredData();
            if (IsNewWipe())
            {
                _data.CurrentStage = GetStageName(Stage.Warmup);
                _data.StageEndsAtUnix = 0;
                _data.WipeId = SaveRestore.WipeId;
                BeginStage(Stage.Warmup, true);
                return;
            }

            Stage stage;
            if (!TryGetStage(_data.CurrentStage, out stage) || _data.StageEndsAtUnix <= 0)
            {
                BeginStage(Stage.Warmup, true);
                return;
            }

            long now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
            long endsAt = _data.StageEndsAtUnix;
            for (int i = 0; i < 10000 && endsAt <= now; i++)
            {
                stage = GetNextStage(stage);
                endsAt += GetStageSeconds(stage);
            }

            if (endsAt <= now)
            {
                BeginStage(Stage.Build, true);
                return;
            }

            _data.CurrentStage = GetStageName(stage);
            _data.StageEndsAtUnix = endsAt;
            BeginStage(stage, true, Math.Max(1, (int)(endsAt - now)));
        }

        private bool IsNewWipe()
        {
            string wipeId = SaveRestore.WipeId;
            if (!string.IsNullOrEmpty(_data.WipeId) && !string.IsNullOrEmpty(wipeId) && _data.WipeId != wipeId) return true;
            if (!string.IsNullOrEmpty(_data.WipeId) || _data.SavedAtUnix <= 0 || SaveRestore.SaveCreatedTime.Year < 2020) return false;
            long saveCreatedAt = new DateTimeOffset(SaveRestore.SaveCreatedTime.ToUniversalTime()).ToUnixTimeSeconds();
            return saveCreatedAt > _data.SavedAtUnix;
        }

        private Stage GetNextStage(Stage stage)
        {
            if (stage == Stage.Warmup) return Stage.Build;
            if (stage == Stage.Build) return Stage.Pvp;
            if (stage == Stage.Pvp) return Stage.Raid;
            return Stage.Build;
        }

        private bool TryGetStage(string value, out Stage stage)
        {
            stage = Stage.Warmup;
            if (string.IsNullOrWhiteSpace(value)) return false;
            value = value.ToLowerInvariant();
            if (value == "warmup" || value == "phase_warmup" || value == "time" || value == "phase_time")
            {
                stage = Stage.Warmup;
                return true;
            }
            if (value == "build" || value == "phase_build")
            {
                stage = Stage.Build;
                return true;
            }
            if (value == "pvp" || value == "phase_pvp")
            {
                stage = Stage.Pvp;
                return true;
            }
            if (value == "raid" || value == "phase_raid")
            {
                stage = Stage.Raid;
                return true;
            }
            return false;
        }

        private string GetStageName(Stage stage)
        {
            if (stage == Stage.Warmup) return "warmup";
            if (stage == Stage.Build) return "build";
            if (stage == Stage.Pvp) return "pvp";
            return "raid";
        }

        private void StartBuildZones()
        {
            ScanCupboards();
            if (_zoneTimer == null) _zoneTimer = timer.Every(ZoneCheckRate, CheckPlayersInZones);
        }

        private void StopBuildZones()
        {
            DestroyTimer(ref _zoneTimer);
            ClearZones();
            _lastAllowedPositions.Clear();
            _denyAttempts.Clear();
            _lastDenyAttemptTime.Clear();
        }

        private void ScanCupboards()
        {
            foreach (BaseNetworkable entity in BaseNetworkable.serverEntities)
            {
                BuildingPrivlidge priv = entity as BuildingPrivlidge;
                if (priv == null || priv.IsDestroyed) continue;
                TrackCupboard(priv, BasePlayer.FindByID(priv.OwnerID), false);
            }
        }

        private object CanBuild(Planner planner, Construction construction, Construction.Target target)
        {
            if (!IsCupboardPlacement(planner, construction)) return null;
            BasePlayer player = planner?.GetOwnerPlayer() ?? target.player;
            if (player == null) return null;
            if (_stage == Stage.Raid)
            {
                Toast(player, "На рейд стадии нельзя ставить шкаф");
                return true;
            }
            string key = GetGroupKey(player.userID, player);
            if (!HasTeamCupboard(key, null)) return null;
            Toast(player, "У вашей команды уже есть шкаф");
            return true;
        }

        private object CanDeployItem(BasePlayer player, Deployer deployer, NetworkableId entityId)
        {
            if (player == null || !IsCupboardDeployable(deployer)) return null;
            if (_stage == Stage.Raid)
            {
                Toast(player, "На рейд стадии нельзя ставить шкаф");
                return true;
            }
            string key = GetGroupKey(player.userID, player);
            if (!HasTeamCupboard(key, null)) return null;
            Toast(player, "У вашей команды уже есть шкаф");
            return true;
        }

        private void OnEntityBuilt(Planner planner, GameObject go)
        {
            BuildingPrivlidge priv = go == null ? null : UnityEngine.GameObjectEx.ToBaseEntity(go) as BuildingPrivlidge;
            if (priv == null) return;
            BasePlayer player = planner?.GetOwnerPlayer();
            NextTick(() =>
            {
                if (priv == null || priv.IsDestroyed) return;
                TrackCupboard(priv, player, true);
            });
        }

        private void OnItemDeployed(Deployer deployer, ItemModDeployable deployable, BaseEntity entity)
        {
            TrackDeployedCupboard(deployer, entity);
        }

        private void OnItemDeployed(Deployer deployer, BaseEntity parent, BaseEntity entity)
        {
            TrackDeployedCupboard(deployer, entity);
        }

        private void TrackDeployedCupboard(Deployer deployer, BaseEntity entity)
        {
            BuildingPrivlidge priv = entity as BuildingPrivlidge;
            if (priv == null) return;
            BasePlayer player = deployer?.GetOwnerPlayer();
            NextTick(() =>
            {
                if (priv == null || priv.IsDestroyed) return;
                TrackCupboard(priv, player, true);
            });
        }

        private void OnEntityKill(BuildingPrivlidge priv)
        {
            if (priv == null || priv.net == null) return;
            if (_cupboardZones.TryGetValue(priv.net.ID, out string zoneKey)) RemoveZone(zoneKey);
            RemoveTeamCupboard(priv.net.ID);
        }

        private void TrackCupboard(BuildingPrivlidge priv, BasePlayer builder, bool enforceLimit)
        {
            if (priv == null || priv.IsDestroyed || priv.net == null) return;
            ulong ownerId = priv.OwnerID.IsSteamId() ? priv.OwnerID : builder != null ? builder.userID : 0;
            if (ownerId == 0) return;
            BasePlayer owner = BasePlayer.FindAwakeOrSleepingByID(ownerId) ?? builder;
            string key = GetGroupKey(ownerId, owner);
            if (string.IsNullOrEmpty(key)) return;
            if (HasTeamCupboard(key, priv))
            {
                if (enforceLimit)
                {
                    Toast(builder, "У вашей команды уже есть шкаф");
                    RefundCupboard(builder);
                    priv.Kill();
                }
                return;
            }

            _teamCupboards[key] = priv.net.ID;
            if (_stage != Stage.Build) return;
            if (_zones.TryGetValue(key, out BuildZone current) && IsZoneAlive(current)) return;
            if (current != null) RemoveZone(key);
            BuildZone zone = new BuildZone
            {
                Key = key,
                OwnerId = ownerId,
                CupboardId = priv.net.ID,
                Cupboard = priv,
                Sphere = CreateSphere(priv.transform.position)
            };
            _zones[key] = zone;
            _cupboardZones[priv.net.ID] = key;
        }

        private bool IsCupboardPlacement(Planner planner, Construction construction)
        {
            Item item = planner?.GetOwnerItem();
            if (item != null && item.info != null && item.info.shortname == "cupboard.tool") return true;
            ItemModDeployable deployable = item?.info?.GetComponent<ItemModDeployable>();
            string path = deployable == null ? string.Empty : deployable.entityPrefab.resourcePath;
            if (!string.IsNullOrEmpty(path) && path.ToLowerInvariant().Contains("cupboard.tool")) return true;
            string prefab = construction == null ? string.Empty : construction.fullName;
            if (string.IsNullOrEmpty(prefab)) return false;
            return prefab.ToLowerInvariant().Contains("cupboard.tool");
        }

        private bool IsCupboardDeployable(Deployer deployer)
        {
            Item item = deployer?.GetOwnerItem();
            if (item != null && item.info != null && item.info.shortname == "cupboard.tool") return true;
            ItemModDeployable deployable = deployer?.GetModDeployable();
            string path = deployable == null ? string.Empty : deployable.entityPrefab.resourcePath;
            return !string.IsNullOrEmpty(path) && path.ToLowerInvariant().Contains("cupboard.tool");
        }

        private bool HasTeamCupboard(string key, BuildingPrivlidge ignore)
        {
            if (string.IsNullOrEmpty(key)) return false;
            if (!_teamCupboards.TryGetValue(key, out NetworkableId id)) return false;
            if (ignore != null && ignore.net != null && ignore.net.ID == id) return false;
            if (IsCupboardAlive(id)) return true;
            _teamCupboards.Remove(key);
            return false;
        }

        private bool IsCupboardAlive(NetworkableId id)
        {
            foreach (BaseNetworkable entity in BaseNetworkable.serverEntities)
            {
                BuildingPrivlidge priv = entity as BuildingPrivlidge;
                if (priv == null || priv.net == null || priv.IsDestroyed) continue;
                if (priv.net.ID == id) return true;
            }

            return false;
        }

        private void RemoveTeamCupboard(NetworkableId id)
        {
            string removeKey = null;
            foreach (KeyValuePair<string, NetworkableId> pair in _teamCupboards)
            {
                if (pair.Value != id) continue;
                removeKey = pair.Key;
                break;
            }

            if (removeKey != null) _teamCupboards.Remove(removeKey);
        }

        private void RefundCupboard(BasePlayer player)
        {
            if (player == null || !player.IsConnected) return;
            Item item = ItemManager.CreateByName("cupboard.tool", 1);
            if (item != null) player.GiveItem(item);
        }

        private SphereEntity CreateSphere(Vector3 position)
        {
            SphereEntity sphere = GameManager.server.CreateEntity(SpherePrefab, position, Quaternion.identity) as SphereEntity;
            if (sphere == null) return null;
            float targetRadius = ZoneRadius * 2f;
            sphere.currentRadius = SphereStartRadius;
            sphere.lerpRadius = SphereStartRadius;
            sphere.lerpSpeed = targetRadius / SphereExpandSeconds;
            sphere.enableSaving = false;
            sphere.Spawn();
            sphere.LerpRadiusTo(targetRadius, targetRadius / SphereExpandSeconds);
            return sphere;
        }

        private void CreateMapMarkers(BuildZone zone, string tag, Vector3 position)
        {
        }

        private float CalculateMapRadius()
        {
            const float a = 100 / 6f;
            float b = Mathf.Sqrt(a) / 2f;
            float c = World.Size / 1300f;
            return b / c;
        }

        private void CheckPlayersInZones()
        {
            if (_stage != Stage.Build || _zones.Count == 0) return;
            foreach (BasePlayer player in BasePlayer.activePlayerList)
            {
                if (player == null || !player.IsConnected || player.IsNpc || !player.IsAlive()) continue;
                BuildZone forbidden = FindForbiddenZone(player);
                if (forbidden == null)
                {
                    _lastAllowedPositions[player.userID] = player.transform.position;
                    _denyAttempts.Remove(player.userID);
                    _lastDenyAttemptTime.Remove(player.userID);
                    continue;
                }

                DenyZoneEntry(player, forbidden);
            }
        }

        private BuildZone FindForbiddenZone(BasePlayer player)
        {
            return FindForbiddenZone(player, player.transform.position);
        }

        private BuildZone FindForbiddenZone(BasePlayer player, Vector3 position)
        {
            List<string> deadZones = null;
            foreach (BuildZone zone in _zones.Values)
            {
                if (!IsZoneAlive(zone))
                {
                    if (deadZones == null) deadZones = new List<string>();
                    deadZones.Add(zone.Key);
                    continue;
                }
                if (Vector3.Distance(position, zone.Cupboard.transform.position) > ZoneRadius) continue;
                if (IsAllowedInZone(zone, player)) continue;
                return zone;
            }

            if (deadZones != null)
            {
                for (int i = 0; i < deadZones.Count; i++)
                    RemoveZone(deadZones[i]);
            }

            return null;
        }

        private void DenyZoneEntry(BasePlayer player, BuildZone zone)
        {
            Vector3 safePosition = GetSafePosition(player, zone);
            float now = Time.realtimeSinceStartup;
            if (_lastDenyAttemptTime.TryGetValue(player.userID, out float last) && now - last < DenyAttemptCooldown)
            {
                ForceOutOfZone(player, safePosition);
                return;
            }

            _lastDenyAttemptTime[player.userID] = now;
            int attempts = 0;
            _denyAttempts.TryGetValue(player.userID, out attempts);
            attempts++;
            _denyAttempts[player.userID] = attempts;
            Toast(player, $"Запрещено {attempts}/{MaxDenyAttempts}");

            if (attempts >= MaxDenyAttempts)
            {
                player.Kick("Запрещено входить в чужую билд-зону");
                _denyAttempts.Remove(player.userID);
                _lastDenyAttemptTime.Remove(player.userID);
                return;
            }

            ForceOutOfZone(player, safePosition);
        }

        private Vector3 GetSafePosition(BasePlayer player, BuildZone zone)
        {
            if (!_lastAllowedPositions.TryGetValue(player.userID, out Vector3 safePosition))
            {
                Vector3 direction = player.transform.position - zone.Cupboard.transform.position;
                if (direction.sqrMagnitude < 1f) direction = Vector3.forward;
                direction.y = 0f;
                safePosition = zone.Cupboard.transform.position + direction.normalized * (ZoneRadius + 2f);
                safePosition.y = player.transform.position.y;
            }

            return safePosition;
        }

        private void ForceOutOfZone(BasePlayer player, Vector3 safePosition)
        {
            if (player == null) return;
            if (player.isMounted) player.EnsureDismounted();
            player.Teleport(safePosition);
        }

        private object CanMountEntity(BasePlayer player, BaseMountable mountable)
        {
            if (_stage != Stage.Build || player == null || mountable == null) return null;
            if (FindForbiddenZone(player, mountable.transform.position) == null) return null;
            Toast(player, "Нельзя заезжать в чужую билд-зону");
            return true;
        }

        private void OnEntityMounted(BaseMountable mountable, BasePlayer player)
        {
            if (_stage != Stage.Build || player == null || mountable == null) return;
            BuildZone zone = FindForbiddenZone(player, player.transform.position);
            if (zone == null) return;
            DenyZoneEntry(player, zone);
        }

        private bool IsAllowedInZone(BuildZone zone, BasePlayer player)
        {
            if (zone == null || player == null) return false;
            if (player.IsAdmin) return true;
            if (zone.OwnerId == player.userID) return true;
            if (IsAlly(zone.OwnerId, player.userID)) return true;
            BuildingPrivlidge priv = zone.Cupboard;
            return priv != null && priv.authorizedPlayers != null && priv.authorizedPlayers.Contains(player.userID);
        }

        private bool IsAlly(ulong ownerId, ulong playerId)
        {
            if (ownerId == playerId) return true;
            RelationshipManager.PlayerTeam team = RelationshipManager.ServerInstance?.FindPlayersTeam(ownerId);
            if (team != null && team.members != null && team.members.Contains(playerId)) return true;
            if (SClan?.Call<bool>("IsTeammate", ownerId, playerId) == true) return true;
            if (Friends?.Call("GetFriends", ownerId) is ulong[] friends)
            {
                for (int i = 0; i < friends.Length; i++)
                    if (friends[i] == playerId)
                        return true;
            }
            if (Clans?.Call("GetClanMembers", ownerId.ToString()) is ulong[] clanMembers)
            {
                for (int i = 0; i < clanMembers.Length; i++)
                    if (clanMembers[i] == playerId)
                        return true;
            }
            if (FClan?.Call("GetClanMembers", ownerId) is List<ulong> fclanMembers && fclanMembers.Contains(playerId)) return true;
            return false;
        }

        private string GetGroupKey(ulong ownerId, BasePlayer player)
        {
            string tag = GetClanTag(ownerId);
            if (!string.IsNullOrEmpty(tag)) return "clan:" + tag;
            RelationshipManager.PlayerTeam team = player != null && player.currentTeam != 0 ? player.Team : RelationshipManager.ServerInstance?.FindPlayersTeam(ownerId);
            if (team != null && team.teamID != 0) return "team:" + team.teamID;
            return "player:" + ownerId;
        }

        private string GetGroupTag(ulong ownerId, BasePlayer player)
        {
            string tag = GetClanTag(ownerId);
            if (!string.IsNullOrEmpty(tag)) return tag;
            RelationshipManager.PlayerTeam team = player != null && player.currentTeam != 0 ? player.Team : RelationshipManager.ServerInstance?.FindPlayersTeam(ownerId);
            if (team != null && team.teamID != 0) return "TEAM " + team.teamID;
            BasePlayer owner = BasePlayer.FindAwakeOrSleepingByID(ownerId);
            return owner == null ? ownerId.ToString() : owner.displayName;
        }

        private string GetClanTag(ulong ownerId)
        {
            string tag = FClan?.Call<string>("GetClanTag", ownerId);
            if (!string.IsNullOrEmpty(tag)) return tag;
            tag = SClan?.Call<string>("GetClanTag", ownerId);
            return string.IsNullOrEmpty(tag) ? null : tag;
        }

        private bool IsZoneAlive(BuildZone zone)
        {
            return zone != null && zone.Cupboard != null && !zone.Cupboard.IsDestroyed && zone.Sphere != null && !zone.Sphere.IsDestroyed;
        }

        private void RemoveZone(string key)
        {
            if (string.IsNullOrEmpty(key)) return;
            if (!_zones.TryGetValue(key, out BuildZone zone)) return;
            if (zone.Sphere != null && !zone.Sphere.IsDestroyed) zone.Sphere.Kill();
            if (zone.RadiusMarker != null && !zone.RadiusMarker.IsDestroyed) zone.RadiusMarker.Kill();
            if (zone.NameMarker != null && !zone.NameMarker.IsDestroyed) zone.NameMarker.Kill();
            _zones.Remove(key);
            _cupboardZones.Remove(zone.CupboardId);
        }

        private void ClearZones()
        {
            foreach (BuildZone zone in _zones.Values)
            {
                if (zone.Sphere != null && !zone.Sphere.IsDestroyed) zone.Sphere.Kill();
                if (zone.RadiusMarker != null && !zone.RadiusMarker.IsDestroyed) zone.RadiusMarker.Kill();
                if (zone.NameMarker != null && !zone.NameMarker.IsDestroyed) zone.NameMarker.Kill();
            }

            _zones.Clear();
            _cupboardZones.Clear();
        }

        private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
        {
            if (entity == null || info == null) return null;
            if (info.InitiatorPlayer != null && info.InitiatorPlayer.IsAdmin) return null;

            if (_stage == Stage.Build)
            {
                BasePlayer target = entity as BasePlayer;
                if (target != null && !target.IsNpc)
                {
                    if (IsSuicideDamage(target, info)) return null;
                    if (info.damageTypes.Has(DamageType.Fall)) return null;
                    return true;
                }

                if (IsExplosiveDamage(info) && IsBuildProtectedEntity(entity))
                {
                    return true;
                }

                if (IsPlayerOwnedEntity(entity) && info.InitiatorPlayer != null)
                {
                    return true;
                }
            }

            if (_stage == Stage.Pvp)
            {
                BasePlayer target = entity as BasePlayer;
                if (target != null && !target.IsNpc && IsExplosiveDamage(info))
                {
                    return true;
                }

                if ((target == null || target.IsNpc) && IsBuildProtectedEntity(entity) && info.InitiatorPlayer != null)
                {
                    return true;
                }
            }

            return null;
        }

        private object CanLootPlayer(BasePlayer target, BasePlayer looter)
        {
            if (_stage != Stage.Build) return null;
            if (target == null || looter == null || target == looter) return null;
            if (!target.IsSleeping()) return null;
            Toast(looter, "Спящего игрока нельзя обыскивать");
            return false;
        }

        private bool IsSuicideDamage(BasePlayer target, HitInfo info)
        {
            if (target == null || info == null) return false;
            if (info.damageTypes.Has(DamageType.Suicide)) return true;
            return info.InitiatorPlayer == target && info.damageTypes.Total() >= 999f;
        }

        private object CanEquipItem(PlayerInventory inventory, Item item)
        {
            if (_stage != Stage.Build || inventory == null || item == null) return null;
            BasePlayer player = inventory.GetComponent<BasePlayer>();
            if (player == null || player.IsNpc || !IsBlockedHoldItem(player, item)) return null;
            Toast(player, "На билд стадии оружие запрещено");
            return false;
        }

        private object CanEquipItem(PlayerInventory inventory, Item item, int targetSlot)
        {
            return CanEquipItem(inventory, item);
        }

        private object OnActiveItemChange(BasePlayer player, Item oldItem, ItemId newItemId)
        {
            if (_stage != Stage.Build || player == null || player.IsNpc || player.inventory?.containerBelt == null) return null;
            Item newItem = player.inventory.containerBelt.FindItemByUID(newItemId);
            if (newItem == null || !IsBlockedHoldItem(player, newItem)) return null;
            Toast(player, "На билд стадии оружие запрещено");
            return true;
        }

        private void OnActiveItemChanged(BasePlayer player, Item oldItem, Item newItem)
        {
            if (_stage != Stage.Build || player == null || player.IsNpc || newItem == null) return;
            if (IsBlockedHoldItem(player, newItem)) NextTick(() => ClearActiveItem(player));
        }

        private object OnPlayerInput(BasePlayer player, InputState input)
        {
            if (_stage != Stage.Build || player == null || player.IsNpc) return null;
            Item item = player.GetActiveItem();
            if (item != null && IsBlockedHoldItem(player, item)) ClearActiveItem(player);
            return null;
        }

        private bool IsBlockedHoldItem(BasePlayer player, Item item)
        {
            if (item == null || item.info == null) return false;
            if (item.info.category == ItemCategory.Tool) return false;
            if (item.info.category == ItemCategory.Weapon) return true;
            return player != null && player.IsHostileItem(item);
        }

        private void ClearActiveWeaponsForAll()
        {
            foreach (BasePlayer player in BasePlayer.activePlayerList)
            {
                if (player == null || player.IsNpc) continue;
                Item item = player.GetActiveItem();
                if (item != null && IsBlockedHoldItem(player, item)) ClearActiveItem(player);
            }
        }

        private void ClearActiveItem(BasePlayer player)
        {
            if (player == null) return;
            player.UpdateActiveItem(default(ItemId));
            player.SendNetworkUpdateImmediate();
        }

        private bool IsRocketDamage(HitInfo info)
        {
            if (info == null || !info.damageTypes.Has(DamageType.Explosion)) return false;
            return HasRocketName(info.WeaponPrefab) || HasRocketName(info.Initiator) || HasRocketName(info.ProjectilePrefab);
        }

        private bool IsExplosiveDamage(HitInfo info)
        {
            return info != null && info.damageTypes.Has(DamageType.Explosion);
        }

        private bool HasRocketName(UnityEngine.Object obj)
        {
            if (obj == null) return false;
            BaseEntity entity = obj as BaseEntity;
            string name = entity != null ? entity.ShortPrefabName : obj.name;
            if (string.IsNullOrEmpty(name)) return false;
            name = name.ToLowerInvariant();
            return name.Contains("rocket") || name.Contains("mlrs");
        }

        private bool IsPlayerOwnedEntity(BaseEntity entity)
        {
            return entity != null && entity.OwnerID.IsSteamId();
        }

        private bool IsBuildProtectedEntity(BaseEntity entity)
        {
            if (entity == null) return false;
            if (entity is BuildingBlock || entity is SimpleBuildingBlock) return true;
            return IsPlayerOwnedEntity(entity);
        }

        private void Toast(BasePlayer player, string text)
        {
            if (player == null || !player.IsConnected) return;
            player.ShowToast(GameTip.Styles.Red_Normal, new Translate.Phrase("fbuildzonestages.toast", text), false);
        }

        private void DestroyTimer(ref Timer value)
        {
            if (value == null) return;
            value.Destroy();
            value = null;
        }

        [HookMethod("GetCurrentPhase")]
        public string GetCurrentPhase()
        {
            if (_stage == Stage.Warmup) return "warmup";
            if (_stage == Stage.Build) return "build";
            if (_stage == Stage.Pvp) return "pvp";
            return "raid";
        }

        [HookMethod("GetCupboardInfo")]
        public Dictionary<string, object> GetCupboardInfo(BuildingPrivlidge priv)
        {
            if (priv == null || priv.net == null) return null;
            string key = null;
            if (!_cupboardZones.TryGetValue(priv.net.ID, out key))
            {
                foreach (KeyValuePair<string, NetworkableId> entry in _teamCupboards)
                {
                    if (entry.Value == priv.net.ID)
                    {
                        key = entry.Key;
                        break;
                    }
                }
            }

            string clanTag = null;
            if (!string.IsNullOrEmpty(key) && key.StartsWith("clan:")) clanTag = key.Substring(5);
            if (string.IsNullOrEmpty(clanTag)) clanTag = GetClanTag(priv.OwnerID);
            return new Dictionary<string, object>
            {
                ["phase"] = GetCurrentPhase(),
                ["ownerId"] = priv.OwnerID,
                ["groupKey"] = key ?? string.Empty,
                ["clanTag"] = clanTag ?? string.Empty
            };
        }
    }
}

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