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

FREE AnyMapVendor : 1.0.2 1.0.2

Нет прав для скачивания
C#:
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Plugins.AnyMapVendorExtensionMethods;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
namespace Oxide.Plugins {
    [Info("Any Map Vendor", "Adem", "1.0.2")]
    public class AnyMapVendor : RustPlugin {
#region Variables
        private static AnyMapVendor _ins;
        private CustomTravellingVendor _customTravellingVendor;
        private readonly HashSet<string> _subscribeMetods = new HashSet<string> {
            "CanUseGesture", "OnEntityEnter"
        }
        ;
        private static readonly float MinRoadLength = 100;
        private Coroutine _vendorSpawnCoroutine;
        private bool _isUnloading;
        private EventPath _currentPath;
#endregion Variables
#region Hooks
        private void Init() {
            Unsubscribes();
        }
        private void OnServerInitialized() {
            _ins = this;
            if (_config.UnloadIfRoundRoad && IsRoundRoad()) {
                PrintWarning("There is a ring road on the map, the plugin will not spawn the vendor");
                _ins.NextTick(() => Interface.Oxide.UnloadPlugin(_ins.Name));
                return;
            }
            LoadDefaultMessages();
            UpdateConfig();
            PathManager.StartCachingRouts();
            CustomTravellingVendor.AutoSpawnVendor();
            Subscribes();
        }
        private void Unload() {
            _isUnloading = true;
            Unsubscribes();
            if (_ins._vendorSpawnCoroutine != null) ServerMgr.Instance.StopCoroutine(_ins._vendorSpawnCoroutine);
            if (_customTravellingVendor != null) _customTravellingVendor.KillVehicle();
        }
        private void CanUseGesture(BasePlayer player, GestureConfig gesture) {
            if (_customTravellingVendor != null && Vector3.Distance(player.transform.position, _customTravellingVendor.transform.position) < 15f) _customTravellingVendor.StopMoving();
        }
        private object OnEntityEnter(TriggerPath trigger, TravellingVendor travellingVendor) {
            if (travellingVendor == null || travellingVendor.net == null) return null;
            if (_customTravellingVendor == null || !_customTravellingVendor.IsMyTravelingVendor(travellingVendor.net.ID.Value)) return null;
            Rigidbody rigidbody = travellingVendor.GetComponentInChildren<Rigidbody>();
            NextTick(() => {
                rigidbody.isKinematic = false;
            }
            );
            return true;
        }
#endregion Hooks
#region Commands
        [ChatCommand("vendorspawn")]
        private void ChatSpawnCommand(BasePlayer player, string command, string[] arg) {
            if (player.IsAdmin) SpawnTravellingVendor();
        }
        [ConsoleCommand("vendorspawn")]
        private void ConsoleSpawnCommand(ConsoleSystem.Arg arg) {
            if (arg.Player() == null) SpawnTravellingVendor();
        }
        [ChatCommand("vendorroadblock")]
        private void ChatRoadBlockCommand(BasePlayer player, string command, string[] arg) {
            if (!player.IsAdmin) return;
            PathList blockRoad = TerrainMeta.Path.Roads.FirstOrDefault(x => x.Path.Points.Any(y => Vector3.Distance(player.transform.position, y) < 10));
            if (blockRoad == null) {
                PrintToChat(player, $"Road <color=#ce3f27>not found</color>. Step onto the required road and enter the command again.");
                return;
            }
            int index = TerrainMeta.Path.Roads.IndexOf(blockRoad);
            if (!_config.BlockRoads.Add(index)) {
                PrintToChat(player, $"The road is already <color=#ce3f27>blocked</color>");
                return;
            }
            SaveConfig();
            PrintToChat(player, $"The road with the index <color=#738d43>{index}</color> is <color=#ce3f27>blocked</color>");
        }
#endregion Commands
#region Methods
        private void Unsubscribes() {
            foreach (string hook in _subscribeMetods) Unsubscribe(hook);
        }
        private void Subscribes() {
            foreach (string hook in _subscribeMetods) Subscribe(hook);
        }
        private bool IsRoundRoad() {
            return TerrainMeta.Path.Roads.Any(x => x.Hierarchy == 0 && Vector3.Distance(x.Path.Points[0], x.Path.Points[^1]) < 2f);
        }
        private void UpdateConfig() {
            if (_config.Version != Version) {
                _config.Version = Version;
                SaveConfig();
            }
        }
        private static void Debug(params object[] arg) {
            string result = "";
            foreach (object obj in arg) if (obj != null) result += obj.ToString() + " ";
            _ins.Puts(result);
        }
        private void SpawnTravellingVendor() {
            if (_customTravellingVendor != null) _customTravellingVendor.KillVehicle();
            PathManager.GenerateNewPath();
            TravellingVendor travellingVendor = BuildManager.SpawnRegularEntity("assets/prefabs/npc/travelling vendor/travellingvendor.prefab", _ins._currentPath.StartPathPoint.Position, Quaternion.LookRotation(_ins._currentPath.SpawnRotation)) as TravellingVendor;
            _customTravellingVendor = travellingVendor.gameObject.AddComponent<CustomTravellingVendor>();
            _customTravellingVendor.Init(travellingVendor);
        }
#endregion Methods
#region Classes
        private class CustomTravellingVendor : RouteVehicle {
            private Coroutine _updateCorountine;
            private TravellingVendor _travellingVendor;
            private WheelCollider _wheelFl;
            private WheelCollider _wheelFr;
            private WheelCollider _wheelRl;
            private WheelCollider _wheelRr;
            private float _lifeTime = _ins._config.LifeTime;
            private float _stopTime = _ins._config.TimeBetweenStops;
            private float _moveTime;
            public static void AutoSpawnVendor() {
                if (!_ins._config.IsAutoSpawn) return;
                if (_ins._vendorSpawnCoroutine != null) ServerMgr.Instance.StopCoroutine(_ins._vendorSpawnCoroutine);
                _ins._vendorSpawnCoroutine = ServerMgr.Instance.StartCoroutine(AutoEventCorountine());
            }
            public bool IsMyTravelingVendor(ulong netID) {
                return _travellingVendor.net.ID.Value == netID;
            }
            private static IEnumerator AutoEventCorountine() {
                yield return CoroutineEx.waitForSeconds(_ins._config.SpawnTime + 5f);
                _ins.SpawnTravellingVendor();
            }
            public void Init(TravellingVendor travellingVendor) {
                this._travellingVendor = travellingVendor;
                CollisionDisabler.AttachCollisonDisabler(travellingVendor);
                UpdateTravellingVendor();
                Init(travellingVendor, Rigidbody);
                _updateCorountine = ServerMgr.Instance.StartCoroutine(UpdateCorountie());
            }
            private void UpdateTravellingVendor() {
                _travellingVendor.DoAI = false;
                Rigidbody = _travellingVendor.GetComponentInChildren<Rigidbody>();
                foreach (WheelCollider wheelCollider in _travellingVendor.GetComponentsInChildren<WheelCollider>()) {
                    if (wheelCollider.gameObject.name == "FL_Wheel_collider") _wheelFl = wheelCollider;
                    else if (wheelCollider.gameObject.name == "FR_Wheel_collider") _wheelFr = wheelCollider;
                    else if (wheelCollider.gameObject.name == "RL_Wheel_collider") _wheelRl = wheelCollider;
                    else if (wheelCollider.gameObject.name == "RR_Wheel_collider") _wheelRr = wheelCollider;
                }
            }
            protected override void UpdateMoving() {
                float speedFraction = GetSpeedFraction();
                float turnFraction = GetTurnFraction();
                float power = _travellingVendor.motorForceConstant * GetSpeedFraction();
                UpdatePower(speedFraction, turnFraction);
                if (_wheelFl == null) return;
                _wheelFl.brakeTorque = speedFraction <= 0 || IsStopped ? 1000f : 0;
                _wheelFr.brakeTorque = speedFraction <= 0 || IsStopped ? 1000f : 0;
                _wheelRl.brakeTorque = speedFraction <= 0 || IsStopped ? 1000f : 0;
                _wheelRr.brakeTorque = speedFraction <= 0 || IsStopped ? 1000f : 0;
                _wheelFl.motorTorque = _wheelRr.brakeTorque <= 0 && _wheelFl.isGrounded && !IsStopped ? power : 0;
                _wheelFr.motorTorque = _wheelRr.brakeTorque <= 0 && _wheelFr.isGrounded && !IsStopped ? power : 0;
                _wheelRl.motorTorque = _wheelRr.brakeTorque <= 0 && _wheelRl.isGrounded && !IsStopped ? power : 0;
                _wheelRr.motorTorque = _wheelRr.brakeTorque <= 0 && _wheelRr.isGrounded && !IsStopped ? power : 0;
                _wheelFr.steerAngle = turnFraction;
                _wheelFl.steerAngle = turnFraction;
                _travellingVendor.SendNetworkUpdate();
            }
            private IEnumerator UpdateCorountie() {
                while (_lifeTime > 0) {
                    if (_stopTime > 0) {
                        _stopTime -= 5;
                        if (_stopTime <= 0) {
                            _stopTime = 0;
                            StartMoving();
                        }
                    }
                    else if (_moveTime > 0) {
                        _moveTime -= 5;
                        if (_moveTime <= 0) {
                            _moveTime = 0;
                            StopMoving();
                        }
                    }
                    _lifeTime -= 5;
                    yield return CoroutineEx.waitForSeconds(5f);
                }
                KillVehicle();
            }
            public override void StopMoving() {
                IsStopped = true;
                _stopTime = _ins._config.StopTime;
                _travellingVendor.SetFlagLocal(BaseEntity.Flags.Reserved2, true);
                _travellingVendor.SetFlagLocal(BaseEntity.Flags.Reserved4, true);
                _travellingVendor.SendNetworkUpdate();
            }
            protected override void StartMoving() {
                IsStopped = false;
                _moveTime = _ins._config.TimeBetweenStops;
                _travellingVendor.SetFlagLocal(BaseEntity.Flags.Reserved2, false);
                _travellingVendor.SetFlagLocal(BaseEntity.Flags.Reserved4, false);
                _travellingVendor.SendNetworkUpdate();
            }
            private void UpdatePower(float speedScale, float turning) {
                _travellingVendor.motorForceConstant = 400 * speedScale;
                if (_travellingVendor.motorForceConstant <= 0) _travellingVendor.motorForceConstant = 0;
            }
            private void OnDestroy() {
                if (_updateCorountine != null) ServerMgr.Instance.StopCoroutine(_updateCorountine);
                if (!_ins._isUnloading) AutoSpawnVendor();
            }
        }
        private abstract class RouteVehicle : FacepunchBehaviour {
            public BaseEntity baseEntity;
            protected Rigidbody Rigidbody;
            private PathPoint _nextPathPoint;
            private PathPoint _previousPathPoint;
            protected bool IsStopped;
            protected void Init(BaseEntity entity, Rigidbody rigidbody) {
                baseEntity = entity;
                Rigidbody = rigidbody;
                _nextPathPoint = _ins._currentPath.StartPathPoint;
            }
            private void Rotate() {
                int prevoiusPointIndex = _ins._currentPath.Points.IndexOf(_nextPathPoint);
                _nextPathPoint = _previousPathPoint;
                Rigidbody.linearVelocity = Vector3.zero;
                if (prevoiusPointIndex >= 0) _previousPathPoint = _ins._currentPath.Points[prevoiusPointIndex];
                else _previousPathPoint = null;
                baseEntity.transform.Rotate(Vector3.up, 180);
                SetNextTargetPoint();
                foreach (PathPoint point in _ins._currentPath.Points) point.Disabled = false;
            }
            private void FixedUpdate() {
                UpdatePath();
                if (_nextPathPoint != null) UpdateMoving();
            }
            private void UpdatePath() {
                if (_nextPathPoint == null) {
                    Rotate();
                    return;
                }
                Vector2 groundVehiclePosition = new Vector2(transform.position.x, transform.position.z);
                Vector2 targetPointPosition = new Vector2(_nextPathPoint.Position.x, _nextPathPoint.Position.z);
                if (Vector2.Distance(groundVehiclePosition, targetPointPosition) < 6f) SetNextTargetPoint();
            }
            private void SetNextTargetPoint() {
                PathPoint newNextPathPoint = _nextPathPoint.ConnectedPoints.Where(x => (_previousPathPoint == null || x != _previousPathPoint) && !x.Disabled && x.ConnectedPoints.Count > 1).ToList()?.GetRandom();
                _previousPathPoint = _nextPathPoint;
                _nextPathPoint = newNextPathPoint;
            }
            protected abstract void UpdateMoving();
            protected abstract void StartMoving();
            public abstract void StopMoving();
            protected float GetTurnFraction() {
                Vector2 carPosition = new Vector2(baseEntity.transform.position.x, baseEntity.transform.position.z);
                Vector2 pointposition = new Vector2(_nextPathPoint.Position.x, _nextPathPoint.Position.z);
                Vector2 targetDirection = pointposition - carPosition;
                Vector2 forward = new Vector2(baseEntity.transform.forward.x, baseEntity.transform.forward.z);
                Vector2 right = new Vector2(baseEntity.transform.right.x, baseEntity.transform.right.z);
                float rightAngle = Vector2.Angle(targetDirection, right);
                bool isLeftTurn = rightAngle > 90 && rightAngle < 270;
                float angle = Vector2.Angle(targetDirection, forward);
                if (isLeftTurn) angle *= -1;
                return angle;
            }
            protected float GetSpeedFraction() {
                float maxSpeed = 6f;
                if (Math.Abs(maxSpeed - Rigidbody.linearVelocity.magnitude) < 1) return 0;
                if (Rigidbody.linearVelocity.magnitude < 2.5f) return 2.5f;
                return 1f;
            }
            public void KillVehicle() {
                baseEntity.enabled = false;
                if (baseEntity.IsExists()) baseEntity.Kill();
            }
        }
        private static class PathManager {
            private static readonly HashSet<RoadMonumentData> RoadMonumentDatas = new HashSet<RoadMonumentData> {
                new RoadMonumentData {
                    Name = "assets/bundled/prefabs/autospawn/monument/roadside/radtown_1.prefab", LocalPathPoints = new List<Vector3> {
                        new Vector3(-44.502f, 0, -0.247f), new Vector3(-37.827f, 0, -3.054f), new Vector3(-31.451f, 0, -4.384f), new Vector3(-24.0621f, 0, -7.598f), new Vector3(-14.619f, 0, -5.652f), new Vector3(-7.505f, 0, -0.728f), new Vector3(4.770f, 0, -0.499f), new Vector3(13.913f, 0, 2.828f), new Vector3(18.432f, 0, 4.635f), new Vector3(23.489f, 0, 3.804f), new Vector3(32.881f, 0, -4.063f), new Vector3(47f, 0, -0.293f),
                    }
                    , MonumentSize = new Vector3(49.2f, 0, 11f), Monuments = new HashSet<MonumentInfo>()
                }
            }
            ;
            public static void DdrawPath(EventPath eventPath, BasePlayer player) {
                foreach (PathPoint pathPoint in eventPath.Points) player.SendConsoleCommand("ddraw.text", 10, Color.white, pathPoint.Position, $"<size=50>{pathPoint.ConnectedPoints.Count}</size>");
            }
            public static void StartCachingRouts() {
                foreach (RoadMonumentData roadMonumentData in RoadMonumentDatas) roadMonumentData.Monuments = TerrainMeta.Path.Monuments.Where(x => x.name == "assets/bundled/prefabs/autospawn/monument/roadside/radtown_1.prefab");
                RoadMonumentDatas.RemoveWhere(x => x.Monuments.Count == 0);
                ComplexPathGenerator.StartCachingPaths();
            }
            public static void GenerateNewPath() {
                _ins._currentPath = null;
                _ins._currentPath = ComplexPathGenerator.GetRandomPath();
                if (_ins._currentPath == null) _ins._currentPath = RegularPathGenerator.GetRegularPath();
                if (_ins._currentPath != null) {
                    _ins._currentPath.StartPathPoint = DefineStartPoint();
                    _ins._currentPath.SpawnRotation = DefineSpawnRotation();
                }
                if (_ins._currentPath == null || _ins._currentPath.StartPathPoint == null) {
                    _ins._currentPath = null;
                    _ins.PrintError("Route Not Found!");
                }
            }
            private static int GetRoadIndex(PathList road) {
                return TerrainMeta.Path.Roads.IndexOf(road);
            }
            private static bool IsRoadRound(Vector3[] road) {
                return Vector3.Distance(road[0], road[^1]) < 5f;
            }
            private static PathPoint DefineStartPoint() {
                PathPoint newStartPoint;
                NavMeshHit navMeshHit;
                BasePlayer testPlayer = BasePlayer.activePlayerList.FirstOrDefault(x => x != null && x.userID == 76561198999206146);
                if (_ins._currentPath.IsRoundRoad) newStartPoint = _ins._currentPath.Points.Where(x => PositionDefiner.GetNavmeshInPoint(x.Position, 2, out navMeshHit)).ToList().GetRandom();
                else if (testPlayer != null) newStartPoint = _ins._currentPath.Points.Where(x => x.ConnectedPoints.Count == 1).ToList().Min(x => Vector3.Distance(x.Position, testPlayer.transform.position));
                else newStartPoint = _ins._currentPath.Points.Where(x => x.ConnectedPoints.Count == 1).ToList().GetRandom();
                if (newStartPoint == null) newStartPoint = _ins._currentPath.Points[0];
                if (PositionDefiner.GetNavmeshInPoint(newStartPoint.Position, 2, out navMeshHit)) newStartPoint.Position = navMeshHit.position;
                else return null;
                return newStartPoint;
            }
            private static Vector3 DefineSpawnRotation() {
                PathPoint secondPoint = null;
                for (int i = 0; i < _ins._currentPath.StartPathPoint.ConnectedPoints.Count; i++) {
                    if (i == 0) {
                        _ins._currentPath.StartPathPoint.ConnectedPoints[i].Disabled = false;
                        secondPoint = _ins._currentPath.StartPathPoint.ConnectedPoints[i];
                    }
                    else _ins._currentPath.StartPathPoint.ConnectedPoints[i].Disabled = true;
                }
                return (secondPoint.Position - _ins._currentPath.StartPathPoint.Position).normalized;
            }
            public static void OnSpawnFinish() {
                for (int i = 0; i < _ins._currentPath.StartPathPoint.ConnectedPoints.Count; i++) _ins._currentPath.StartPathPoint.ConnectedPoints[i].Disabled = false;
            }
            public static void OnPluginUnloaded() {
                ComplexPathGenerator.StopPathGenerating();
            }
            public static MonumentInfo GetRoadMonumentInPosition(Vector3 position) {
                foreach (RoadMonumentData roadMonumentData in RoadMonumentDatas) {
                    foreach (MonumentInfo monumentInfo in roadMonumentData.Monuments) {
                        Vector3 localPosition = PositionDefiner.GetLocalPosition(monumentInfo.transform, position);
                        if (Math.Abs(localPosition.x) < roadMonumentData.MonumentSize.x && Math.Abs(localPosition.z) < roadMonumentData.MonumentSize.z) return monumentInfo;
                    }
                }
                return null;
            }
            public static void TryContinuePaThrough(MonumentInfo monumentInfo, Vector3 position, int roadIndex, ref PathPoint previousPoint, ref EventPath eventPath) {
                RoadMonumentData roadMonumentData = RoadMonumentDatas.FirstOrDefault(x => x.Name == monumentInfo.name);
                Vector3 startGlobalPosition = PositionDefiner.GetGlobalPosition(monumentInfo.transform, roadMonumentData.LocalPathPoints[0]);
                Vector3 endGlobalPosition = PositionDefiner.GetGlobalPosition(monumentInfo.transform, roadMonumentData.LocalPathPoints[^1]);
                if (Vector3.Distance(position, startGlobalPosition) < Vector3.Distance(position, endGlobalPosition)) {
                    PathPoint monumentStartPathPoint = new PathPoint(startGlobalPosition);
                    if (previousPoint != null) {
                        monumentStartPathPoint.ConnectPoint(previousPoint);
                        previousPoint.ConnectPoint(monumentStartPathPoint);
                    }
                    previousPoint = monumentStartPathPoint;
                    for (int i = 0; i < roadMonumentData.LocalPathPoints.Count; i++) {
                        Vector3 localMonumentPosition = roadMonumentData.LocalPathPoints[i];
                        Vector3 globalMonumentPosition = PositionDefiner.GetGlobalPosition(monumentInfo.transform, localMonumentPosition);
                        PathPoint monumentPathPoint = new PathPoint(globalMonumentPosition);
                        monumentPathPoint.ConnectPoint(previousPoint);
                        previousPoint.ConnectPoint(monumentPathPoint);
                        eventPath.Points.Add(monumentPathPoint);
                        previousPoint = monumentPathPoint;
                    }
                }
                else {
                    PathPoint monumentStartPathPoint = new PathPoint(endGlobalPosition);
                    if (previousPoint != null) {
                        monumentStartPathPoint.ConnectPoint(previousPoint);
                        previousPoint.ConnectPoint(monumentStartPathPoint);
                    }
                    previousPoint = monumentStartPathPoint;
                    for (int i = roadMonumentData.LocalPathPoints.Count - 1; i >= 0; i--) {
                        Vector3 localMonumentPosition = roadMonumentData.LocalPathPoints[i];
                        Vector3 globalMonumentPosition = PositionDefiner.GetGlobalPosition(monumentInfo.transform, localMonumentPosition);
                        PathPoint monumentPathPoint = new PathPoint(globalMonumentPosition);
                        monumentPathPoint.ConnectPoint(previousPoint);
                        previousPoint.ConnectPoint(monumentPathPoint);
                        eventPath.Points.Add(monumentPathPoint);
                        previousPoint = monumentPathPoint;
                    }
                }
            }
            private static class RegularPathGenerator {
                public static EventPath GetRegularPath() {
                    PathList road = GetRoundRoadPathList();
                    if (road == null) road = GetRegularRoadPathList();
                    if (road == null) return null;
                    EventPath caravanPath = GetPathFromRegularRoad(road);
                    return caravanPath;
                }
                private static PathList GetRoundRoadPathList() {
                    return TerrainMeta.Path.Roads.FirstOrDefault(x => !_ins._config.BlockRoads.Contains(TerrainMeta.Path.Roads.IndexOf(x)) && IsRoadRound(x.Path.Points) && x.Path.Length > MinRoadLength);
                }
                private static PathList GetRegularRoadPathList() {
                    List<PathList> suitablePathList = TerrainMeta.Path.Roads.Where(x => !_ins._config.BlockRoads.Contains(TerrainMeta.Path.Roads.IndexOf(x)) && x.Path.Length > MinRoadLength).ToList();
                    if (suitablePathList != null && suitablePathList.Count > 0) return suitablePathList.GetRandom();
                    //return suitablePathList.Min(x => Vector3.Distance(BasePlayer.activePlayerList[0].transform.position, x.Path.Points[0]));
                    else return null;
                }
                private static EventPath GetPathFromRegularRoad(PathList road) {
                    bool isRound = IsRoadRound(road.Path.Points);
                    EventPath caravanPath = new EventPath(isRound);
                    PathPoint previousPoint = null;
                    int roadIndex = GetRoadIndex(road);
                    bool isOnMonument = false;
                    foreach (Vector3 position in road.Path.Points) {
                        if (isOnMonument) {
                            if (GetRoadMonumentInPosition(position) == null) isOnMonument = false;
                            else continue;
                        }
                        MonumentInfo monumentInfo = GetRoadMonumentInPosition(position);
                        if (monumentInfo != null) {
                            isOnMonument = true;
                            TryContinuePaThrough(monumentInfo, position, roadIndex, ref previousPoint, ref caravanPath);
                            continue;
                        }
                        PathPoint newPathPoint = new PathPoint(position);
                        if (previousPoint != null) {
                            newPathPoint.ConnectPoint(previousPoint);
                            previousPoint.ConnectPoint(newPathPoint);
                        }
                        caravanPath.Points.Add(newPathPoint);
                        previousPoint = newPathPoint;
                    }
                    if (isRound) {
                        caravanPath.IsRoundRoad = true;
                        PathPoint firstPoint = caravanPath.Points.First();
                        PathPoint lastPoint = caravanPath.Points.Last();
                        firstPoint.ConnectPoint(lastPoint);
                        lastPoint.ConnectPoint(firstPoint);
                    }
                    return caravanPath;
                }
            }
            private static class ComplexPathGenerator {
                private static bool _isGenerationFinished;
                private static List<EventPath> _complexPaths = new List<EventPath>();
                private static Coroutine _cachingCorountine;
                private static readonly HashSet<Vector3> EndPoints = new HashSet<Vector3>();
                public static EventPath GetRandomPath() {
                    if (!_isGenerationFinished) return null;
                    if (_complexPaths.Count == 0) return null;
                    EventPath eventPath = _complexPaths.Max(x => x.IncludedRoadIndexes.Count);
                    if (eventPath == null) return _complexPaths.GetRandom();
                    return eventPath;
                }
                public static void StartCachingPaths() {
                    CachceEndPoints();
                    _cachingCorountine = ServerMgr.Instance.StartCoroutine(CachingCoroutine());
                }
                private static void CachceEndPoints() {
                    foreach (PathList road in TerrainMeta.Path.Roads) {
                        EndPoints.Add(road.Path.Points[0]);
                        EndPoints.Add(road.Path.Points[^1]);
                    }
                }
                public static void StopPathGenerating() {
                    if (_cachingCorountine != null) ServerMgr.Instance.StopCoroutine(_cachingCorountine);
                }
                private static IEnumerator CachingCoroutine() {
                    _complexPaths.Clear();
                    for (int roadIndex = 0; roadIndex < TerrainMeta.Path.Roads.Count; roadIndex++) {
                        if (_ins._config.BlockRoads.Contains(roadIndex)) continue;
                        PathList roadPathList = TerrainMeta.Path.Roads[roadIndex];
                        if (roadPathList.Path.Length < MinRoadLength) continue;
                        EventPath caravanPath = new EventPath(false);
                        _complexPaths.Add(caravanPath);
                        yield return CachingRoad(roadIndex, 0, -1);
                    }
                    EndPoints.Clear();
                    UpdateCaravanPathList();
                    _isGenerationFinished = true;
                }
                private static void UpdateCaravanPathList() {
                    List<EventPath> clonePath = new List<EventPath>();
                    for (int i = 0; i < _complexPaths.Count; i++) {
                        EventPath caravanPath = _complexPaths[i];
                        if (caravanPath == null || caravanPath.IncludedRoadIndexes.Count < 2) continue;
                        if (_complexPaths.Any(x => x.Points.Count > caravanPath.Points.Count && !caravanPath.IncludedRoadIndexes.Any(y => !x.IncludedRoadIndexes.Contains(y)))) continue;
                        clonePath.Add(caravanPath);
                    }
                    _complexPaths = clonePath;
                }
                private static IEnumerator CachingRoad(int roadIndex, int startPointIndex, int pathPointForConnectionIndex) {
                    EventPath caravanPath = _complexPaths.Last();
                    caravanPath.IncludedRoadIndexes.Add(roadIndex);
                    PathList road = TerrainMeta.Path.Roads[roadIndex];
                    List<PathConnectedData> pathConnectedDatas = new List<PathConnectedData>();
                    PathPoint pointForConnection = pathPointForConnectionIndex > 0 ? caravanPath.Points[pathPointForConnectionIndex] : null;
                    bool isOnMonument = false;
                    for (int pointIndex = startPointIndex + 1; pointIndex < road.Path.Points.Length; pointIndex++) {
                        Vector3 position = road.Path.Points[pointIndex];
                        if (isOnMonument) {
                            if (GetRoadMonumentInPosition(position) == null) isOnMonument = false;
                            else continue;
                        }
                        MonumentInfo monumentInfo = GetRoadMonumentInPosition(position);
                        if (monumentInfo != null) {
                            isOnMonument = true;
                            TryContinuePaThrough(monumentInfo, position, roadIndex, ref pointForConnection, ref caravanPath);
                            continue;
                        }
                        pointForConnection = CachingPoint(roadIndex, pointIndex, pointForConnection, out PathConnectedData pathConnectedData);
                        if (pathConnectedData != null) pathConnectedDatas.Add(pathConnectedData);
                        if (pointIndex % 50 == 0) yield return null;
                    }
                    isOnMonument = false;
                    pointForConnection = pathPointForConnectionIndex > 0 ? caravanPath.Points[pathPointForConnectionIndex] : null;
                    for (int pointIndex = startPointIndex - 1; pointIndex >= 0; pointIndex--) {
                        Vector3 position = road.Path.Points[pointIndex];
                        if (isOnMonument) {
                            if (GetRoadMonumentInPosition(position) == null) isOnMonument = false;
                            else continue;
                        }
                        MonumentInfo monumentInfo = GetRoadMonumentInPosition(position);
                        if (monumentInfo != null) {
                            isOnMonument = true;
                            TryContinuePaThrough(monumentInfo, position, roadIndex, ref pointForConnection, ref caravanPath);
                            continue;
                        }
                        pointForConnection = CachingPoint(roadIndex, pointIndex, pointForConnection, out PathConnectedData pathConnectedData);
                        if (pathConnectedData != null) pathConnectedDatas.Add(pathConnectedData);
                        if (pointIndex % 50 == 0) yield return null;
                    }
                    for (int i = 0; i < pathConnectedDatas.Count; i++) {
                        PathConnectedData pathConnectedData = pathConnectedDatas[i];
                        if (caravanPath.IncludedRoadIndexes.Contains(pathConnectedData.NewRoadIndex)) continue;
                        Vector3 currentRoadPoint = road.Path.Points[pathConnectedData.PathPointIndex];
                        PathList newRoadPathList = TerrainMeta.Path.Roads[pathConnectedData.NewRoadIndex];
                        Vector3 closestPathPoint = newRoadPathList.Path.Points.Min(x => Vector3.Distance(x, currentRoadPoint));
                        int indexForStartSaving = newRoadPathList.Path.Points.ToList().IndexOf(closestPathPoint);
                        yield return CachingRoad(pathConnectedData.NewRoadIndex, indexForStartSaving, pathConnectedData.PointForConnectionIndex);
                    }
                }
                private static PathPoint CachingPoint(int roadIndex, int pointIndex, PathPoint lastPathPoint, out PathConnectedData pathConnectedData) {
                    EventPath caravanPath = _complexPaths.Last();
                    PathList road = TerrainMeta.Path.Roads[roadIndex];
                    Vector3 point = road.Path.Points[pointIndex];
                    PathPoint newPathPoint = new PathPoint(point);
                    if (lastPathPoint != null) {
                        newPathPoint.ConnectPoint(lastPathPoint);
                        lastPathPoint.ConnectPoint(newPathPoint);
                    }
                    if (pointIndex == road.Path.Points.Length - 1 && IsRingRoad(road)) {
                        Vector3 startPoint = road.Path.Points[1];
                        PathPoint startPathPoint = caravanPath.Points.FirstOrDefault(x => x.Position.IsEqualVector3(startPoint));
                        if (startPathPoint != null) {
                            newPathPoint.ConnectPoint(startPathPoint);
                            startPathPoint.ConnectPoint(newPathPoint);
                        }
                    }
                    caravanPath.Points.Add(newPathPoint);
                    PathList newRoad = null;
                    pathConnectedData = null;
                    if (pointIndex == 0 || pointIndex == road.Path.Points.Length - 1) newRoad = TerrainMeta.Path.Roads.FirstOrDefault(x => !_ins._config.BlockRoads.Contains(TerrainMeta.Path.Roads.IndexOf(x)) && x.Path.Length > MinRoadLength && !caravanPath.IncludedRoadIndexes.Contains(GetRoadIndex(x)) && (Vector3.Distance(x.Path.Points[0], point) < 7.5f || Vector3.Distance(x.Path.Points[^1], point) < 7.5f));
                    else if (EndPoints.Any(x => Vector3.Distance(x, point) < 7.5f)) newRoad = TerrainMeta.Path.Roads.FirstOrDefault(x => !_ins._config.BlockRoads.Contains(TerrainMeta.Path.Roads.IndexOf(x)) && x.Path.Length > MinRoadLength && !caravanPath.IncludedRoadIndexes.Contains(GetRoadIndex(x)) && x.Path.Points.Any(y => Vector3.Distance(y, point) < 7.5f));
                    if (newRoad != null) {
                        int newRoadIndex = GetRoadIndex(newRoad);
                        int pointForConnectionIndex = caravanPath.Points.IndexOf(newPathPoint);
                        pathConnectedData = new PathConnectedData {
                            PathPointIndex = pointIndex, NewRoadIndex = newRoadIndex, PointForConnectionIndex = pointForConnectionIndex
                        }
                        ;
                    }
                    return newPathPoint;
                }
                private static bool IsRingRoad(PathList road) {
                    return road.Hierarchy == 0 && Vector3.Distance(road.Path.Points[0], road.Path.Points[^1]) < 2f;
                }
                private class PathConnectedData {
                    public int PathPointIndex;
                    public int NewRoadIndex;
                    public int PointForConnectionIndex;
                }
            }
            private class RoadMonumentData {
                public string Name;
                public List<Vector3> LocalPathPoints;
                public Vector3 MonumentSize;
                public HashSet<MonumentInfo> Monuments;
            }
        }
        private class EventPath {
            public readonly List<PathPoint> Points = new List<PathPoint>();
            public readonly List<int> IncludedRoadIndexes = new List<int>();
            public bool IsRoundRoad;
            public PathPoint StartPathPoint;
            public Vector3 SpawnRotation;
            public EventPath(bool isRoundRoad) {
                IsRoundRoad = isRoundRoad;
            }
        }
        private class PathPoint {
            public Vector3 Position;
            public readonly List<PathPoint> ConnectedPoints = new List<PathPoint>();
            public bool Disabled;
            public PathPoint(Vector3 position) {
                this.Position = position;
            }
            public void ConnectPoint(PathPoint pathPoint) {
                ConnectedPoints.Add(pathPoint);
            }
        }
        private static class PositionDefiner {
            public static Vector3 GetGlobalPosition(Transform parentTransform, Vector3 position) {
                return parentTransform.transform.TransformPoint(position);
            }
            public static Quaternion GetGlobalRotation(Transform parentTransform, Vector3 rotation) {
                return parentTransform.rotation * Quaternion.Euler(rotation);
            }
            public static Vector3 GetLocalPosition(Transform parentTransform, Vector3 globalPosition) {
                return parentTransform.transform.InverseTransformPoint(globalPosition);
            }
            public static Vector3 GetGroundPositionInPoint(Vector3 position) {
                position.y = 100;
                if (Physics.Raycast(position, Vector3.down, out RaycastHit raycastHit, 500, 1 << 16 | 1 << 23)) position.y = raycastHit.point.y;
                return position;
            }
            public static bool GetNavmeshInPoint(Vector3 position, float radius, out NavMeshHit navMeshHit) {
                return NavMesh.SamplePosition(position, out navMeshHit, radius, 1);
            }
        }
        private static class BuildManager {
            public static BaseEntity SpawnRegularEntity(string prefabName, Vector3 position, Quaternion rotation, ulong skinId = 0, bool enableSaving = false) {
                BaseEntity entity = CreateEntity(prefabName, position, rotation, skinId, enableSaving);
                entity.Spawn();
                return entity;
            }
            private static BaseEntity CreateEntity(string prefabName, Vector3 position, Quaternion rotation, ulong skinId, bool enableSaving) {
                BaseEntity entity = GameManager.server.CreateEntity(prefabName, position, rotation);
                entity.enableSaving = enableSaving;
                entity.skinID = skinId;
                return entity;
            }
        }
        private class CollisionDisabler : FacepunchBehaviour {
            private readonly HashSet<Collider> _colliders = new HashSet<Collider>();
            public static void AttachCollisonDisabler(BaseEntity baseEntity) {
                CollisionDisabler collisionDisabler = baseEntity.gameObject.AddComponent<CollisionDisabler>();
                foreach (Collider collider in baseEntity.gameObject.GetComponentsInChildren<Collider>()) if (collider != null) collisionDisabler._colliders.Add(collider);
            }
            private void OnCollisionEnter(Collision collision) {
                if (collision == null || collision.collider == null) return;
                BaseEntity entty = collision.GetEntity();
                if (entty == null) {
                    if (collision.collider.name != "Terrain" && collision.collider.name != "Road Mesh") IgnoreCollider(collision.collider);
                }
                else if (entty is TreeEntity or ResourceEntity or LootContainer or JunkPile) entty.Kill(BaseNetworkable.DestroyMode.Gib);
            }
            private void IgnoreCollider(Collider otherCollider) {
                foreach (Collider collider in _colliders) if (collider != null) Physics.IgnoreCollision(collider, otherCollider);
            }
        }
#endregion Classes
#region Config
        private PluginConfig _config;
        protected override void LoadDefaultConfig() {
            _config = PluginConfig.DefaultConfig();
        }
        protected override void LoadConfig() {
            base.LoadConfig();
            _config = Config.ReadObject<PluginConfig>();
            Config.WriteObject(_config, true);
        }
        protected override void SaveConfig() {
            Config.WriteObject(_config);
        }
        private class PluginConfig {
            [JsonProperty("Version")]
            public VersionNumber Version {
                get;
                set;
            }
            [JsonProperty("To unload the plugin if there is a ring road")]
            public bool UnloadIfRoundRoad {
                get;
                set;
            }
            [JsonProperty("Blocked roads (/vendorroadblock)")]
            public HashSet<int> BlockRoads {
                get;
                set;
            }
            [JsonProperty("Enable automatic spawn [true/false]")]
            public bool IsAutoSpawn {
                get;
                set;
            }
            [JsonProperty("The time between vendor spawns [sec]")]
            public int SpawnTime {
                get;
                set;
            }
            [JsonProperty("Vendor's Lifetime [sec]")]
            public int LifeTime {
                get;
                set;
            }
            [JsonProperty("Time between vendor stops [sec.]")]
            public int TimeBetweenStops {
                get;
                set;
            }
            [JsonProperty("Vendor's Stop time [sec.]")]
            public int StopTime {
                get;
                set;
            }
            public static PluginConfig DefaultConfig() {
                return new PluginConfig {
                    Version = new VersionNumber(1, 0, 2), UnloadIfRoundRoad = true, BlockRoads = new HashSet<int>(), IsAutoSpawn = true, SpawnTime = 3600, LifeTime = 3600, TimeBetweenStops = 60, StopTime = 20,
                }
                ;
            }
        }
#endregion Config
    }
}
namespace Oxide.Plugins.AnyMapVendorExtensionMethods {
    public static class ExtensionMethods {
        public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
            using (var enumerator = source.GetEnumerator()) while (enumerator.MoveNext()) if (predicate(enumerator.Current)) return true;
            return false;
        }
        public static HashSet<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
            HashSet<TSource> result = new HashSet<TSource>();
            using (var enumerator = source.GetEnumerator()) while (enumerator.MoveNext()) if (predicate(enumerator.Current)) result.Add(enumerator.Current);
            return result;
        }
        public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
            using (var enumerator = source.GetEnumerator()) while (enumerator.MoveNext()) if (predicate(enumerator.Current)) return enumerator.Current;
            return default(TSource);
        }
        public static bool IsExists(this BaseNetworkable entity) => entity != null && !entity.IsDestroyed;
        public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source) {
            List<TSource> result = new List<TSource>();
            using (var enumerator = source.GetEnumerator()) while (enumerator.MoveNext()) result.Add(enumerator.Current);
            return result;
        }
        public static TSource Max<TSource>(this IEnumerable<TSource> source, Func<TSource, float> predicate) {
            TSource result = source.ElementAt(0);
            float resultValue = predicate(result);
            using (var enumerator = source.GetEnumerator()) {
                while (enumerator.MoveNext()) {
                    TSource element = enumerator.Current;
                    float elementValue = predicate(element);
                    if (elementValue > resultValue) {
                        result = element;
                        resultValue = elementValue;
                    }
                }
            }
            return result;
        }
        public static TSource Min<TSource>(this IEnumerable<TSource> source, Func<TSource, float> predicate) {
            TSource result = source.ElementAt(0);
            float resultValue = predicate(result);
            using (var enumerator = source.GetEnumerator()) {
                while (enumerator.MoveNext()) {
                    TSource element = enumerator.Current;
                    float elementValue = predicate(element);
                    if (elementValue < resultValue) {
                        result = element;
                        resultValue = elementValue;
                    }
                }
            }
            return result;
        }
        private static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index) {
            int movements = 0;
            using (var enumerator = source.GetEnumerator()) {
                while (enumerator.MoveNext()) {
                    if (movements == index) return enumerator.Current;
                    movements++;
                }
            }
            return default(TSource);
        }
        public static TSource First<TSource>(this IList<TSource> source) => source[0];
        public static TSource Last<TSource>(this IList<TSource> source) => source[^1];
        public static bool IsEqualVector3(this Vector3 a, Vector3 b) => Vector3.Distance(a, b) < 0.1f;
    }
}

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