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

FREE AutomatedWorkcarts : 0.35.0 0.35.0

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Rust;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
using static BaseEntity;
using static TrainCar;
using static TrainEngine;
using static TrainTrackSpline;
namespace Oxide.Plugins {
    [Info("Automated Workcarts", "WhiteThunder", "0.35.0")]
    [Description("Automates workcarts with NPC conductors.")]
    internal class AutomatedWorkcarts : CovalencePlugin {
#region Fields
        [PluginReference]
        private Plugin CargoTrainEvent;
        private const string PermissionToggle = "automatedworkcarts.toggle";
        private const string PermissionManageTriggers = "automatedworkcarts.managetriggers";
        private const string ShopkeeperPrefab = "assets/prefabs/npc/bandit/shopkeepers/bandit_shopkeeper.prefab";
        private const string GenericMapMarkerPrefab = "assets/prefabs/tools/map/genericradiusmarker.prefab";
        private const string VendingMapMarkerPrefab = "assets/prefabs/deployable/vendingmachine/vending_mapmarker.prefab";
        private const string ExplosionMapMakerPrefab = "assets/prefabs/tools/map/explosionmarker.prefab";
        private const string CrateMarkerPrefab = "assets/prefabs/tools/map/cratemarker.prefab";
        private const string BradleyExplosionEffectPrefab = "assets/prefabs/npc/m2bradley/effects/bradley_explosion.prefab";
        private static readonly FieldInfo TrainCouplingIsValidField = typeof(TrainCoupling).GetField("isValid", BindingFlags.NonPublic | BindingFlags.Instance) ?? typeof(TrainCoupling).GetField("isValid", BindingFlags.Public | BindingFlags.Instance);
        private readonly object False = false;
        private static readonly Regex IdRegex = new("\\$id", RegexOptions.IgnoreCase | RegexOptions.Compiled);
        private readonly BasePlayer[] _playerQueryResults = new BasePlayer[64];
        private Configuration _config;
        private StoredPluginData _data;
        private StoredTunnelData _tunnelData;
        private StoredMapData _mapData;
        private readonly SpawnedTrainCarTracker _spawnedTrainCarTracker = new();
        private readonly DisableSpawnPointManager _disableSpawnPointManager = new();
        private readonly TriggerManager _triggerManager;
        private readonly TrainManager _trainManager;
        private readonly RouteManager _routeManager;
        private readonly ColorMarkerUpdateManager _colorMarkerUpdateManager;
        private Coroutine _startupCoroutine;
        private Timer _showStatesTimer;
        public AutomatedWorkcarts() {
            _trainManager = new TrainManager(this, _spawnedTrainCarTracker);
            _triggerManager = new TriggerManager(this, _trainManager);
            _routeManager = new RouteManager(this);
            _colorMarkerUpdateManager = new ColorMarkerUpdateManager(this);
        }
#endregion
#region Hooks
        private void Init() {
            _config.Init();
            _data = StoredPluginData.Load();
            _tunnelData = StoredTunnelData.Load();
            permission.RegisterPermission(PermissionToggle, this);
            permission.RegisterPermission(PermissionManageTriggers, this);
            if (!_config.MapMarkers.AnyColorsEnabled) {
                Unsubscribe(nameof(OnPlayerConnected));
            }
        }
        private void OnServerInitialized() {
            if (!_config.UsingDefaults) {
                var addedPrefabs = _config.PopulateConfig();
                if (addedPrefabs.Count > 0) {
                    LogWarning($"Discovered and added {addedPrefabs.Count} train engine prefabs to Configuration.\n - {string.Join("\n - ", addedPrefabs)}");
                    SaveConfig();
                }
            }
            _config.OnServerInitialized();
            _mapData = StoredMapData.Load();
            _startupCoroutine = ServerMgr.Instance.StartCoroutine(new TrackedCoroutine(this).WithEnumerator(DoStartupRoutine()));
        }
        private void Unload() {
            if (_startupCoroutine != null) {
                ServerMgr.Instance.StopCoroutine(_startupCoroutine);
            }
            OnServerSave();
            _triggerManager.DestroyAll();
            _trainManager.Unload();
            _disableSpawnPointManager.Unload();
            _routeManager.Unload();
            _colorMarkerUpdateManager.Unload();
        }
        private void OnServerSave() {
            if (_trainManager.UpdateTrainEngineData()) {
                _data.Save();
            }
            else {
                _data.SaveIfDirty();
            }
        }
        private void OnNewSave() {
            _data = StoredPluginData.Clear();
        }
        private void OnPlayerConnected(BasePlayer player) {
            if (player.IsReceivingSnapshot) {
                timer.Once(1f, () => OnPlayerConnected(player));
                return;
            }
            _colorMarkerUpdateManager.Restart();
        }
        private object OnTrainCarUncouple(TrainCar trainCar, BasePlayer player) {
            // Disallow uncoupling train cars from automated trains.return _trainManager.HasTrainController(trainCar)
            ? False : null;
        }
#endregion
#region Commands
        [Command("aw.toggle")]
        private void CommandAutomateTrain(IPlayer player, string cmd, string[] args) {
            if (player.IsServer || !VerifyPermission(player, PermissionToggle)) return;
            var basePlayer = player.Object as BasePlayer;
            var trainCar = GetTrainCarWhereAiming(basePlayer);
            if (trainCar == null) {
                ReplyToPlayer(player, Lang.ErrorNoWorkcartFound);
                return;
            }
            var trainController = _trainManager.GetTrainController(trainCar);
            if (trainController == null) {
                var leadTrainEngine = GetLeadTrainEngine(trainCar);
                if (leadTrainEngine == null) {
                    ReplyToPlayer(player, Lang.ErrorNoWorkcart);
                    return;
                }
                if (IsTrainOwned(trainCar)) {
                    ReplyToPlayer(player, Lang.ErrorWorkcartOwned);
                    return;
                }
                if (!_trainManager.CanHaveMoreConductors()) {
                    ReplyToPlayer(player, Lang.ErrorMaxConductors, _trainManager.CountedConductors, _config.MaxConductors);
                    return;
                }
                TrainEngineData trainEngineData = null;
                if (args.Length > 0) {
                    var routeName = GetRouteNameFromArg(args[0], requirePrefix : false);
                    if (!string.IsNullOrWhiteSpace(routeName)) {
                        trainEngineData = new TrainEngineData {
                            Route = routeName
                        }
                        ;
                    }
                }
                if (_trainManager.TryCreateTrainController(leadTrainEngine, trainEngineData : trainEngineData)) {
                    var baseMessage = trainEngineData != null ? GetMessage(player, Lang.ToggleOnWithRouteSuccess, trainEngineData.Route) : GetMessage(player, Lang.ToggleOnSuccess);
                    player.Reply(baseMessage + " " + GetConductorCountMessage(player));
                    if (player.HasPermission(PermissionManageTriggers)) {
                        if (trainEngineData?.Route != null) {
                            _triggerManager.SetPlayerDisplayedRoute(basePlayer, trainEngineData.Route);
                        }
                        _triggerManager.ShowAllRepeatedly(basePlayer);
                    }
                }
                else {
                    ReplyToPlayer(player, Lang.ErrorAutomateBlocked);
                }
            }
            else {
                _trainManager.KillTrainController(trainCar);
                player.Reply(GetMessage(player, Lang.ToggleOffSuccess) + " " + GetConductorCountMessage(player));
            }
        }
        [Command("aw.resetall")]
        private void CommandResetTrains(IPlayer player, string cmd, string[] args) {
            if (!player.IsServer && !VerifyPermission(player, PermissionToggle)) return;
            var trainCount = _trainManager.ResetAll();
            ReplyToPlayer(player, Lang.ResetAllSuccess, trainCount);
        }
        [Command("aw.addtrigger", "awt.add")]
        private void CommandAddTrigger(IPlayer player, string cmd, string[] args) {
            if (player.IsServer || !VerifyPermission(player, PermissionManageTriggers)) return;
            if (!_config.EnableMapTriggers) {
                ReplyToPlayer(player, Lang.ErrorMapTriggersDisabled);
                return;
            }
            if (!VerifyAimingAtTrackPosition(player, out var trackPosition)) return;
            var triggerData = new TriggerData {
                Position = trackPosition
            }
            ;
            AddTriggerShared(player, cmd, args, triggerData);
        }
        [Command("aw.addtunneltrigger", "awt.addt")]
        private void CommandAddTunnelTrigger(IPlayer player, string cmd, string[] args) {
            if (player.IsServer || !VerifyPermission(player, PermissionManageTriggers)) return;
            if (!VerifyAimingAtTrackPosition(player, out var trackPosition) || !VerifySupportedNearbyTrainTunnel(player, trackPosition, out var dungeonCellWrapper)) return;
            if (!_config.IsTunnelTypeEnabled(dungeonCellWrapper.TunnelType)) {
                ReplyToPlayer(player, Lang.ErrorTunnelTypeDisabled, dungeonCellWrapper.TunnelType);
                return;
            }
            var triggerData = new TriggerData {
                TunnelType = dungeonCellWrapper.TunnelType.ToString(), Position = dungeonCellWrapper.InverseTransformPoint(trackPosition),
            }
            ;
            AddTriggerShared(player, cmd, args, triggerData, dungeonCellWrapper);
        }
        private void AddTriggerShared(IPlayer player, string cmd, string[] args, TriggerData triggerData, DungeonCellWrapper dungeonCellWrapper = null) {
            foreach (var arg in args) {
                if (!VerifyValidArgAndModifyTrigger(player, cmd, arg, triggerData, Lang.AddTriggerSyntax)) return;
            }
            if (!triggerData.IsSpawner && !triggerData.AddConductor && !triggerData.Destroy && triggerData.GetTrackSelectionInstruction() == null && triggerData.GetSpeedInstruction() == null && triggerData.GetDirectionInstruction() == null) {
                triggerData.Speed = EngineSpeeds.Zero.ToString();
            }
            var basePlayer = player.Object as BasePlayer;
            if (triggerData.IsSpawner) {
                var rotation = Quaternion.Euler(basePlayer.viewAngles);
                if (dungeonCellWrapper != null) {
                    rotation *= Quaternion.Inverse(dungeonCellWrapper.Rotation);
                }
                triggerData.RotationAngle = rotation.eulerAngles.y % 360;
            }
            _triggerManager.AddTrigger(triggerData);
            if (triggerData.Route != null) {
                _triggerManager.SetPlayerDisplayedRoute(basePlayer, triggerData.Route);
            }
            _triggerManager.ShowAllRepeatedly(basePlayer);
            ReplyToPlayer(player, Lang.AddTriggerSuccess, GetTriggerPrefix(player, triggerData), triggerData.Id);
        }
        [Command("aw.updatetrigger", "awt.update")]
        private void CommandUpdateTrigger(IPlayer player, string cmd, string[] args) {
            if (!VerifyCanModifyTrigger(player, cmd, args, Lang.UpdateTriggerSyntax, out var triggerData, out var optionArgs)) return;
            if (optionArgs.Length == 0) {
                ReplyToPlayer(player, Lang.UpdateTriggerSyntax, cmd, GetTriggerOptions(player));
                return;
            }
            var newTriggerData = triggerData.Clone();
            foreach (var arg in optionArgs) {
                if (!VerifyValidArgAndModifyTrigger(player, cmd, arg, newTriggerData, Lang.UpdateTriggerSyntax)) return;
            }
            _triggerManager.UpdateTrigger(triggerData, newTriggerData);
            var basePlayer = player.Object as BasePlayer;
            if (triggerData.Route != null) {
                _triggerManager.SetPlayerDisplayedRoute(basePlayer, triggerData.Route);
            }
            _triggerManager.ShowAllRepeatedly(basePlayer);
            ReplyToPlayer(player, Lang.UpdateTriggerSuccess, GetTriggerPrefix(player, triggerData), triggerData.Id);
        }
        [Command("aw.replacetrigger", "awt.replace")]
        private void CommandReplaceTrigger(IPlayer player, string cmd, string[] args) {
            if (!VerifyCanModifyTrigger(player, cmd, args, Lang.UpdateTriggerSyntax, out var triggerData, out var optionArgs)) return;
            if (optionArgs.Length == 0) {
                ReplyToPlayer(player, Lang.UpdateTriggerSyntax, cmd, GetTriggerOptions(player));
                return;
            }
            var newTriggerData = new TriggerData();
            foreach (var arg in optionArgs) {
                if (!VerifyValidArgAndModifyTrigger(player, cmd, arg, newTriggerData, Lang.UpdateTriggerSyntax)) return;
            }
            _triggerManager.UpdateTrigger(triggerData, newTriggerData);
            var basePlayer = player.Object as BasePlayer;
            if (triggerData.Route != null) {
                _triggerManager.SetPlayerDisplayedRoute(basePlayer, triggerData.Route);
            }
            _triggerManager.ShowAllRepeatedly(basePlayer);
            ReplyToPlayer(player, Lang.UpdateTriggerSuccess, GetTriggerPrefix(player, triggerData), triggerData.Id);
        }
        [Command("aw.enabletrigger", "awt.enable")]
        private void CommandEnableTrigger(IPlayer player, string cmd, string[] args) {
            if (!VerifyCanModifyTrigger(player, cmd, args, Lang.SimpleTriggerSyntax, out var triggerData, out _)) return;
            var newTriggerData = triggerData.Clone();
            newTriggerData.Enabled = true;
            _triggerManager.UpdateTrigger(triggerData, newTriggerData);
            var basePlayer = player.Object as BasePlayer;
            _triggerManager.ShowAllRepeatedly(basePlayer);
            ReplyToPlayer(player, Lang.UpdateTriggerSuccess, GetTriggerPrefix(player, triggerData), triggerData.Id);
        }
        [Command("aw.disabletrigger", "awt.disable")]
        private void CommandDisableTrigger(IPlayer player, string cmd, string[] args) {
            if (!VerifyCanModifyTrigger(player, cmd, args, Lang.SimpleTriggerSyntax, out var triggerData, out _)) return;
            var newTriggerData = triggerData.Clone();
            newTriggerData.Enabled = false;
            _triggerManager.UpdateTrigger(triggerData, newTriggerData);
            var basePlayer = player.Object as BasePlayer;
            _triggerManager.ShowAllRepeatedly(basePlayer);
            ReplyToPlayer(player, Lang.UpdateTriggerSuccess, GetTriggerPrefix(player, triggerData), triggerData.Id);
        }
        [Command("aw.movetrigger", "awt.move")]
        private void CommandMoveTrigger(IPlayer player, string cmd, string[] args) {
            if (!VerifyCanModifyTrigger(player, cmd, args, Lang.SimpleTriggerSyntax, out var triggerData, out _)) return;
            if (!VerifyAimingAtTrackPosition(player, out var trackPosition)) return;
            if (triggerData.TriggerType == TrainTriggerType.Tunnel) {
                if (!VerifySupportedNearbyTrainTunnel(player, trackPosition, out var dungeonCellWrapper)) return;
                if (dungeonCellWrapper.TunnelType != triggerData.GetTunnelType()) {
                    ReplyToPlayer(player, Lang.ErrorUnsupportedTunnel);
                    return;
                }
                trackPosition = dungeonCellWrapper.InverseTransformPoint(trackPosition);
            }
            _triggerManager.MoveTrigger(triggerData, trackPosition);
            var basePlayer = player.Object as BasePlayer;
            if (triggerData.Route != null) {
                _triggerManager.SetPlayerDisplayedRoute(basePlayer, triggerData.Route);
            }
            _triggerManager.ShowAllRepeatedly(basePlayer);
            ReplyToPlayer(player, Lang.MoveTriggerSuccess, GetTriggerPrefix(player, triggerData), triggerData.Id);
        }
        [Command("aw.removetrigger", "awt.remove")]
        private void CommandRemoveTrigger(IPlayer player, string cmd, string[] args) {
            if (!VerifyCanModifyTrigger(player, cmd, args, Lang.SimpleTriggerSyntax, out var triggerData, out _)) return;
            _triggerManager.RemoveTrigger(triggerData);
            var basePlayer = player.Object as BasePlayer;
            _triggerManager.ShowAllRepeatedly(basePlayer);
            ReplyToPlayer(player, Lang.RemoveTriggerSuccess, GetTriggerPrefix(player, triggerData), triggerData.Id);
        }
        [Command("aw.rotatetrigger", "awt.rotate")]
        private void CommandSetTriggerRotation(IPlayer player, string cmd, string[] args) {
            if (!VerifyCanModifyTrigger(player, cmd, args, Lang.SimpleTriggerSyntax, out var triggerData, out _)) return;
            var basePlayer = player.Object as BasePlayer;
            var playerPosition = basePlayer.transform.position;
            var triggerInstance = _triggerManager.FindNearestTrigger(playerPosition, triggerData);
            var rotation = Quaternion.Euler(basePlayer.viewAngles);
            var needsRespawn = false;
            if (triggerInstance.Spline != null) {
                rotation = GetSplineTangentRotation(triggerInstance.Spline, triggerInstance.DistanceOnSpline, rotation);
                if (Vector3.Dot(triggerInstance.SpawnRotation * Vector3.forward, rotation * Vector3.forward) < 0) {
                    needsRespawn = true;
                }
            }
            if (triggerInstance is TunnelTriggerInstance tunnelTriggerInstance) {
                rotation *= Quaternion.Inverse(tunnelTriggerInstance.DungeonCellWrapper.Rotation);
            }
            _triggerManager.RotateTrigger(triggerData, rotation.eulerAngles.y % 360);
            if (needsRespawn) {
                _triggerManager.RespawnTrigger(triggerData);
            }
            _triggerManager.ShowAllRepeatedly(basePlayer);
            ReplyToPlayer(player, Lang.RotateTriggerSuccess, GetTriggerPrefix(player, triggerData), triggerData.Id);
        }
        [Command("aw.respawntrigger", "awt.respawn")]
        private void CommandRespawnTrigger(IPlayer player, string cmd, string[] args) {
            if (!VerifyCanModifyTrigger(player, cmd, args, Lang.SimpleTriggerSyntax, out var triggerData, out _)) return;
            if (!triggerData.IsSpawner) {
                ReplyToPlayer(player, Lang.ErrorRequiresSpawnTrigger);
                return;
            }
            if (!triggerData.Enabled) {
                ReplyToPlayer(player, Lang.ErrorTriggerDisabled);
                return;
            }
            _triggerManager.RespawnTrigger(triggerData);
            var basePlayer = player.Object as BasePlayer;
            _triggerManager.ShowAllRepeatedly(basePlayer);
        }
        [Command("aw.addtriggercommand", "awt.addcommand", "awt.addcmd")]
        private void CommandAddCommand(IPlayer player, string cmd, string[] args) {
            if (!VerifyCanModifyTrigger(player, cmd, args, Lang.AddCommandSyntax, out var triggerData, out var optionArgs)) return;
            if (optionArgs.Length < 1) {
                ReplyToPlayer(player, Lang.AddCommandSyntax, cmd);
                return;
            }
            var quotedCommands = optionArgs.Select(command => command.Contains(" ") ? $"\"{command}\"" : command).ToArray();
            _triggerManager.AddTriggerCommand(triggerData, string.Join(" ", quotedCommands));
            var basePlayer = player.Object as BasePlayer;
            _triggerManager.ShowAllRepeatedly(basePlayer);
            ReplyToPlayer(player, Lang.UpdateTriggerSuccess, GetTriggerPrefix(player, triggerData), triggerData.Id);
        }
        [Command("aw.removetriggercommand", "awt.removecommand", "awt.removecmd")]
        private void CommandRemoveCommand(IPlayer player, string cmd, string[] args) {
            if (!VerifyCanModifyTrigger(player, cmd, args, Lang.RemoveCommandSyntax, out var triggerData, out var optionArgs)) return;
            if (optionArgs.Length < 1 || !int.TryParse(optionArgs[0], out var commandIndex)) {
                ReplyToPlayer(player, Lang.RemoveCommandSyntax, cmd);
                return;
            }
            if (commandIndex < 1 || commandIndex > triggerData.Commands.Count) {
                ReplyToPlayer(player, Lang.RemoveCommandErrorIndex, commandIndex);
                return;
            }
            _triggerManager.RemoveTriggerCommand(triggerData, commandIndex - 1);
            var basePlayer = player.Object as BasePlayer;
            _triggerManager.ShowAllRepeatedly(basePlayer);
            ReplyToPlayer(player, Lang.UpdateTriggerSuccess, GetTriggerPrefix(player, triggerData), triggerData.Id);
        }
        [Command("aw.settriggertrain", "awt.train")]
        private void CommandTriggerTrain(IPlayer player, string cmd, string[] args) {
            if (!VerifyCanModifyTrigger(player, cmd, args, Lang.RemoveCommandSyntax, out var triggerData, out var optionArgs)) return;
            var trainCarAliases = new List<string>();
            foreach (var arg in optionArgs) {
                var trainCarPrefab = TrainCarPrefab.FindPrefab(arg);
                if (trainCarPrefab == null) {
                    ReplyToPlayer(player, Lang.ErrorUnrecognizedTrainCar, arg);
                    return;
                }
                trainCarAliases.Add(trainCarPrefab.TrainCarAlias);
            }
            var newTriggerData = triggerData.Clone();
            newTriggerData.TrainCars = trainCarAliases.ToArray();
            _triggerManager.UpdateTrigger(triggerData, newTriggerData);
            var basePlayer = player.Object as BasePlayer;
            _triggerManager.ShowAllRepeatedly(basePlayer);
            ReplyToPlayer(player, Lang.UpdateTriggerSuccess, GetTriggerPrefix(player, triggerData), triggerData.Id);
        }
        [Command("aw.showtriggers", "awt.show")]
        private void CommandShowTriggers(IPlayer player, string cmd, string[] args) {
            if (player.IsServer || !VerifyPermission(player, PermissionManageTriggers) || !VerifyAnyTriggers(player)) return;
            var duration = 60;
            string routeName = null;
            foreach (var arg in args) {
                if (duration == 60) {
                    if (int.TryParse(arg, out var argIntValue)) {
                        duration = argIntValue;
                        continue;
                    }
                }
                if (routeName == null) {
                    var routeNameArg = GetRouteNameFromArg(arg, requirePrefix : false);
                    if (!string.IsNullOrWhiteSpace(routeNameArg)) routeName = routeNameArg;
                }
            }
            var basePlayer = player.Object as BasePlayer;
            _triggerManager.SetPlayerDisplayedRoute(basePlayer, routeName);
            _triggerManager.ShowAllRepeatedly(basePlayer, duration);
            if (routeName != null) {
                ReplyToPlayer(player, Lang.ShowTriggersWithRouteSuccess, routeName, FormatTime(duration));
            }
            else {
                ReplyToPlayer(player, Lang.ShowTriggersSuccess, FormatTime(duration));
            }
        }
        // Debugging command, usable by one player at a time.[Command("aw.showstates")]
        private void CommandShowStates(IPlayer player, string cmd, string[] args) {
            if (player.IsServer || !VerifyPermission(player, PermissionManageTriggers) || !VerifyAnyTriggers(player)) return;
            if (args.Length < 1 || !int.TryParse(args[0], out var duration)) {
                duration = 60;
            }
            var drawDuration = 0.5f;
            var maxDistanceSquared = Mathf.Pow(_config.TriggerDisplayDistance, 2);
            var basePlayer = player.Object as BasePlayer;
            _showStatesTimer?.Destroy();
            _showStatesTimer = timer.Repeat(drawDuration, Mathf.RoundToInt(duration / drawDuration), () => {
                if (basePlayer == null || basePlayer.IsDestroyed || !basePlayer.IsConnected) {
                    _showStatesTimer.Destroy();
                    _showStatesTimer = null;
                    return;
                }
                _trainManager.ShowNearbyTrainStates(basePlayer, maxDistanceSquared, drawDuration);
            }
            );
        }
#endregion
#region API
        [HookMethod(nameof(API_AutomateWorkcart))]
        public bool API_AutomateWorkcart(TrainEngine trainEngine) {
            return _trainManager.HasTrainController(trainEngine) || _trainManager.TryCreateTrainController(trainEngine);
        }
        [HookMethod(nameof(API_StopAutomatingWorkcart))]
        public void API_StopAutomatingWorkcart(TrainEngine trainEngine) {
            _trainManager.KillTrainController(trainEngine);
        }
        [HookMethod(nameof(API_IsWorkcartAutomated))]
        public bool API_IsWorkcartAutomated(TrainEngine trainEngine) {
            return _trainManager.HasTrainController(trainEngine);
        }
        [HookMethod(nameof(API_GetAutomatedWorkcarts))]
        public TrainEngine[] API_GetAutomatedWorkcarts() {
            return _trainManager.GetAutomatedTrainEngines();
        }
#endregion
#region Exposed Hooks
        private static class ExposedHooks {
            public static object OnWorkcartAutomationStart(TrainEngine trainEngine) {
                return Interface.CallHook("OnWorkcartAutomationStart", trainEngine);
            }
            public static void OnWorkcartAutomationStarted(TrainEngine trainEngine) {
                Interface.CallHook("OnWorkcartAutomationStarted", trainEngine);
            }
            public static void OnWorkcartAutomationStopped(TrainEngine trainEngine) {
                Interface.CallHook("OnWorkcartAutomationStopped", trainEngine);
            }
        }
#endregion
#region Dependencies
        private bool IsCargoTrain(TrainEngine trainEngine) {
            return CargoTrainEvent?.Call("IsTrainSpecial", trainEngine.net.ID) is true;
        }
#endregion
#region Helper Methods - Static
        private static List<T> FindPrefabsOfType<T>() where T : BaseEntity {
            var prefabList = new List<T>();
            foreach (var assetPath in GameManifest.Current.entities) {
                var entity = GameManager.server.FindPrefab(assetPath)?.GetComponent<T>();
                if (entity == null) continue;
                prefabList.Add(entity);
            }
            return prefabList;
        }
#endregion
#region Helper Methods - Command Checks
        private bool VerifyPermission(IPlayer player, string permissionName) {
            if (player.HasPermission(permissionName)) return true;
            ReplyToPlayer(player, Lang.ErrorNoPermission);
            return false;
        }
        private bool VerifyAnyTriggers(IPlayer player) {
            if (_mapData.MapTriggers.Count > 0 || _tunnelData.TunnelTriggers.Count > 0) return true;
            ReplyToPlayer(player, Lang.ErrorNoTriggers);
            return false;
        }
        private bool VerifyTriggerExists(IPlayer player, int triggerId, TrainTriggerType triggerType, out TriggerData triggerData) {
            triggerData = _triggerManager.FindTrigger(triggerId, triggerType);
            if (triggerData != null) return true;
            _triggerManager.ShowAllRepeatedly(player.Object as BasePlayer);
            ReplyToPlayer(player, Lang.ErrorTriggerNotFound, GetTriggerPrefix(player, triggerType), triggerId);
            return false;
        }
        private bool VerifyAimingAtTrackPosition(IPlayer player, out Vector3 trackPosition) {
            if (TryGetTrackPosition(player.Object as BasePlayer, out trackPosition)) return true;
            ReplyToPlayer(player, Lang.ErrorNoTrackFound);
            return false;
        }
        private bool IsTriggerArg(IPlayer player, string arg, out int triggerId, out TrainTriggerType triggerType) {
            triggerType = TrainTriggerType.Map;
            triggerId = 0;
            if (arg.StartsWith("#")) {
                arg = arg.Substring(1);
            }
            if (arg.Length <= 1) return false;
            var triggerPrefix = arg.Substring(0, 1).ToLower();
            var triggerIdString = arg.Substring(1).ToLower();
            if (!int.TryParse(triggerIdString, out triggerId)) return false;
            if (triggerPrefix == GetTriggerPrefix(player, TrainTriggerType.Tunnel).ToLower()) {
                triggerType = TrainTriggerType.Tunnel;
                return true;
            }
            if (triggerPrefix == GetTriggerPrefix(player, TrainTriggerType.Map).ToLower()) {
                triggerType = TrainTriggerType.Map;
                return true;
            }
            return false;
        }
        private bool VerifyTriggerWhereAiming(IPlayer player, string cmd, string[] args, string errorMessageName, out TriggerData triggerData, out string[] optionArgs) {
            var basePlayer = player.Object as BasePlayer;
            optionArgs = args;
            triggerData = null;
            triggerData = _triggerManager.FindNearestTriggerWhereAiming(basePlayer)?.TriggerData;
            if (triggerData != null) return true;
            _triggerManager.ShowAllRepeatedly(basePlayer);
            ReplyToPlayer(player, errorMessageName, cmd, GetTriggerOptions(player));
            return false;
        }
        private bool VerifyCanModifyTrigger(IPlayer player, string cmd, string[] args, string errorMessageName, out TriggerData triggerData, out string[] optionArgs) {
            triggerData = null;
            optionArgs = null;
            if (!VerifyPermission(player, PermissionManageTriggers) || !VerifyAnyTriggers(player)) return false;
            if (args.Length > 0 && IsTriggerArg(player, args[0], out var triggerId, out var triggerType)) {
                optionArgs = args.Skip(1).ToArray();
                return VerifyTriggerExists(player, triggerId, triggerType, out triggerData);
            }
            if (player.IsServer) {
                // Server commands must specify a trigger id.ReplyToPlayer(player, errorMessageName, cmd, GetTriggerOptions(player));
                return false;
            }
            return VerifyTriggerWhereAiming(player, cmd, args, errorMessageName, out triggerData, out optionArgs);
        }
        private bool VerifySupportedNearbyTrainTunnel(IPlayer player, Vector3 trackPosition, out DungeonCellWrapper dungeonCellWrapper) {
            dungeonCellWrapper = FindNearestDungeonCell(trackPosition);
            if (dungeonCellWrapper == null || dungeonCellWrapper.TunnelType == TunnelType.Unsupported) {
                ReplyToPlayer(player, Lang.ErrorUnsupportedTunnel);
                return false;
            }
            return true;
        }
        private bool VerifyValidArgAndModifyTrigger(IPlayer player, string cmd, string arg, TriggerData triggerData, string errorMessageName) {
            var argLower = arg.ToLower();
            if (argLower is "start" or "conductor") {
                triggerData.AddConductor = true;
                return true;
            }
            if (argLower.StartsWith("brake")) {
                triggerData.Brake = true;
                return true;
            }
            if (argLower.StartsWith("destroy")) {
                triggerData.Destroy = true;
                return true;
            }
            if (argLower.StartsWith("enable")) {
                triggerData.Enabled = true;
                return true;
            }
            if (argLower.StartsWith("disable")) {
                triggerData.Enabled = false;
                return true;
            }
            var prefab = TrainCarPrefab.FindPrefab(argLower);
            if (prefab != null) {
                if (triggerData.TrainCars == null) {
                    triggerData.TrainCars = new[] {
                        prefab.TrainCarAlias
                    }
                    ;
                }
                else {
                    var length = triggerData.TrainCars.Length;
                    Array.Resize(ref triggerData.TrainCars, length + 1);
                    triggerData.TrainCars[length] = prefab.TrainCarAlias;
                }
                return true;
            }
            if (arg[^1] == '%' && float.TryParse(arg[..^1], out var percentage)) {
                triggerData.Chance = Mathf.Clamp(percentage / 100f, 0, 1);
                return true;
            }
            if (float.TryParse(arg, out var stopDuration)) {
                triggerData.StopDuration = stopDuration;
                return true;
            }
            var routeName = GetRouteNameFromArg(arg, requirePrefix : true);
            if (!string.IsNullOrWhiteSpace(routeName)) {
                triggerData.Route = routeName;
                return true;
            }
            if (Enum.TryParse(arg, true, out SpeedInstruction speedInstruction)) {
                var speedString = speedInstruction.ToString();
                // If zero speed is already set, assume this is the departure speed.if (triggerData.Speed == SpeedInstruction.Zero.ToString())
                {
                    triggerData.DepartureSpeed = speedString;
                }
                else {
                    triggerData.Speed = speedString;
                }
                return true;
            }
            if (Enum.TryParse(arg, true, out DirectionInstruction directionInstruction)) {
                triggerData.Direction = directionInstruction.ToString();
                return true;
            }
            if (Enum.TryParse(arg, true, out TrackSelectionInstruction trackSelectionInstruction)) {
                triggerData.TrackSelection = trackSelectionInstruction.ToString();
                return true;
            }
            ReplyToPlayer(player, errorMessageName, cmd, GetTriggerOptions(player));
            return false;
        }
#endregion
#region Helper Methods - Coupling
        private static void UpdateAllowedCouplings(TrainCar trainCar, bool allowFront, bool allowRear) {
            var coupling = trainCar.coupling;
            var frontCoupling = coupling.frontCoupling;
            var rearCoupling = coupling.rearCoupling;
            if (trainCar.frontCoupling == null || trainCar.rearCoupling == null) {
                // Some train cars do not allow coupling, such as the classic workcart.return;
            }
            if (!allowFront && frontCoupling.IsCoupled) {
                frontCoupling.Uncouple(reflect : true);
            }
            if (!allowRear && rearCoupling.IsCoupled) {
                rearCoupling.Uncouple(reflect : true);
            }
            if (frontCoupling.isValid != allowFront) {
                TrainCouplingIsValidField.SetValue(frontCoupling, allowFront);
            }
            if (rearCoupling.isValid != allowRear) {
                TrainCouplingIsValidField.SetValue(rearCoupling, allowRear);
            }
        }
        private static void DisableTrainCoupling(CompleteTrain completeTrain) {
            var firstTrainCar = completeTrain.trainCars.FirstOrDefault();
            var lastTrainCar = completeTrain.trainCars.LastOrDefault();
            if (firstTrainCar == null || lastTrainCar == null) return;
            UpdateAllowedCouplings(firstTrainCar, firstTrainCar.coupling.IsFrontCoupled, firstTrainCar.coupling.IsRearCoupled);
            if (lastTrainCar != firstTrainCar) {
                UpdateAllowedCouplings(lastTrainCar, lastTrainCar.coupling.IsFrontCoupled, lastTrainCar.coupling.IsRearCoupled);
            }
        }
        private static void EnableTrainCoupling(CompleteTrain completeTrain) {
            var firstTrainCar = completeTrain.trainCars.FirstOrDefault();
            var lastTrainCar = completeTrain.trainCars.LastOrDefault();
            if (firstTrainCar == null || lastTrainCar == null) return;
            UpdateAllowedCouplings(firstTrainCar, allowFront : true, allowRear : true);
            if (lastTrainCar != firstTrainCar) {
                UpdateAllowedCouplings(lastTrainCar, allowFront : true, allowRear : true);
            }
        }
#endregion
#region Helper Methods
        private static void LogError(string message) => Interface.Oxide.LogError($"[Automated Workcarts] {message}");
        private static void LogWarning(string message) => Interface.Oxide.LogWarning($"[Automated Workcarts] {message}");
        private static int GetNextTriggerId(List<TriggerData> triggerList) {
            var highestTriggerId = 0;
            foreach (var triggerData in triggerList) {
                highestTriggerId = Math.Max(highestTriggerId, triggerData.Id);
            }
            return highestTriggerId + 1;
        }
        private static string GetRouteNameFromArg(string routeName, bool requirePrefix = true) {
            if (routeName.StartsWith("@")) return routeName[1..];
            return requirePrefix ? null : routeName;
        }
        private static float GetThrottleFraction(EngineSpeeds throttle) {
            switch (throttle) {
                case EngineSpeeds.Rev_Hi : return -1;
                case EngineSpeeds.Rev_Med : return -0.5f;
                case EngineSpeeds.Rev_Lo : return -0.2f;
                case EngineSpeeds.Fwd_Lo : return 0.2f;
                case EngineSpeeds.Fwd_Med : return 0.5f;
                case EngineSpeeds.Fwd_Hi : return 1;
                default : return 0;
            }
        }
        private static TrainEngine GetLeadTrainEngine(CompleteTrain completeTrain) {
            if (completeTrain.PrimaryTrainCar == completeTrain.trainCars[0]) {
                for (var i = 0; i < completeTrain.trainCars.Count; i++) {
                    var trainEngine = completeTrain.trainCars[i] as TrainEngine;
                    if ((object)trainEngine != null) return trainEngine;
                }
            }
            else {
                for (var i = completeTrain.trainCars.Count - 1; i >= 0; i--) {
                    var trainEngine = completeTrain.trainCars[i] as TrainEngine;
                    if ((object)trainEngine != null) return trainEngine;
                }
            }
            return null;
        }
        private static TrainEngine GetLeadTrainEngine(TrainCar trainCar) {
            var trainEngine = trainCar as TrainEngine;
            if (trainEngine != null) return trainEngine;
            return GetLeadTrainEngine(trainCar.completeTrain);
        }
        private static void DetermineTrainCarOrientations(TrainCar trainCar, Vector3 forward, TrainCar otherTrainCar, out TrainCar forwardTrainCar) {
            var position = trainCar.transform.position;
            var otherPosition = otherTrainCar.transform.position;
            var forwardPosition = position + forward * 100f;
            forwardTrainCar = trainCar;
            if ((forwardPosition - position).sqrMagnitude > (forwardPosition - otherPosition).sqrMagnitude) {
                forwardTrainCar = otherTrainCar;
            }
        }
        private static Vector3 GetTrainCarForward(TrainCar trainCar) {
            return trainCar.GetTrackSpeed() >= 0 ? trainCar.transform.forward : -trainCar.transform.forward;
        }
        private static void EnableInvincibility(TrainCar trainCar) {
            trainCar.initialSpawnTime = float.MaxValue;
        }
        private static void DisableInvincibility(TrainCar trainCar) {
            trainCar.initialSpawnTime = Time.time;
        }
        private static void EnableSavingRecursive(BaseEntity entity, bool enableSaving) {
            entity.EnableSaving(enableSaving);
            foreach (var child in entity.children) {
                if (child is BasePlayer) continue;
                EnableSavingRecursive(child, enableSaving);
            }
        }
        private static TrainCar SpawnTrainCar(string prefabName, Vector3 position, Quaternion rotation) {
            var trainCar = GameManager.server.CreateEntity(prefabName, position, rotation) as TrainCar;
            if (trainCar == null) return null;
            // Ensure the train car does not decay for some time.trainCar.lastDecayTick = Time.realtimeSinceStartup;
            trainCar.limitNetworking = true;
            trainCar.EnableSaving(false);
            trainCar.Spawn();
            if (trainCar.IsDestroyed) return null;
            // Don't let the train kill itself if there isn't quite enough space.trainCar.CancelInvoke(trainCar.KillMessage);
            trainCar.Invoke(() => EnableSavingRecursive(trainCar, false), 0);
            return trainCar;
        }
        private static float GetSplineDistance(TrainTrackSpline spline, Vector3 position) {
            spline.GetDistance(position, 1, out var distanceOnSpline);
            return distanceOnSpline;
        }
        private static TrainCar AddTrainCar(TrainCar frontTrainCar, TrainCarPrefab frontTrainCarPrefab, TrainCarPrefab trainCarPrefab, TrackSelection trackSelection) {
            var rearCouplingTransform = frontTrainCarPrefab.Reverse ? frontTrainCar.frontCoupling : frontTrainCar.rearCoupling;
            if (rearCouplingTransform == null) return null;
            var rearWheelPos = frontTrainCarPrefab.Reverse ? frontTrainCar.GetFrontWheelPos() : frontTrainCar.GetRearWheelPos();
            var wheelToRearCouplingDistance = Math.Abs(rearWheelPos.z - rearCouplingTransform.position.z);
            var rearSpline = frontTrainCarPrefab.Reverse ? frontTrainCar.FrontTrackSection : frontTrainCar.RearTrackSection;
            var rearWheelDistanceOnSpline = GetSplineDistance(rearSpline, rearWheelPos);
            var frontTrainCarForward = frontTrainCarPrefab.Reverse ? -frontTrainCar.transform.forward : frontTrainCar.transform.forward;
            var askerIsForward = rearSpline.IsForward(frontTrainCarForward, rearWheelDistanceOnSpline);
            var splineInfo = new SplineInfo {
                Spline = rearSpline, Distance = rearWheelDistanceOnSpline, Ascending = !askerIsForward, IsForward = askerIsForward,
            }
            ;
            // Spawn the train slightly farther away so it has space.It will be moved forward after spawn.var spawnDistanceOffset = 2;
            var finalDistance = wheelToRearCouplingDistance + GetTrainCarFrontCouplingOffsetZ(trainCarPrefab);
            var finalPosition = GetPositionAlongTrack(splineInfo, finalDistance, trackSelection, out var finalSplineInfo);
            var resultPosition = GetPositionAlongTrack(finalSplineInfo, spawnDistanceOffset, trackSelection, out var spawnSplineInfo);
            var resultRotation = GetSplineTangentRotation(spawnSplineInfo.Spline, spawnSplineInfo.Distance, frontTrainCar.transform.rotation);
            if (trainCarPrefab.Reverse != frontTrainCarPrefab.Reverse) {
                resultRotation = Quaternion.LookRotation(resultRotation * -Vector3.forward);
            }
            // TODO: Fix issue where workcarts jump on start, when first two are both reverse
            var rearTrainCar = SpawnTrainCar(trainCarPrefab.PrefabPath, resultPosition, resultRotation);
            if (rearTrainCar != null) {
                if (rearTrainCar.FrontTrackSection == null) {
                    rearTrainCar.Kill();
                    return null;
                }
                rearTrainCar.MoveFrontWheelsAlongTrackSpline(rearTrainCar.FrontTrackSection, rearTrainCar.FrontWheelSplineDist, spawnDistanceOffset, rearTrainCar.RearTrackSection != rearTrainCar.FrontTrackSection ? rearTrainCar.RearTrackSection : null, trackSelection );
                rearTrainCar.transform.position = finalPosition;
                var frontCoupling = frontTrainCarPrefab.Reverse ? frontTrainCar.coupling.frontCoupling : frontTrainCar.coupling.rearCoupling;
                var rearCoupling = trainCarPrefab.Reverse ? rearTrainCar.coupling.rearCoupling : rearTrainCar.coupling.frontCoupling;
                frontCoupling.TryCouple(rearCoupling, reflect : true);
            }
            return rearTrainCar;
        }
        private static float GetTrainCarFrontCouplingOffsetZ(TrainCarPrefab trainCarPrefab) {
            var prefab = GameManager.server.FindPrefab(trainCarPrefab.PrefabPath)?.GetComponent<TrainCar>();
            if (prefab == null) return 0;
            return trainCarPrefab.Reverse ? prefab.rearCoupling.localPosition.z : prefab.frontCoupling.localPosition.z;
        }
        private static ConnectedTrackInfo GetAdjacentTrackInfo(TrainTrackSpline spline, TrackSelection selection, bool isAscending = true, bool askerIsForward = true) {
            var trackOptions = isAscending ? spline.nextTracks : spline.prevTracks;
            if (trackOptions.Count == 0) return null;
            if (trackOptions.Count == 1) return trackOptions[0];
            switch (selection) {
                case TrackSelection.Left : return isAscending == askerIsForward ? trackOptions.FirstOrDefault() : trackOptions.LastOrDefault();
                case TrackSelection.Right : return isAscending == askerIsForward ? trackOptions.LastOrDefault() : trackOptions.FirstOrDefault();
                default : return trackOptions[isAscending ? spline.straightestNextIndex : spline.straightestPrevIndex];
            }
        }
        private static Quaternion GetSplineTangentRotation(TrainTrackSpline spline, float distanceOnSpline, Quaternion approximateRotation) {
            spline.GetPositionAndTangent(distanceOnSpline, approximateRotation * Vector3.forward, out var tangentDirection);
            return Quaternion.LookRotation(tangentDirection);
        }
        private static int CompareVectors(Vector3 a, Vector3 b) {
            var result = a.y.CompareTo(b.y);
            if (result != 0) return result;
            result = a.x.CompareTo(b.x);
            if (result != 0) return result;
            return a.z.CompareTo(b.z);
        }
        private IEnumerator DoStartupRoutine() {
            if (_config.DisableDefaultTunnelWorkcartSpawnPoints) yield return _disableSpawnPointManager.DisableSpawnPointsRoutine();
            yield return _triggerManager.CreateAll();
            var foundTrainEngineIds = new HashSet<ulong>();
            foreach (var entity in BaseNetworkable.serverEntities) {
                var trainEngine = entity as TrainEngine;
                if (trainEngine == null) continue;
                var trainEngineData = _data.GetTrainEngineData(trainEngine.net.ID.Value);
                if (trainEngineData != null) {
                    foundTrainEngineIds.Add(trainEngine.net.ID.Value
// ... [Code preview truncated due to length. Full source in attached file] ...

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