Decompiled source of LethalBotsCommands v2.1.0

LethalBotsCommands.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalBots.AI;
using LethalBots.AI.AIStates;
using LethalBots.Enums;
using LethalBots.Managers;
using LethalBots.Utils.Helpers;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.AI;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace LethalBotsCommands;

[BepInPlugin("Erskine.LethalBotsCommands", "LethalBotsCommands", "2.1.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
	public static Plugin Instance;

	public static ManualLogSource Log;

	public ConfigEntry<bool> cEnabled;

	public ConfigEntry<bool> cRequireRange;

	public ConfigEntry<float> cRangeMeters;

	public ConfigEntry<bool> cReport;

	public ConfigEntry<bool> cLoadout;

	public ConfigEntry<bool> cTransfer;

	public ConfigEntry<bool> cRegroup;

	public ConfigEntry<bool> cWait;

	public ConfigEntry<bool> cCollect;

	public ConfigEntry<bool> cTakeOff;

	public ConfigEntry<bool> cFollow;

	public ConfigEntry<bool> cLead;

	public ConfigEntry<bool> cWalkieRange;

	public ConfigEntry<bool> cWalkieVoice;

	public ConfigEntry<bool> cNoFollowTeleport;

	public ConfigEntry<float> cFollowPathfindMaxDist;

	public ConfigEntry<bool> cImmersiveFollow;

	public ConfigEntry<float> cEarshot;

	public ConfigEntry<bool> cDiag;

	private void Awake()
	{
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Expected O, but got Unknown
		//IL_022d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0237: Expected O, but got Unknown
		//IL_028d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0297: Expected O, but got Unknown
		//IL_02e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e9: Expected O, but got Unknown
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		cEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch for all chat and voice bot commands.");
		cRequireRange = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RequireRange", true, "If on, only your bots within RangeMeters respond. If off, your bots respond no matter how far away they are.");
		cRangeMeters = ((BaseUnityPlugin)this).Config.Bind<float>("General", "RangeMeters", 25f, new ConfigDescription("How close (metres) one of your bots must be to respond, when RequireRange is on.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 200f), new object[0]));
		cReport = ((BaseUnityPlugin)this).Config.Bind<bool>("Commands", "Report", true, "Enable 'report' (bot says the total scrap value on the ship plus quota progress).");
		cLoadout = ((BaseUnityPlugin)this).Config.Bind<bool>("Commands", "Loadout", true, "Enable 'loadout' / 'gear' (bot goes and grabs its saved loadout).");
		cTransfer = ((BaseUnityPlugin)this).Config.Bind<bool>("Commands", "Transfer", true, "Enable 'transfer' (bot hands the loot it is carrying to the ship).");
		cRegroup = ((BaseUnityPlugin)this).Config.Bind<bool>("Commands", "Regroup", true, "Enable 'regroup' / 'group' (bot teleports to you).");
		cWait = ((BaseUnityPlugin)this).Config.Bind<bool>("Commands", "Wait", true, "Enable 'wait' / 'hold' / 'stay' (bot holds its position).");
		cCollect = ((BaseUnityPlugin)this).Config.Bind<bool>("Commands", "Collect", true, "Enable 'collect' (bot goes to grab the delivery from the dropship).");
		cTakeOff = ((BaseUnityPlugin)this).Config.Bind<bool>("Commands", "TakeOff", true, "Enable 'takeoff' / 'launch' / 'leave' (a bot starts the ship). Also matches 'take off'.");
		cFollow = ((BaseUnityPlugin)this).Config.Bind<bool>("Commands", "Follow", true, "Enable 'follow' (bot groups up and follows you). Also matches 'follow me'.");
		cLead = ((BaseUnityPlugin)this).Config.Bind<bool>("Commands", "LeadWay", true, "Enable 'lead the way' / 'leadway' (bot leaves your group and goes off looking for scrap). Also matches 'lead way'.");
		cWalkieRange = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "WalkieRange", true, "If on, a bot can hear a command from any distance when BOTH you and that bot are holding a walkie-talkie, even past RangeMeters.");
		cWalkieVoice = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "WalkieResponse", true, "When a command reaches a bot ONLY over the walkie (out of normal range), the bot audibly keys up its radio so you hear the transmission over your own active walkie. Turn this OFF to keep the bot's reply as chat text only (no radio audio). The chat reply is sent either way. Needs WalkieRange on.");
		cEarshot = ((BaseUnityPlugin)this).Config.Bind<float>("General", "EarshotMeters", 25f, new ConfigDescription("Within this many metres a bot is in genuine earshot, so its command reply plays as a normal voice (no radio echo). Only when a bot is FARTHER than this (or across an entrance from you) does its reply come over the walkie. This stops a bot that walked up to you from echoing its reply over the radio a beat after you already heard it. Independent of RangeMeters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 200f), new object[0]));
		cNoFollowTeleport = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "FollowPathfindsInsteadOfTeleporting", true, "When on, a following bot paths back to you instead of instantly teleporting to catch up, UP TO FollowPathfindMaxDistance. Past that distance it still teleports, so a bot deep in the facility actually reaches you instead of walking forever. Off = vanilla (always teleport). Affects all following (wheel and chat).");
		cFollowPathfindMaxDist = ((BaseUnityPlugin)this).Config.Bind<float>("General", "FollowPathfindMaxDistance", 60f, new ConfigDescription("How far (metres) a following bot will pathfind to reach you before it gives up and teleport-catches-up instead. Only used when FollowPathfindsInsteadOfTeleporting is on. Lower = teleports sooner; higher = walks from further away (but may never arrive if it can't path).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 500f), new object[0]));
		cImmersiveFollow = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ImmersiveFollowAcrossEntrances", true, "When you call a bot to follow from the OTHER side of an entrance (you outside, bot inside the facility, or vice versa), the bot walks to the nearest entrance, goes through it, and then paths to you, instead of teleporting straight to you. If it can't make it across in time it falls back to teleporting so it isn't left behind.");
		cDiag = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DiagnosticLogging", true, "Log detailed [diag] lines for every command: what matched, each bot's distance, whether it was in range or reached over walkie, and what ran. Turn on to confirm the mod works (especially long-range walkie commands) by reading the log; turn off for a quiet log.");
		Harmony h = new Harmony("Erskine.LethalBotsCommands");
		PatchOne(h, typeof(ManagerStartPatch));
		PatchOne(h, typeof(VoiceCommandsPatch));
		PatchOne(h, typeof(ChatEntryPatch));
		PatchOne(h, typeof(VoiceEntryPatch));
		PatchOne(h, typeof(NoFollowTeleportPatch));
		PatchOne(h, typeof(BotVoicePatch));
		PatchOne(h, typeof(BotVoiceSyncPatch));
		Log.LogInfo((object)"LethalBotsCommands v2.1.0 loaded. Type or say: report, loadout, transfer, regroup. Prefix a bot name for that bot, add 'all' for everyone; no name uses the nearest bot.");
	}

	private static void PatchOne(Harmony h, Type patchClass)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			new PatchClassProcessor(h, patchClass).Patch();
		}
		catch (Exception ex)
		{
			Log.LogError((object)("patch " + patchClass.Name + " failed (this hook disabled, others still active): " + ex.Message));
		}
	}
}
[HarmonyPatch(typeof(LethalBotManager), "Start")]
internal static class ManagerStartPatch
{
	[HarmonyPostfix]
	private static void Postfix()
	{
		Commands.RegisterCommands();
	}
}
[HarmonyPatch(typeof(LethalBotManager), "RegisterVoiceCommands")]
internal static class VoiceCommandsPatch
{
	[HarmonyPrefix]
	private static void Prefix()
	{
		Commands.RegisterCommands();
	}
}
[HarmonyPatch(typeof(HUDManager), "AddPlayerChatMessageClientRpc")]
internal static class ChatEntryPatch
{
	[HarmonyPrefix]
	private static void Prefix(HUDManager __instance, out bool __state)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Expected O, but got Unknown
		__state = Commands.IsClientRpcExec((NetworkBehaviour)__instance);
	}

	[HarmonyPostfix]
	private static void Postfix(string chatMessage, int playerId, bool __state)
	{
		if (__state)
		{
			Commands.Dispatch(chatMessage, Commands.ResolvePlayer(playerId), "chat");
		}
	}
}
[HarmonyPatch(typeof(LethalBotAI), "CheckAndBringCloserTeleportLethalBot")]
internal static class NoFollowTeleportPatch
{
	[HarmonyPrefix]
	private static bool Prefix(LethalBotAI __instance)
	{
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Expected O, but got Unknown
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Expected O, but got Unknown
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Expected O, but got Unknown
		//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if ((Object)(object)Plugin.Instance == (Object)null || Plugin.Instance.cNoFollowTeleport == null || !Plugin.Instance.cNoFollowTeleport.Value)
			{
				return true;
			}
			EnemyAI val = (EnemyAI)__instance;
			PlayerControllerB targetPlayer = val.targetPlayer;
			PlayerControllerB npc = Commands.GetNpc(__instance);
			if ((Object)targetPlayer == (Object)null || (Object)npc == (Object)null)
			{
				return false;
			}
			bool flag = Plugin.Instance.cImmersiveFollow != null && Plugin.Instance.cImmersiveFollow.Value;
			if (flag && npc.isInsideFactory != targetPlayer.isInsideFactory)
			{
				Commands.BeginImmersiveRoute(__instance, targetPlayer);
				return false;
			}
			if (flag)
			{
				return false;
			}
			Vector3 val2 = ((Component)val).transform.position - ((Component)targetPlayer).transform.position;
			float magnitude = ((Vector3)(ref val2)).magnitude;
			float value = Plugin.Instance.cFollowPathfindMaxDist.Value;
			return magnitude > value;
		}
		catch
		{
			return true;
		}
	}
}
[HarmonyPatch(typeof(AudioManager), "PlayAudio")]
internal static class BotVoicePatch
{
	[HarmonyPostfix]
	private static void Postfix(string smallPathAudioClip)
	{
		Commands.OnBotVoicePlayed(smallPathAudioClip);
	}
}
[HarmonyPatch(typeof(AudioManager), "SyncPlayAudio")]
internal static class BotVoiceSyncPatch
{
	[HarmonyPostfix]
	private static void Postfix(string path)
	{
		Commands.OnBotVoicePlayed(path);
	}
}
[HarmonyPatch(typeof(LethalBotManager), "TransmitVoiceChatToAllClientsClientRpc")]
internal static class VoiceEntryPatch
{
	[HarmonyPrefix]
	private static void Prefix(LethalBotManager __instance, out bool __state)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Expected O, but got Unknown
		__state = Commands.IsClientRpcExec((NetworkBehaviour)__instance);
	}

	[HarmonyPostfix]
	private static void Postfix(string message, int playerId, bool __state)
	{
		if (__state)
		{
			Commands.Dispatch(message, Commands.ResolvePlayer(playerId), "voice");
		}
	}
}
internal sealed class CmdDef
{
	public string[] Words;

	public ConfigEntry<bool> Toggle;

	public Action<LethalBotAI, PlayerControllerB> Effect;

	public Action<List<LethalBotAI>, PlayerControllerB> BatchEffect;

	public string Ack;

	public EnumVoicesState Voice;
}
internal static class Commands
{
	private static readonly string[] AllWords = new string[18]
	{
		"report", "loadout", "gear", "gearup", "transfer", "handover", "regroup", "group", "wait", "hold",
		"stay", "collect", "takeoff", "launch", "leave", "follow", "leadway", "lead"
	};

	private static readonly string[] AllWord = new string[3] { "all", "everyone", "everybody" };

	private static List<CmdDef> _defs;

	private static bool _commandsDone;

	private static string _lastNotifyMsg = null;

	private static float _lastNotifyTime = -999f;

	private static string _lastCmdMsg = null;

	private static float _lastCmdTime = -999f;

	private static bool _lastCmdResult = false;

	private static readonly HashSet<int> _walkieReached = new HashSet<int>();

	private static readonly HashSet<int> _routing = new HashSet<int>();

	private static FieldInfo _rpcStageField;

	private static bool _rpcStageResolved;

	private static LethalBotAI _radioBot;

	private static float _radioUntil = -999f;

	private static bool _radioDidTransmit;

	private static readonly List<AudioClip> _voiceClipCache = new List<AudioClip>();

	private static bool _voiceHookConfirmed;

	private static AudioClip _lastTxClip;

	private static float _lastTxTime = -999f;

	private static List<CmdDef> Defs()
	{
		//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_021b: Unknown result type (might be due to invalid IL or missing references)
		//IL_033a: Unknown result type (might be due to invalid IL or missing references)
		if (_defs != null)
		{
			return _defs;
		}
		List<CmdDef> list = new List<CmdDef>();
		list.Add(new CmdDef
		{
			Words = new string[1] { "report" },
			Toggle = Plugin.Instance.cReport,
			Effect = Effects.Report
		});
		list.Add(new CmdDef
		{
			Words = new string[3] { "loadout", "gear", "gearup" },
			Toggle = Plugin.Instance.cLoadout,
			Effect = Effects.Loadout,
			Ack = "On it, grabbing my loadout."
		});
		list.Add(new CmdDef
		{
			Words = new string[2] { "transfer", "handover" },
			Toggle = Plugin.Instance.cTransfer,
			Effect = Effects.Transfer,
			Ack = "Bringing the loot to the ship."
		});
		list.Add(new CmdDef
		{
			Words = new string[2] { "regroup", "group" },
			Toggle = Plugin.Instance.cRegroup,
			Effect = Effects.Regroup,
			Ack = "Regrouping on you.",
			Voice = (EnumVoicesState)16
		});
		list.Add(new CmdDef
		{
			Words = new string[3] { "wait", "hold", "stay" },
			Toggle = Plugin.Instance.cWait,
			Effect = Effects.Wait,
			Ack = "Holding position.",
			Voice = (EnumVoicesState)15
		});
		list.Add(new CmdDef
		{
			Words = new string[1] { "collect" },
			Toggle = Plugin.Instance.cCollect,
			BatchEffect = Collect.Run
		});
		list.Add(new CmdDef
		{
			Words = new string[3] { "takeoff", "launch", "leave" },
			Toggle = Plugin.Instance.cTakeOff,
			Effect = Effects.TakeOff
		});
		list.Add(new CmdDef
		{
			Words = new string[1] { "follow" },
			Toggle = Plugin.Instance.cFollow,
			Effect = Effects.Follow,
			Ack = "Right behind you!",
			Voice = (EnumVoicesState)16
		});
		list.Add(new CmdDef
		{
			Words = new string[2] { "leadway", "lead" },
			Toggle = Plugin.Instance.cLead,
			Effect = Effects.LeadWay,
			Ack = "Going for scrap!"
		});
		_defs = list;
		return _defs;
	}

	internal static void RegisterCommands()
	{
		//IL_00db: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e2: Expected O, but got Unknown
		if (_commandsDone)
		{
			return;
		}
		try
		{
			HashSet<string> hashSet = new HashSet<string>();
			try
			{
				hashSet = new HashSet<string>(ChatCommandsManager.GetAllRegisteredChatCommandKeywords());
			}
			catch
			{
			}
			int num = 0;
			foreach (CmdDef item2 in Defs())
			{
				if (item2.Toggle != null && !item2.Toggle.Value)
				{
					continue;
				}
				List<string> list = new List<string>();
				string[] words = item2.Words;
				foreach (string item in words)
				{
					if (!hashSet.Contains(item))
					{
						list.Add(item);
					}
				}
				if (list.Count != 0)
				{
					ChatCommand val = new ChatCommand(list.ToArray(), (Func<AIState, LethalBotAI, PlayerControllerB, string, bool, bool>)((AIState s, LethalBotAI b, PlayerControllerB p, string m, bool v) => MatchCommand(m) != null));
					ChatCommandsManager.RegisterGlobalCommand(val);
					num += list.Count;
				}
			}
			_commandsDone = true;
			Plugin.Log.LogInfo((object)("LethalBotsCommands: registered " + num + " command keyword(s) with LethalBots."));
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("command registration failed: " + ex));
		}
	}

	internal static void Dlog(string text)
	{
		try
		{
			if ((Object)(object)Plugin.Instance != (Object)null && Plugin.Instance.cDiag != null && Plugin.Instance.cDiag.Value)
			{
				Plugin.Log.LogInfo((object)("[diag] " + text));
			}
		}
		catch
		{
		}
	}

	internal static void StartLeadDiag(LethalBotAI bot)
	{
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Expected O, but got Unknown
		try
		{
			if (!((Object)(object)Plugin.Instance == (Object)null) && Plugin.Instance.cDiag != null && Plugin.Instance.cDiag.Value && !((Object)bot == (Object)null))
			{
				((MonoBehaviour)Plugin.Instance).StartCoroutine(LeadDiagRoutine(bot, SafeName(bot)));
			}
		}
		catch
		{
		}
	}

	private static IEnumerator LeadDiagRoutine(LethalBotAI bot, string name)
	{
		Vector3 start = Vector3.zero;
		PlayerControllerB np0 = GetNpc(bot);
		if ((Object)np0 != (Object)null)
		{
			start = ((Component)np0).transform.position;
		}
		for (int i = 1; i <= 6; i++)
		{
			yield return (object)new WaitForSeconds(1f);
			LogLeadSample(bot, name, start, i);
		}
	}

	private static void LogLeadSample(LethalBotAI bot, string name, Vector3 start, int sec)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Expected O, but got Unknown
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Expected O, but got Unknown
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Expected O, but got Unknown
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0135: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (!((Object)bot == (Object)null))
			{
				string text = ((bot.State == null) ? "null" : ((object)bot.State).GetType().Name);
				NavMeshAgent agent = ((EnemyAI)bot).agent;
				PlayerControllerB npc = GetNpc(bot);
				float num;
				if ((Object)npc != (Object)null)
				{
					Vector3 val = ((Component)npc).transform.position - start;
					num = ((Vector3)(ref val)).magnitude;
				}
				else
				{
					num = -1f;
				}
				float num2 = num;
				string text3;
				if ((Object)agent != (Object)null)
				{
					string text2 = ((!float.IsInfinity(agent.remainingDistance)) ? agent.remainingDistance.ToString("0.0") : "inf");
					object[] obj = new object[14]
					{
						"speed=", null, null, null, null, null, null, null, null, null,
						null, null, null, null
					};
					Vector3 velocity = agent.velocity;
					obj[1] = ((Vector3)(ref velocity)).magnitude.ToString("0.00");
					obj[2] = " hasPath=";
					obj[3] = agent.hasPath;
					obj[4] = " pending=";
					obj[5] = agent.pathPending;
					obj[6] = " remain=";
					obj[7] = text2;
					obj[8] = " status=";
					obj[9] = agent.pathStatus;
					obj[10] = " enabled=";
					obj[11] = ((Behaviour)agent).enabled;
					obj[12] = " onNavMesh=";
					obj[13] = agent.isOnNavMesh;
					text3 = string.Concat(obj);
				}
				else
				{
					text3 = "no agent";
				}
				Dlog("lead check +" + sec + "s: " + name + " state=" + text + " movedSinceCmd=" + num2.ToString("0.0") + "m " + text3);
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("lead diag failed: " + ex.Message));
		}
	}

	internal static void BeginImmersiveRoute(LethalBotAI bot, PlayerControllerB player)
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Expected O, but got Unknown
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Expected O, but got Unknown
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (!((Object)(object)Plugin.Instance == (Object)null) && !((Object)bot == (Object)null) && !((Object)player == (Object)null))
			{
				int instanceID = ((Object)bot).GetInstanceID();
				if (!_routing.Contains(instanceID))
				{
					_routing.Add(instanceID);
					((MonoBehaviour)Plugin.Instance).StartCoroutine(CrossBoundaryFollowRoutine(bot, player));
				}
			}
		}
		catch
		{
		}
	}

	private static IEnumerator CrossBoundaryFollowRoutine(LethalBotAI bot, PlayerControllerB player)
	{
		int botId = (((Object)bot != (Object)null) ? ((Object)bot).GetInstanceID() : 0);
		PlayerControllerB npc = GetNpc(bot);
		if ((Object)npc == (Object)null || (Object)player == (Object)null)
		{
			_routing.Remove(botId);
			yield break;
		}
		bool botInside = npc.isInsideFactory;
		Dlog(SafeName(bot) + ": cross-boundary follow — bot " + ((!botInside) ? "outside" : "inside") + ", you " + ((!player.isInsideFactory) ? "outside" : "inside") + "; routing through the nearest entrance.");
		try
		{
			GroupManager instance = GroupManager.Instance;
			if ((Object)instance != (Object)null && instance.ArePlayersInSameGroup(npc, player))
			{
				instance.RemoveFromCurrentGroupAndSync(npc);
			}
		}
		catch
		{
		}
		try
		{
			if (botInside)
			{
				bot.State = (AIState)new ReturnToShipState(bot.State, (EnumReturnToShipType)0, (AIState)null);
			}
			else
			{
				bot.State = (AIState)new SearchingForScrapState(bot.State, (EntranceTeleport)null);
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("cross-boundary route failed: " + ex.Message));
		}
		float t = 0f;
		bool crossed = false;
		while (t < 90f)
		{
			yield return (object)new WaitForSeconds(0.5f);
			t += 0.5f;
			if ((Object)bot == (Object)null || ((EnemyAI)bot).isEnemyDead)
			{
				_routing.Remove(botId);
				yield break;
			}
			PlayerControllerB np2 = GetNpc(bot);
			if ((Object)np2 == (Object)null || (Object)player == (Object)null)
			{
				_routing.Remove(botId);
				yield break;
			}
			if (np2.isInsideFactory == player.isInsideFactory)
			{
				crossed = true;
				break;
			}
		}
		if (crossed)
		{
			Dlog(SafeName(bot) + ": crossed the entrance, now following you directly.");
			Effects.DoDirectFollow(bot, player);
		}
		else
		{
			Dlog(SafeName(bot) + ": couldn't cross in time, teleporting so it isn't left behind.");
			try
			{
				bot.SyncTeleportLethalBot(((Component)player).transform.position, (bool?)(!player.isInsideFactory), (EntranceTeleport)null, false, 0f, false, false);
			}
			catch
			{
			}
			Effects.DoDirectFollow(bot, player);
		}
		_routing.Remove(botId);
	}

	internal static bool Dispatch(string message, PlayerControllerB player, string source)
	{
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Expected O, but got Unknown
		try
		{
			if (_lastCmdMsg == message && Time.time - _lastCmdTime < 0.3f)
			{
				return _lastCmdResult;
			}
			_lastCmdMsg = message;
			_lastCmdTime = Time.time;
			Dlog("received '" + message + "' via " + source + " from " + ((!((Object)player != (Object)null)) ? "unknown" : player.playerUsername));
			_lastCmdResult = HandleMessage(message, player);
			return _lastCmdResult;
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Dispatch error: " + ex));
			return false;
		}
	}

	internal static bool IsClientRpcExec(NetworkBehaviour nb)
	{
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Expected O, but got Unknown
		try
		{
			if (!_rpcStageResolved)
			{
				_rpcStageResolved = true;
				_rpcStageField = AccessTools.Field(typeof(NetworkBehaviour), "__rpc_exec_stage");
			}
			if (_rpcStageField == null || (Object)nb == (Object)null)
			{
				return true;
			}
			return Convert.ToInt32(_rpcStageField.GetValue(nb)) == 1;
		}
		catch
		{
			return true;
		}
	}

	internal static PlayerControllerB ResolvePlayer(int id)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Expected O, but got Unknown
		StartOfRound instance = StartOfRound.Instance;
		if ((Object)instance == (Object)null || instance.allPlayerScripts == null || id < 0 || id >= instance.allPlayerScripts.Length)
		{
			return null;
		}
		return instance.allPlayerScripts[id];
	}

	internal static bool HandleMessage(string message, PlayerControllerB sender)
	{
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Expected O, but got Unknown
		//IL_035e: Unknown result type (might be due to invalid IL or missing references)
		//IL_03dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_03e7: Expected O, but got Unknown
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Expected O, but got Unknown
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: Expected O, but got Unknown
		//IL_00c5: Expected O, but got Unknown
		//IL_0382: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d8: Expected O, but got Unknown
		try
		{
			if ((Object)(object)Plugin.Instance == (Object)null || !Plugin.Instance.cEnabled.Value)
			{
				return false;
			}
			CmdDef cmdDef = MatchCommand(message);
			if (cmdDef == null)
			{
				return false;
			}
			Dlog("matched command '" + cmdDef.Words[0] + "' in message '" + message + "'.");
			if ((Object)sender == (Object)null)
			{
				return false;
			}
			PlayerControllerB val = ((!((Object)StartOfRound.Instance != (Object)null)) ? null : StartOfRound.Instance.localPlayerController);
			if ((Object)sender != (Object)val)
			{
				Dlog("sender is not the local player, so this client won't run it (the sender's client will).");
				return true;
			}
			List<LethalBotAI> list = OwnedBots();
			Dlog("you own " + list.Count + " active bot(s). Your walkie active: " + ((!HasActiveWalkie(val)) ? "no" : "YES") + ". WalkieRange feature: " + ((!Plugin.Instance.cWalkieRange.Value) ? "off" : "on") + ". RequireRange: " + ((!Plugin.Instance.cRequireRange.Value) ? "off" : ("on (" + Plugin.Instance.cRangeMeters.Value + "m)")) + ".");
			if (list.Count == 0)
			{
				NotifyOnce(message, "You don't have any bots out.");
				return true;
			}
			PlayerControllerB val2 = ((!((Object)val != (Object)null)) ? sender : val);
			List<LethalBotAI> list2 = ComputeTargets(list, val2, message);
			int num = 0;
			if (Plugin.Instance.cWalkieVoice.Value && Plugin.Instance.cWalkieRange.Value)
			{
				foreach (LethalBotAI item in list2)
				{
					if (WasReachedOverWalkie(item))
					{
						RadioAnnounce(item);
						Dlog(SafeName(item) + " reached over walkie — its reply will carry over the radio.");
						break;
					}
				}
			}
			if (cmdDef.BatchEffect != null)
			{
				if (list2.Count > 0)
				{
					try
					{
						cmdDef.BatchEffect(list2, val2);
						num = list2.Count;
					}
					catch (Exception ex)
					{
						Plugin.Log.LogWarning((object)("command batch effect error: " + ex.Message));
					}
				}
			}
			else
			{
				foreach (LethalBotAI item2 in list2)
				{
					try
					{
						cmdDef.Effect(item2, val2);
						num++;
					}
					catch (Exception ex2)
					{
						Plugin.Log.LogWarning((object)("command effect error: " + ex2.Message));
					}
				}
			}
			if (num > 0 && (int)cmdDef.Voice != 0)
			{
				foreach (LethalBotAI item3 in list2)
				{
					TriggerCommandVoice(item3, cmdDef.Voice);
				}
			}
			if (num > 0 && !string.IsNullOrEmpty(cmdDef.Ack))
			{
				foreach (LethalBotAI item4 in list2)
				{
					try
					{
						if ((Object)item4 != (Object)null)
						{
							item4.SendChatMessage(cmdDef.Ack, false);
						}
					}
					catch (Exception ex3)
					{
						Plugin.Log.LogWarning((object)("ack message failed: " + ex3.Message));
					}
				}
			}
			if (num == 0)
			{
				Dlog("no bot was reachable for '" + message + "' (see the per-bot lines above).");
			}
			else
			{
				Dlog("applied '" + cmdDef.Words[0] + "' to " + num + " bot(s).");
			}
			Plugin.Log.LogInfo((object)("[cmd] '" + message + "' -> " + num + " bot(s)"));
			return true;
		}
		catch (Exception ex4)
		{
			Plugin.Log.LogWarning((object)("HandleMessage error: " + ex4));
			return false;
		}
	}

	private static string[] Tokenize(string message)
	{
		string text = ((message != null) ? message : "").ToLowerInvariant();
		text = text.Replace("take off", "takeoff").Replace("wait here", "wait").Replace("hold position", "hold")
			.Replace("collect delivery", "collect")
			.Replace("grab loadout", "loadout")
			.Replace("lead the way", "leadway")
			.Replace("lead way", "leadway")
			.Replace("follow me", "follow");
		return text.Split(new char[11]
		{
			' ', '\t', ',', '.', '!', '?', '\'', '"', '/', ':',
			';'
		}, StringSplitOptions.RemoveEmptyEntries);
	}

	private static CmdDef MatchCommand(string message)
	{
		string[] array = Tokenize(message);
		if (array.Length == 0)
		{
			return null;
		}
		List<LethalBotAI> owned = OwnedBots();
		CmdDef result = null;
		string[] array2 = array;
		foreach (string text in array2)
		{
			CmdDef cmdDef = FindDef(text);
			if (cmdDef != null)
			{
				result = cmdDef;
			}
			else if (!IsAllWord(text) && (text.Length < 3 || !MatchesBotName(owned, text)))
			{
				return null;
			}
		}
		return result;
	}

	private static CmdDef FindDef(string token)
	{
		foreach (CmdDef item in Defs())
		{
			if (item.Toggle != null && !item.Toggle.Value)
			{
				continue;
			}
			string[] words = item.Words;
			foreach (string text in words)
			{
				if (text == token)
				{
					return item;
				}
			}
		}
		return null;
	}

	private static bool IsAllWord(string t)
	{
		string[] allWord = AllWord;
		foreach (string text in allWord)
		{
			if (text == t)
			{
				return true;
			}
		}
		return false;
	}

	private static bool IsKeyword(string t)
	{
		string[] allWords = AllWords;
		foreach (string text in allWords)
		{
			if (text == t)
			{
				return true;
			}
		}
		return false;
	}

	private static bool MatchesBotName(List<LethalBotAI> owned, string token)
	{
		foreach (LethalBotAI item in owned)
		{
			string text = SafeName(item);
			if (!string.IsNullOrEmpty(text) && text.ToLowerInvariant().Contains(token))
			{
				return true;
			}
		}
		return false;
	}

	private static List<LethalBotAI> ComputeTargets(List<LethalBotAI> owned, PlayerControllerB player, string message)
	{
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f9: Expected O, but got Unknown
		//IL_0368: Unknown result type (might be due to invalid IL or missing references)
		//IL_0373: Expected O, but got Unknown
		//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d5: Expected O, but got Unknown
		//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0204: Unknown result type (might be due to invalid IL or missing references)
		//IL_0209: Unknown result type (might be due to invalid IL or missing references)
		//IL_020b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0210: Unknown result type (might be due to invalid IL or missing references)
		//IL_0430: Unknown result type (might be due to invalid IL or missing references)
		//IL_043b: Expected O, but got Unknown
		//IL_0265: Unknown result type (might be due to invalid IL or missing references)
		//IL_044c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0451: Unknown result type (might be due to invalid IL or missing references)
		//IL_0453: Unknown result type (might be due to invalid IL or missing references)
		//IL_0458: Unknown result type (might be due to invalid IL or missing references)
		//IL_049c: Unknown result type (might be due to invalid IL or missing references)
		//IL_04a7: Expected O, but got Unknown
		_walkieReached.Clear();
		string[] array = Tokenize(message);
		bool flag = false;
		string[] array2 = array;
		foreach (string t in array2)
		{
			if (IsAllWord(t))
			{
				flag = true;
				break;
			}
		}
		Vector3 position = ((Component)player).transform.position;
		LethalBotAI val = null;
		foreach (LethalBotAI item in owned)
		{
			string text = SafeName(item);
			if (string.IsNullOrEmpty(text))
			{
				continue;
			}
			string text2 = text.ToLowerInvariant();
			string[] array3 = array;
			foreach (string text3 in array3)
			{
				if (text3.Length >= 3 && !IsKeyword(text3) && !IsAllWord(text3) && text2.Contains(text3))
				{
					val = item;
					break;
				}
			}
			if (!((Object)val != (Object)null))
			{
				continue;
			}
			break;
		}
		List<LethalBotAI> list = new List<LethalBotAI>();
		bool value = Plugin.Instance.cRequireRange.Value;
		float num = Plugin.Instance.cRangeMeters.Value * Plugin.Instance.cRangeMeters.Value;
		bool flag2 = Plugin.Instance.cWalkieRange.Value && HasActiveWalkie(player);
		foreach (LethalBotAI item2 in owned)
		{
			if (item2.State is MissionControlState)
			{
				continue;
			}
			if (!value)
			{
				list.Add(item2);
				continue;
			}
			PlayerControllerB npc = GetNpc(item2);
			if ((Object)npc == (Object)null)
			{
				continue;
			}
			Vector3 val2 = ((Component)npc).transform.position - position;
			float magnitude = ((Vector3)(ref val2)).magnitude;
			Vector3 val3 = ((Component)npc).transform.position - position;
			bool flag3 = ((Vector3)(ref val3)).sqrMagnitude <= num;
			bool flag4 = HasWalkieInSlots(npc);
			bool flag5 = flag2 && flag4;
			if (flag3 || flag5)
			{
				list.Add(item2);
				if (!flag3 && flag5)
				{
					_walkieReached.Add(((Object)item2).GetInstanceID());
				}
			}
			Dlog(SafeName(item2) + ": dist=" + magnitude.ToString("0.0") + "m inRange=" + ((!flag3) ? "no" : "yes") + " botHasWalkie=" + ((!flag4) ? "no" : "yes") + " yourWalkieActive=" + ((!flag2) ? "no" : "yes") + " -> " + (flag3 ? "reachable (in range)" : ((!flag5) ? "NOT reachable" : "reachable OVER WALKIE")) + ".");
		}
		if ((Object)val != (Object)null)
		{
			List<LethalBotAI> list2 = new List<LethalBotAI>();
			if (list.Contains(val))
			{
				list2.Add(val);
			}
			else
			{
				string text4 = ((!(val.State is MissionControlState)) ? "is too far away to hear you" : "can't take orders while manning the terminal");
				NotifyOnce(message, SafeName(val) + " " + text4 + ".");
			}
			return list2;
		}
		if (list.Count == 0)
		{
			NotifyOnce(message, "No bots are close enough to hear you.");
			return new List<LethalBotAI>();
		}
		if (flag)
		{
			return list;
		}
		LethalBotAI val4 = null;
		float num2 = float.MaxValue;
		foreach (LethalBotAI item3 in list)
		{
			PlayerControllerB npc2 = GetNpc(item3);
			if (!((Object)npc2 == (Object)null))
			{
				Vector3 val5 = ((Component)npc2).transform.position - position;
				float sqrMagnitude = ((Vector3)(ref val5)).sqrMagnitude;
				if (sqrMagnitude < num2)
				{
					num2 = sqrMagnitude;
					val4 = item3;
				}
			}
		}
		List<LethalBotAI> list3 = new List<LethalBotAI>();
		if ((Object)val4 != (Object)null)
		{
			list3.Add(val4);
		}
		return list3;
	}

	internal static List<LethalBotAI> OwnedBots()
	{
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Expected O, but got Unknown
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Expected O, but got Unknown
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Expected O, but got Unknown
		List<LethalBotAI> list = new List<LethalBotAI>();
		LethalBotManager instance = LethalBotManager.Instance;
		LethalBotAI[] array = ((!((Object)instance != (Object)null)) ? null : instance.GetLethalBotsAIOwnedByLocal());
		if (array == null)
		{
			return list;
		}
		LethalBotAI[] array2 = array;
		foreach (LethalBotAI val in array2)
		{
			if (!((Object)val == (Object)null) && !((EnemyAI)val).isEnemyDead)
			{
				PlayerControllerB npc = GetNpc(val);
				if (!((Object)npc == (Object)null) && !npc.isPlayerDead)
				{
					list.Add(val);
				}
			}
		}
		return list;
	}

	internal static PlayerControllerB GetNpc(LethalBotAI b)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Expected O, but got Unknown
		if ((Object)b == (Object)null || b.NpcController == null)
		{
			return null;
		}
		return b.NpcController.Npc;
	}

	private static bool HasActiveWalkie(PlayerControllerB p)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Expected O, but got Unknown
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Expected O, but got Unknown
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Expected O, but got Unknown
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Expected O, but got Unknown
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Expected O, but got Unknown
		//IL_0071: Expected O, but got Unknown
		if ((Object)p == (Object)null)
		{
			return false;
		}
		try
		{
			List<WalkieTalkie> allWalkieTalkies = WalkieTalkie.allWalkieTalkies;
			if (allWalkieTalkies != null)
			{
				foreach (WalkieTalkie item in allWalkieTalkies)
				{
					if (!((Object)item == (Object)null))
					{
						GrabbableObject val = (GrabbableObject)item;
						if (val.isBeingUsed && (Object)val.playerHeldBy == (Object)p)
						{
							return true;
						}
					}
				}
			}
		}
		catch
		{
		}
		if (p.ItemSlots != null)
		{
			GrabbableObject[] itemSlots = p.ItemSlots;
			foreach (GrabbableObject val2 in itemSlots)
			{
				if ((Object)val2 != (Object)null && val2 is WalkieTalkie && val2.isBeingUsed)
				{
					return true;
				}
			}
		}
		return false;
	}

	private static bool HasWalkieInSlots(PlayerControllerB p)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Expected O, but got Unknown
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Expected O, but got Unknown
		if ((Object)p == (Object)null || p.ItemSlots == null)
		{
			return false;
		}
		GrabbableObject[] itemSlots = p.ItemSlots;
		foreach (GrabbableObject val in itemSlots)
		{
			if ((Object)val != (Object)null && val is WalkieTalkie)
			{
				return true;
			}
		}
		return false;
	}

	private static WalkieTalkie FindWalkie(PlayerControllerB p)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Expected O, but got Unknown
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Expected O, but got Unknown
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Expected O, but got Unknown
		if ((Object)p == (Object)null || p.ItemSlots == null)
		{
			return null;
		}
		GrabbableObject[] itemSlots = p.ItemSlots;
		foreach (GrabbableObject val in itemSlots)
		{
			if ((Object)val != (Object)null && val is WalkieTalkie)
			{
				return (WalkieTalkie)val;
			}
		}
		return null;
	}

	private static WalkieTalkie AnyWalkie()
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Expected O, but got Unknown
		try
		{
			List<WalkieTalkie> allWalkieTalkies = WalkieTalkie.allWalkieTalkies;
			if (allWalkieTalkies != null)
			{
				foreach (WalkieTalkie item in allWalkieTalkies)
				{
					if ((Object)item != (Object)null)
					{
						return item;
					}
				}
			}
		}
		catch
		{
		}
		return null;
	}

	private static AudioClip RandomBotVoiceClip()
	{
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Expected O, but got Unknown
		try
		{
			if (_voiceClipCache.Count > 0)
			{
				return _voiceClipCache[Random.Range(0, _voiceClipCache.Count)];
			}
			AudioManager instance = AudioManager.Instance;
			if ((Object)instance == (Object)null || instance.DictAudioClipsByPath == null || instance.DictAudioClipsByPath.Count == 0)
			{
				return null;
			}
			int num = Random.Range(0, instance.DictAudioClipsByPath.Count);
			int num2 = 0;
			foreach (KeyValuePair<string, AudioClip> item in instance.DictAudioClipsByPath)
			{
				if (num2 == num)
				{
					return item.Value;
				}
				num2++;
			}
		}
		catch
		{
		}
		return null;
	}

	internal static bool WasReachedOverWalkie(LethalBotAI b)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Expected O, but got Unknown
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			return (Object)b != (Object)null && _walkieReached.Contains(((Object)b).GetInstanceID());
		}
		catch
		{
			return false;
		}
	}

	private static bool IsWithinEarshot(LethalBotAI bot)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Expected O, but got Unknown
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Expected O, but got Unknown
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Expected O, but got Unknown
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			PlayerControllerB val = ((!((Object)StartOfRound.Instance != (Object)null)) ? null : StartOfRound.Instance.localPlayerController);
			PlayerControllerB npc = GetNpc(bot);
			if ((Object)val == (Object)null || (Object)npc == (Object)null)
			{
				return false;
			}
			if (npc.isInsideFactory != val.isInsideFactory)
			{
				return false;
			}
			float num = ((!((Object)(object)Plugin.Instance != (Object)null)) ? 25f : Plugin.Instance.cEarshot.Value);
			Vector3 val2 = ((Component)npc).transform.position - ((Component)val).transform.position;
			float magnitude = ((Vector3)(ref val2)).magnitude;
			return magnitude <= num;
		}
		catch
		{
			return false;
		}
	}

	internal static void RadioAnnounce(LethalBotAI bot)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Expected O, but got Unknown
		try
		{
			if (!((Object)bot == (Object)null) && !((Object)(object)Plugin.Instance == (Object)null))
			{
				_radioBot = bot;
				_radioUntil = Time.time + 8f;
				_radioDidTransmit = false;
			}
		}
		catch
		{
		}
	}

	internal static void TriggerCommandVoice(LethalBotAI bot, EnumVoicesState state)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Expected O, but got Unknown
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Expected O, but got Unknown
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (!((Object)bot == (Object)null) && (int)state != 0 && bot.LethalBotIdentity != null)
			{
				LethalBotVoice voice = bot.LethalBotIdentity.Voice;
				if (voice != null)
				{
					PlayVoiceParameters val = default(PlayVoiceParameters);
					((PlayVoiceParameters)(ref val)).VoiceState = state;
					((PlayVoiceParameters)(ref val)).CanTalkIfOtherLethalBotTalk = true;
					((PlayVoiceParameters)(ref val)).WaitForCooldown = false;
					((PlayVoiceParameters)(ref val)).CutCurrentVoiceStateToTalk = true;
					((PlayVoiceParameters)(ref val)).CanRepeatVoiceState = true;
					((PlayVoiceParameters)(ref val)).ShouldSync = true;
					PlayerControllerB npc = GetNpc(bot);
					((PlayVoiceParameters)(ref val)).IsLethalBotInside = (Object)npc != (Object)null && npc.isInsideFactory;
					((PlayVoiceParameters)(ref val)).AllowSwearing = true;
					voice.TryPlayVoiceAudio(val);
				}
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("trigger voice failed: " + ex.Message));
		}
	}

	internal static void OnBotVoicePlayed(string path)
	{
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Expected O, but got Unknown
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Expected O, but got Unknown
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Expected O, but got Unknown
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Expected O, but got Unknown
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_0142: Expected O, but got Unknown
		//IL_0157: Unknown result type (might be due to invalid IL or missing references)
		//IL_0161: Unknown result type (might be due to invalid IL or missing references)
		//IL_016b: Expected O, but got Unknown
		//IL_016b: Expected O, but got Unknown
		try
		{
			AudioManager instance = AudioManager.Instance;
			if (!_voiceHookConfirmed)
			{
				_voiceHookConfirmed = true;
				int num = ((!((Object)instance != (Object)null) || instance.DictAudioClipsByPath == null) ? (-1) : instance.DictAudioClipsByPath.Count);
				Dlog("voice hook FIRED. path='" + (path ?? "null") + "' dictCount=" + num);
			}
			if ((Object)instance == (Object)null || instance.DictAudioClipsByPath == null || path == null)
			{
				return;
			}
			AudioClip val = ResolveClip(instance, path);
			if ((Object)val == (Object)null)
			{
				if ((Object)_radioBot != (Object)null && Time.time < _radioUntil)
				{
					Dlog("in-window voice but clip unresolved for path '" + path + "'.");
				}
				return;
			}
			if (!_voiceClipCache.Contains(val))
			{
				_voiceClipCache.Add(val);
				if (_voiceClipCache.Count > 40)
				{
					_voiceClipCache.RemoveAt(0);
				}
			}
			if ((Object)_radioBot != (Object)null && Time.time < _radioUntil && (!((Object)val == (Object)_lastTxClip) || !(Time.time - _lastTxTime < 0.4f)))
			{
				if (IsWithinEarshot(_radioBot))
				{
					Dlog(SafeName(_radioBot) + " is within earshot now, so its reply plays as a normal voice (no radio echo).");
				}
				else if (TransmitClip(_radioBot, val))
				{
					_lastTxClip = val;
					_lastTxTime = Time.time;
					_radioDidTransmit = true;
					Dlog(SafeName(_radioBot) + " transmitted its real voice line over the radio.");
				}
			}
		}
		catch
		{
		}
	}

	private static AudioClip ResolveClip(AudioManager am, string path)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Expected O, but got Unknown
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: Expected O, but got Unknown
		try
		{
			if (am.DictAudioClipsByPath.TryGetValue(path, out var value) && (Object)value != (Object)null)
			{
				return value;
			}
			string text = path;
			int num = Math.Max(path.LastIndexOf('\\'), path.LastIndexOf('/'));
			if (num >= 0)
			{
				text = path.Substring(num + 1);
			}
			int num2 = text.LastIndexOf('.');
			if (num2 >= 0)
			{
				text = text.Substring(0, num2);
			}
			if (text.Length == 0)
			{
				return null;
			}
			foreach (KeyValuePair<string, AudioClip> item in am.DictAudioClipsByPath)
			{
				if ((Object)item.Value == (Object)null || ((item.Key == null || !item.Key.Contains(text)) && (((Object)item.Value).name == null || !((Object)item.Value).name.Contains(text))))
				{
					continue;
				}
				return item.Value;
			}
		}
		catch
		{
		}
		return null;
	}

	private static IEnumerator RadioBarkFallback(LethalBotAI bot, float until)
	{
		while (Time.time < until)
		{
			yield return null;
		}
		if (_radioDidTransmit || !((Object)_radioBot == (Object)bot) || !((Object)bot != (Object)null))
		{
			yield break;
		}
		AudioClip val = RandomBotVoiceClip();
		if ((Object)val == (Object)null)
		{
			WalkieTalkie val2 = AnyWalkie();
			if ((Object)val2 != (Object)null && val2.startTransmissionSFX != null && val2.startTransmissionSFX.Length > 0)
			{
				val = val2.startTransmissionSFX[0];
			}
		}
		if ((Object)val != (Object)null)
		{
			TransmitClip(bot, val);
		}
	}

	private static bool TransmitClip(LethalBotAI bot, AudioClip clip)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Expected O, but got Unknown
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Expected O, but got Unknown
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Expected O, but got Unknown
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Expected O, but got Unknown
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Expected O, but got Unknown
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Expected O, but got Unknown
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Expected O, but got Unknown
		try
		{
			PlayerControllerB npc = GetNpc(bot);
			if ((Object)npc == (Object)null || (Object)clip == (Object)null)
			{
				return false;
			}
			WalkieTalkie val = FindWalkie(npc);
			if ((Object)val == (Object)null)
			{
				return false;
			}
			AudioSource val2 = ((EnemyAI)bot).creatureVoice;
			if ((Object)val2 == (Object)null)
			{
				val2 = val.thisAudio;
			}
			if ((Object)val2 == (Object)null)
			{
				return false;
			}
			GrabbableObject val3 = (GrabbableObject)val;
			bool isBeingUsed = val3.isBeingUsed;
			bool clientIsHoldingAndSpeakingIntoThis = val.clientIsHoldingAndSpeakingIntoThis;
			bool isHoldingButton = val.isHoldingButton;
			PlayerControllerB playerHeldBy = val3.playerHeldBy;
			val3.isBeingUsed = true;
			val.clientIsHoldingAndSpeakingIntoThis = true;
			val.isHoldingButton = true;
			if ((Object)val3.playerHeldBy == (Object)null)
			{
				val3.playerHeldBy = npc;
			}
			try
			{
				WalkieTalkie.TransmitOneShotAudio(val2, clip, 1f);
			}
			catch
			{
			}
			val3.isBeingUsed = isBeingUsed;
			val.clientIsHoldingAndSpeakingIntoThis = clientIsHoldingAndSpeakingIntoThis;
			val.isHoldingButton = isHoldingButton;
			val3.playerHeldBy = playerHeldBy;
			return true;
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("radio transmit failed: " + ex.Message));
			return false;
		}
	}

	internal static string SafeName(LethalBotAI b)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Expected O, but got Unknown
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Expected O, but got Unknown
		try
		{
			if ((Object)b != (Object)null && b.LethalBotIdentity != null)
			{
				string name = b.LethalBotIdentity.Name;
				if (!string.IsNullOrEmpty(name))
				{
					return name;
				}
			}
		}
		catch
		{
		}
		try
		{
			PlayerControllerB npc = GetNpc(b);
			if ((Object)npc != (Object)null)
			{
				return npc.playerUsername;
			}
		}
		catch
		{
		}
		return null;
	}

	private static void NotifyOnce(string message, string text)
	{
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Expected O, but got Unknown
		try
		{
			if (!(_lastNotifyMsg == message) || !(Time.time - _lastNotifyTime < 1.5f))
			{
				_lastNotifyMsg = message;
				_lastNotifyTime = Time.time;
				HUDManager instance = HUDManager.Instance;
				if ((Object)instance != (Object)null)
				{
					instance.DisplayTip("Bot Command", text, false, false, "LBC_botcmd");
				}
			}
		}
		catch
		{
		}
	}
}
internal static class Effects
{
	internal static void Report(LethalBotAI bot, PlayerControllerB player)
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Expected O, but got Unknown
		int valueOfAllScrapOnShip = LethalBotManager.GetValueOfAllScrapOnShip(bot, true);
		TimeOfDay instance = TimeOfDay.Instance;
		string text = ((!((Object)instance != (Object)null)) ? ("Ship scrap: $" + valueOfAllScrapOnShip + ".") : ("Ship scrap: $" + valueOfAllScrapOnShip + ". Quota: $" + instance.quotaFulfilled + "/$" + instance.profitQuota + "."));
		bot.SendChatMessage(text, false);
	}

	internal static void Regroup(LethalBotAI bot, PlayerControllerB player)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		bot.SyncTeleportLethalBot(((Component)player).transform.position, (bool?)(!player.isInsideFactory), (EntranceTeleport)null, false, 0f, false, false);
	}

	internal static void Transfer(LethalBotAI bot, PlayerControllerB player)
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Expected O, but got Unknown
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Expected O, but got Unknown
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Expected O, but got Unknown
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Expected O, but got Unknown
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		LethalBotManager instance = LethalBotManager.Instance;
		PlayerControllerB val = ((bot.NpcController != null) ? bot.NpcController.Npc : null);
		if ((Object)instance != (Object)null && (Object)val != (Object)null)
		{
			instance.AddPlayerToLootTransferListAndSync(new NetworkBehaviourReference((NetworkBehaviour)val));
		}
		bot.State = (AIState)new TransferLootState(bot.State);
	}

	internal static void Loadout(LethalBotAI bot, PlayerControllerB player)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Expected O, but got Unknown
		bot.State = (AIState)new GrabLoadoutState(bot.State, (AIState)null);
	}

	internal static void Wait(LethalBotAI bot, PlayerControllerB player)
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Expected O, but got Unknown
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Expected O, but got Unknown
		PlayerControllerB val = ((bot.NpcController != null) ? bot.NpcController.Npc : null);
		if ((Object)val != (Object)null)
		{
			bot.State = (AIState)new HoldPositionState(bot.State, ((Component)val).transform.position);
		}
	}

	internal static void TakeOff(LethalBotAI bot, PlayerControllerB player)
	{
		try
		{
			ChatCommandsManager.OnPlayerChatMessageReceived(bot.State, "start the ship", player, true);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("take off failed: " + ex.Message));
		}
	}

	internal static void Follow(LethalBotAI bot, PlayerControllerB player)
	{
		DoDirectFollow(bot, player);
	}

	internal static void DoDirectFollow(LethalBotAI bot, PlayerControllerB player)
	{
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Expected O, but got Unknown
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Expected O, but got Unknown
		PlayerControllerB val = ((bot.NpcController != null) ? bot.NpcController.Npc : null);
		try
		{
			if (bot.IsUsingTerminal() || bot.IsInSpecialAnimation())
			{
				bot.LeaveTerminalRpc(true, true);
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("follow leave-terminal: " + ex.Message));
		}
		GroupManager instance = GroupManager.Instance;
		if ((Object)instance != (Object)null && (Object)val != (Object)null)
		{
			instance.CreateOrJoinGroupWithMembersAndSync(player, (PlayerControllerB[])(object)new PlayerControllerB[1] { val }, false);
		}
		bot.SyncAssignTargetAndSetMovingTo(player);
	}

	internal static void LeadWay(LethalBotAI bot, PlayerControllerB player)
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Expected O, but got Unknown
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Expected O, but got Unknown
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Expected O, but got Unknown
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Expected O, but got Unknown
		PlayerControllerB val = ((bot.NpcController != null) ? bot.NpcController.Npc : null);
		GroupManager instance = GroupManager.Instance;
		if ((Object)instance != (Object)null && (Object)player != (Object)null && (Object)val != (Object)null && instance.ArePlayersInSameGroup(val, player))
		{
			instance.RemoveFromCurrentGroupAndSync(val);
		}
		bot.State = (AIState)new SearchingForScrapState(bot.State, (EntranceTeleport)null);
		Commands.StartLeadDiag(bot);
	}
}
internal static class Collect
{
	private struct ResumeInfo
	{
		public LethalBotAI bot;

		public AIState prev;

		public bool wasManning;
	}

	private static Coroutine _co;

	private static readonly List<ResumeInfo> _resume = new List<ResumeInfo>();

	private static FieldInfo _latchField;

	private static bool _latchResolved;

	internal static void Run(List<LethalBotAI> targets, PlayerControllerB player)
	{
		List<LethalBotAI> list = Commands.OwnedBots();
		if (list != null && list.Count != 0 && !((Object)(object)Plugin.Instance == (Object)null))
		{
			if (_co != null)
			{
				((MonoBehaviour)Plugin.Instance).StopCoroutine(_co);
			}
			_co = ((MonoBehaviour)Plugin.Instance).StartCoroutine(Routine(list));
		}
	}

	private static IEnumerator Routine(List<LethalBotAI> all)
	{
		float t = 0f;
		ItemDropship ship = null;
		for (; t < 60f; t += 0.25f)
		{
			ship = Object.FindObjectOfType<ItemDropship>();
			if ((Object)ship != (Object)null && ship.shipLanded)
			{
				break;
			}
			ship = null;
			yield return (object)new WaitForSeconds(0.25f);
		}
		if ((Object)ship == (Object)null)
		{
			_co = null;
			yield break;
		}
		_resume.Clear();
		LethalBotManager mgr = LethalBotManager.Instance;
		PlayerControllerB manner = ((!((Object)mgr == (Object)null)) ? mgr.MissionControlPlayer : null);
		SetLatch(v: true);
		foreach (LethalBotAI item2 in all)
		{
			if ((Object)item2 != (Object)null && item2.State != null)
			{
				PlayerControllerB val = ((item2.NpcController != null) ? item2.NpcController.Npc : null);
				bool wasManning = (Object)manner != (Object)null && (Object)val == (Object)manner;
				ResumeInfo item = new ResumeInfo
				{
					bot = item2,
					prev = item2.State,
					wasManning = wasManning
				};
				_resume.Add(item);
				item2.State = (AIState)new CollectPurchasedItemsState(item2.State, (AIState)null);
				try
				{
					item2.SendChatMessage("Grabbing the delivery!", false);
				}
				catch
				{
				}
			}
		}
		WaitForSeconds poll = new WaitForSeconds(1f);
		for (t = 0f; t < 300f; t += 1f)
		{
			ItemDropship ds = Object.FindObjectOfType<ItemDropship>();
			if ((Object)ds == (Object)null || !ds.shipLanded)
			{
				break;
			}
			yield return poll;
		}
		SetLatch(v: false);
		yield return (object)new WaitForSeconds(1.5f);
		Restore();
		_co = null;
	}

	private static void Restore()
	{
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Expected O, but got Unknown
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Expected O, but got Unknown
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Expected O, but got Unknown
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Expected O, but got Unknown
		LethalBotManager instance = LethalBotManager.Instance;
		foreach (ResumeInfo item in _resume)
		{
			LethalBotAI bot = item.bot;
			if ((Object)bot == (Object)null || item.prev == null)
			{
				continue;
			}
			try
			{
				bool flag = bot.State is CollectPurchasedItemsState;
				bool flag2 = bot.State == null || ((object)bot.State).GetType() != ((object)item.prev).GetType();
				if (flag || flag2)
				{
					if (item.wasManning)
					{
						PlayerControllerB val = ((bot.NpcController != null) ? bot.NpcController.Npc : null);
						if ((Object)instance != (Object)null && (Object)val != (Object)null)
						{
							instance.MissionControlPlayer = val;
						}
					}
					bot.State = item.prev;
				}
				if (!item.wasManning && bot.State is CollectPurchasedItemsState)
				{
					bot.State = (AIState)new SearchingForScrapState(bot.State, (EntranceTeleport)null);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("collect resume failed: " + ex.Message));
			}
		}
		_resume.Clear();
	}

	private static void SetLatch(bool v)
	{
		if (!_latchResolved)
		{
			_latchResolved = true;
			try
			{
				_latchField = typeof(CollectPurchasedItemsState).GetField("<CollectDeliveredItems>k__BackingField", BindingFlags.Static | BindingFlags.NonPublic);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("collect-latch reflect failed: " + ex.Message));
			}
			if (_latchField == null)
			{
				Plugin.Log.LogWarning((object)"collect-latch field not found; collect won't work on moons.");
			}
		}
		try
		{
			if (_latchField != null)
			{
				_latchField.SetValue(null, v);
			}
		}
		catch (Exception ex2)
		{
			Plugin.Log.LogWarning((object)("collect-latch set failed: " + ex2.Message));
		}
	}
}