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

FREE FBradleyRoute : 1.0.0 1.0.0

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

namespace Oxide.Plugins;

[Info("FBradleyRoute", "fufeldev", "1.0.0")]
public class FBradleyRoute : RustPlugin
{
    private const string BradleyPrefab = "assets/prefabs/npc/m2bradley/bradleyapc.prefab";
    private const string DataPath = "FBradleyRoute";
    private static readonly int Mask = LayerMask.GetMask("Default", "Terrain", "Construction", "Water", "Deployed", "Tree", "Debris", "World", "Player (Server)", "AI", "Ragdoll");
    private readonly Dictionary<ulong, RouteState> editRoutes = new Dictionary<ulong, RouteState>();
    private readonly Dictionary<ulong, RouteState> controlledBradleys = new Dictionary<ulong, RouteState>();
    private readonly List<ulong> removeControlled = new List<ulong>();
    private readonly HashSet<ulong> hiddenPlayers = new HashSet<ulong>();
    private Timer drawTimer;
    private Timer routeTimer;

    private class RouteState
    {
        public readonly List<Vector3> Points = new List<Vector3>();
        public bool Loop;
        public ulong OwnerId;
    }

    private class StoredData
    {
        public Dictionary<ulong, StoredRoute> Routes = new Dictionary<ulong, StoredRoute>();
    }

    private class StoredRoute
    {
        public List<StoredPoint> Points = new List<StoredPoint>();
        public bool Loop;
    }

    private class StoredPoint
    {
        public float X;
        public float Y;
        public float Z;
    }

    private void OnServerInitialized()
    {
        LoadData();
        drawTimer = timer.Every(2f, DrawAllRoutes);
        routeTimer = timer.Every(1f, TickControlledRoutes);
    }

    private void Unload()
    {
        SaveData();
        drawTimer?.Destroy();
        routeTimer?.Destroy();
        editRoutes.Clear();
        controlledBradleys.Clear();
        removeControlled.Clear();
        hiddenPlayers.Clear();
    }

    [ChatCommand("br")]
    private void CmdBr(BasePlayer player, string command, string[] args)
    {
        HandleCommand(player, args);
    }

    [ChatCommand("bradleyroute")]
    private void CmdBradleyRoute(BasePlayer player, string command, string[] args)
    {
        HandleCommand(player, args);
    }

    [ChatCommand("spawnbradley")]
    private void CmdSpawnBradley(BasePlayer player, string command, string[] args)
    {
        if (player == null || !player.IsAdmin) return;
        var result = SpawnRouteBradley();
        if (result == null) SendReply(player, "Не удалось создать Bradley. Нет сохранённого маршрута (минимум 2 точки) или танк уже жив.");
        else SendReply(player, $"Bradley создан на первой точке маршрута: {result}");
    }

    private object OnBradleyApcPatrol(BradleyAPC bradley)
    {
        return IsControlled(bradley) ? true : null;
    }

    private object OnBradleyApcHunt(BradleyAPC bradley)
    {
        return IsControlled(bradley) ? true : null;
    }

    private object CanBradleyApcTarget(BradleyAPC bradley, BaseEntity target)
    {
        return null;
    }

    private void OnEntityKill(BaseNetworkable entity)
    {
        BradleyAPC bradley = entity as BradleyAPC;
        if (bradley?.net == null) return;
        controlledBradleys.Remove(bradley.net.ID.Value);
    }

    private void HandleCommand(BasePlayer player, string[] args)
    {
        if (player == null) return;
        if (!player.IsAdmin)
        {
            SendReply(player, "Нет доступа.");
            return;
        }

        string action = args != null && args.Length > 0 ? args[0].ToLower() : "help";
        if (action == "start" || action == "new")
        {
            RouteState route = GetEditRoute(player);
            route.Points.Clear();
            route.Loop = HasArg(args, "loop");
            SaveData();
            SendReply(player, route.Loop ? "Новый маршрут: кольцо." : "Новый маршрут: от старта к финишу.");
            return;
        }

        if (action == "loop")
        {
            RouteState route = GetEditRoute(player);
            route.Loop = true;
            SaveData();
            SendReply(player, "Маршрут переключён в кольцо.");
            return;
        }

        if (action == "once")
        {
            RouteState route = GetEditRoute(player);
            route.Loop = false;
            SaveData();
            SendReply(player, "Маршрут переключён в режим старт-финиш.");
            return;
        }

        if (action == "add")
        {
            if (!TryGetPoint(player, out Vector3 point))
            {
                SendReply(player, "Не вижу точку для маршрута.");
                return;
            }

            RouteState route = GetEditRoute(player);
            route.Points.Add(point);
            SaveData();
            SendReply(player, $"Точка {route.Points.Count}: {Format(point)}");
            DrawRoute(player, route, 8f);
            return;
        }

        if (action == "undo")
        {
            RouteState route = GetEditRoute(player);
            if (route.Points.Count > 0) route.Points.RemoveAt(route.Points.Count - 1);
            SaveData();
            SendReply(player, $"Точек в маршруте: {route.Points.Count}.");
            DrawRoute(player, route, 8f);
            return;
        }

        if (action == "clear")
        {
            editRoutes.Remove(player.userID);
            SaveData();
            SendReply(player, "Редактируемый маршрут очищен.");
            return;
        }

        if (action == "show")
        {
            RouteState route = GetEditRoute(player);
            DrawRoute(player, route, 20f);
            SendReply(player, $"Показан маршрут. Точек: {route.Points.Count}.");
            return;
        }

        if (action == "hide")
        {
            hiddenPlayers.Add(player.userID);
            SendReply(player, "Разметка маршрутов скрыта. /br on чтобы включить обратно.");
            return;
        }

        if (action == "on" || action == "unhide")
        {
            hiddenPlayers.Remove(player.userID);
            SendReply(player, "Разметка маршрутов включена.");
            return;
        }

        if (action == "apply")
        {
            RouteState route = GetEditRoute(player);
            if (route.Points.Count < 2)
            {
                SendReply(player, "Нужно минимум 2 точки.");
                return;
            }

            BradleyAPC bradley = FindLookOrNearestBradley(player);
            if (bradley == null)
            {
                SendReply(player, "Bradley не найден рядом или по взгляду.");
                return;
            }

            ApplyRoute(bradley, route, player.userID);
            SendReply(player, $"Маршрут назначен Bradley {bradley.net.ID.Value}. После финиша поедет заново.");
            DrawRoute(player, route, 20f);
            return;
        }

        if (action == "spawn")
        {
            RouteState route = GetEditRoute(player);
            if (route.Points.Count < 2)
            {
                SendReply(player, "Нужно минимум 2 точки.");
                return;
            }

            BradleyAPC bradley = SpawnBradley(route);
            if (bradley == null)
            {
                SendReply(player, "Не удалось создать Bradley.");
                return;
            }

            ApplyRoute(bradley, route, player.userID);
            SendReply(player, $"Создан Bradley {bradley.net.ID.Value} и назначен маршрут. После финиша поедет заново.");
            DrawRoute(player, route, 20f);
            return;
        }

        if (action == "stop")
        {
            BradleyAPC bradley = FindLookOrNearestBradley(player);
            if (bradley == null)
            {
                SendReply(player, "Bradley не найден рядом или по взгляду.");
                return;
            }

            StopBradleyRoute(bradley);
            SendReply(player, $"Маршрут Bradley {bradley.net.ID.Value} остановлен.");
            return;
        }

        SendHelp(player);
    }

    private void LoadData()
    {
        editRoutes.Clear();
        StoredData data;
        try
        {
            data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(DataPath);
        }
        catch
        {
            data = new StoredData();
        }

        if (data?.Routes == null) return;
        foreach (KeyValuePair<ulong, StoredRoute> pair in data.Routes)
        {
            if (pair.Value == null || pair.Value.Points == null) continue;
            RouteState route = new RouteState { Loop = pair.Value.Loop, OwnerId = pair.Key };
            for (int i = 0; i < pair.Value.Points.Count; i++)
            {
                StoredPoint point = pair.Value.Points[i];
                if (point == null) continue;
                route.Points.Add(new Vector3(point.X, point.Y, point.Z));
            }
            editRoutes[pair.Key] = route;
        }
    }

    private void SaveData()
    {
        StoredData data = new StoredData();
        foreach (KeyValuePair<ulong, RouteState> pair in editRoutes)
        {
            if (pair.Value == null) continue;
            StoredRoute route = new StoredRoute { Loop = pair.Value.Loop };
            for (int i = 0; i < pair.Value.Points.Count; i++)
            {
                Vector3 point = pair.Value.Points[i];
                route.Points.Add(new StoredPoint { X = point.x, Y = point.y, Z = point.z });
            }
            data.Routes[pair.Key] = route;
        }

        Interface.Oxide.DataFileSystem.WriteObject(DataPath, data);
    }

    private RouteState GetEditRoute(BasePlayer player)
    {
        RouteState route;
        if (editRoutes.TryGetValue(player.userID, out route)) return route;
        route = new RouteState { OwnerId = player.userID };
        editRoutes[player.userID] = route;
        return route;
    }

    private bool HasArg(string[] args, string value)
    {
        if (args == null) return false;
        for (int i = 0; i < args.Length; i++)
        {
            if (args[i].ToLower() == value) return true;
        }
        return false;
    }

    private bool TryGetPoint(BasePlayer player, out Vector3 point)
    {
        if (Physics.Raycast(player.eyes.HeadRay(), out RaycastHit hit, 500f, Mask, QueryTriggerInteraction.Ignore))
        {
            point = hit.point;
            return true;
        }

        point = Vector3.zero;
        return false;
    }

    private BradleyAPC FindLookOrNearestBradley(BasePlayer player)
    {
        if (Physics.Raycast(player.eyes.HeadRay(), out RaycastHit hit, 200f, Mask, QueryTriggerInteraction.Ignore))
        {
            BradleyAPC looked = hit.collider != null ? hit.collider.GetComponentInParent<BradleyAPC>() : null;
            if (looked != null && looked.IsAlive()) return looked;
        }

        BradleyAPC nearest = null;
        float nearestDistance = 80f * 80f;
        foreach (BaseNetworkable networkable in BaseNetworkable.serverEntities)
        {
            BradleyAPC bradley = networkable as BradleyAPC;
            if (bradley == null || !bradley.IsAlive()) continue;
            float distance = (bradley.transform.position - player.transform.position).sqrMagnitude;
            if (distance >= nearestDistance) continue;
            nearestDistance = distance;
            nearest = bradley;
        }

        return nearest;
    }

    private BradleyAPC SpawnBradley(RouteState route)
    {
        Vector3 first = route.Points[0] + Vector3.up * 1f;
        Vector3 second = route.Points[1] - route.Points[0];
        second.y = 0f;
        Quaternion rotation = second.sqrMagnitude > 0.1f ? Quaternion.LookRotation(second.normalized, Vector3.up) : Quaternion.identity;
        BaseEntity entity = GameManager.server.CreateEntity(BradleyPrefab, first, rotation);
        if (entity == null) return null;

        BradleyAPC bradley = entity.GetComponent<BradleyAPC>();
        if (bradley == null)
        {
            entity.Kill();
            return null;
        }

        bradley.RoadSpawned = true;
        entity.Spawn();
        return bradley;
    }

    private void ApplyRoute(BradleyAPC bradley, RouteState route, ulong ownerId)
    {
        SetBradleyPath(bradley, route);

        RouteState active = new RouteState { Loop = route.Loop, OwnerId = ownerId };
        for (int i = 0; i < route.Points.Count; i++)
        {
            active.Points.Add(route.Points[i]);
        }
        controlledBradleys[bradley.net.ID.Value] = active;
    }

    private void SetBradleyPath(BradleyAPC bradley, RouteState route)
    {
        if (bradley.currentPath == null) bradley.currentPath = new List<Vector3>();
        bradley.currentPath.Clear();
        for (int i = 0; i < route.Points.Count; i++)
        {
            bradley.currentPath.Add(route.Points[i] + Vector3.up * 1f);
        }

        bradley.currentPathIndex = -1;
        bradley.pathLooping = route.Loop;
        bradley.finalDestination = bradley.currentPath[bradley.currentPath.Count - 1];
        bradley.SetDestination(bradley.currentPath[0]);
        bradley.DoAI = true;
    }

    private void StopBradleyRoute(BradleyAPC bradley)
    {
        if (bradley.currentPath != null) bradley.currentPath.Clear();
        bradley.currentPathIndex = -1;
        bradley.pathLooping = false;
        bradley.finalDestination = bradley.transform.position;
        bradley.SetDestination(bradley.transform.position);
        if (bradley.net != null) controlledBradleys.Remove(bradley.net.ID.Value);
    }

    private bool IsControlled(BradleyAPC bradley)
    {
        return bradley?.net != null && controlledBradleys.ContainsKey(bradley.net.ID.Value);
    }

    private void TickControlledRoutes()
    {
        removeControlled.Clear();
        foreach (KeyValuePair<ulong, RouteState> pair in controlledBradleys)
        {
            BradleyAPC bradley = BaseNetworkable.serverEntities.Find(new NetworkableId(pair.Key)) as BradleyAPC;
            if (bradley == null || !bradley.IsAlive() || pair.Value == null || pair.Value.Points.Count < 2)
            {
                removeControlled.Add(pair.Key);
                continue;
            }

            if (!bradley.HasPath() || bradley.PathComplete())
            {
                SetBradleyPath(bradley, pair.Value);
            }
        }

        for (int i = 0; i < removeControlled.Count; i++)
        {
            controlledBradleys.Remove(removeControlled[i]);
        }
    }

    private void DrawAllRoutes()
    {
        foreach (BasePlayer player in BasePlayer.activePlayerList)
        {
            if (player == null || !player.IsAdmin) continue;
            if (hiddenPlayers.Contains(player.userID)) continue;

            RouteState editRoute;
            if (editRoutes.TryGetValue(player.userID, out editRoute)) DrawRoute(player, editRoute, 2.2f);
        }
    }

    private void DrawRoute(BasePlayer player, RouteState route, float duration)
    {
        if (route == null || route.Points.Count == 0) return;

        for (int i = 0; i < route.Points.Count; i++)
        {
            Vector3 point = route.Points[i] + Vector3.up * 0.35f;
            Color color = i == 0 ? Color.green : i == route.Points.Count - 1 && !route.Loop ? Color.red : Color.yellow;
            UnityEngine.DDraw.Sphere(player, point, 1.2f, color, duration, false, false);
            UnityEngine.DDraw.Text(player, point + Vector3.up * 1.4f, (i + 1).ToString(), Color.white, duration, false, false, 1.2f);

            if (i + 1 < route.Points.Count)
            {
                UnityEngine.DDraw.Line(player, point, route.Points[i + 1] + Vector3.up * 0.35f, Color.cyan, duration, false, false);
            }
        }

        if (route.Loop && route.Points.Count > 2)
        {
            UnityEngine.DDraw.Line(player, route.Points[route.Points.Count - 1] + Vector3.up * 0.35f, route.Points[0] + Vector3.up * 0.35f, Color.cyan, duration, false, false);
        }
    }

    private string Format(Vector3 point)
    {
        return $"{point.x:0.0} {point.y:0.0} {point.z:0.0}";
    }

    private void SendHelp(BasePlayer player)
    {
        SendReply(player, "Команды FBradleyRoute:");
        SendReply(player, "/br start - очистить текущие точки и начать новый маршрут.");
        SendReply(player, "/br start loop - начать новый кольцевой маршрут.");
        SendReply(player, "/br add - добавить точку туда, куда смотришь.");
        SendReply(player, "/br undo - удалить последнюю добавленную точку.");
        SendReply(player, "/br show - показать сохранённые точки, сферы, номера и линии.");
        SendReply(player, "/br hide - скрыть постоянную разметку маршрутов.");
        SendReply(player, "/br on - включить постоянную разметку обратно.");
        SendReply(player, "/br apply - назначить маршрут ближайшему Bradley или Bradley по взгляду.");
        SendReply(player, "/br spawn - создать Bradley на первой точке, отправить по маршруту и повторять после финиша.");
        SendReply(player, "/br stop - остановить маршрут ближайшего Bradley или Bradley по взгляду.");
        SendReply(player, "/br clear - удалить твой сохранённый маршрут из data-файла.");
        SendReply(player, "/br loop - сделать текущий маршрут кольцевым.");
        SendReply(player, "/br once - сделать текущий маршрут обычным старт-финиш.");
        SendReply(player, "/bradleyroute - полный алиас команды /br.");
    }

    [HookMethod("SpawnRouteBradley")]
    public object SpawnRouteBradley()
    {
        RouteState route = null;
        foreach (var pair in editRoutes)
        {
            if (pair.Value != null && pair.Value.Points.Count >= 2)
            {
                route = pair.Value;
                break;
            }
        }

        if (route == null)
        {
            foreach (var pair in controlledBradleys)
            {
                if (pair.Value != null && pair.Value.Points.Count >= 2)
                {
                    route = pair.Value;
                    break;
                }
            }
        }

        if (route == null) return null;

        foreach (BaseNetworkable networkable in BaseNetworkable.serverEntities)
        {
            BradleyAPC existing = networkable as BradleyAPC;
            if (existing != null && existing.IsAlive()) return null;
        }

        BradleyAPC bradley = SpawnBradley(route);
        if (bradley == null) return null;

        ApplyRoute(bradley, route, 0);
        return bradley.net.ID.Value;
    }
}

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